Browse Source

zebra: implement connected prefixes for ND RA

Zebra rtadv so far required an explicit "ipv6 nd prefix ..." statement
for each IPv6 prefix to be announced through Prefix Information TLVs in
ND RA messages. This change adds an option for an automatic mapping of
connected IPv6 prefixes into PI TLVs ("ipv6 nd prefix connected-all" et
al.)

* rtadv.h: add struct rtadv_connprefix, extend struct rtadvconf
* rtadv.c
  * struct rtadv: extend with new fields
  * rtadv_new(): update to do more initialization
  * rtadv_cp_enabled(): new helper function
  * rtadv_append_connprefix_tlvs(): new helper function
  * rtadv_send_packet(): update to use the helper
  * rtadv_set_if_cp_none(): new helper function
  * rtadv_set_if_cp_auto(): new helper function
  * rtadv_set_if_cp_all(): new helper function
  * rtadv_set_top_enable_cp(): new helper function
  * rtadv_parse_connprefix(): new helper function
  * if_ipv6_nd_prefix_connectedall: new DEFUN
  * if_ipv6_nd_prefix_connectednone: new DEFUN
  * if_no_ipv6_nd_prefix_connectedval: new DEFUN
  * top_ipv6_nd_prefix_connectedall: new DEFUN
  * top_ipv6_nd_prefix_connectednone: new DEFUN
  * rtadv_print_connprefix(): new helper function
  * rtadv_config_write(): update to use the helper
  * top_rtadv_config_write(): update to use the helper
  * rtadv_init(): list new commands
  * rtadv_refresh_connected(): new exported function
  * rtadv_if_new_hook(): update initialization
* connected.c: update connected_withdraw() and connected_announce() to
  call rtadv_refresh_connected() after connected prefixes list is
  changed
* interface.c: idem for if_delete_update(), ipv6_address_install(),
  ipv6_address_uninstall(), ipv6_address_uninstall()
* doc/ipv6.texi: list new commands

By Paul Jakma:
* rtadv.h: (struct rtadv.h) Move back to here, more logical. rtadv
  doesn't need a complete knowledge of rib.h structs, so quite possible.
Denis Ovsienko 7 years ago
parent
commit
a280410ef2
6 changed files with 681 additions and 28 deletions
  1. 31 0
      doc/ipv6.texi
  2. 7 0
      zebra/connected.c
  3. 14 0
      zebra/interface.c
  4. 1 22
      zebra/rib.h
  5. 585 5
      zebra/rtadv.c
  6. 43 1
      zebra/rtadv.h

+ 31 - 0
doc/ipv6.texi

@@ -35,6 +35,18 @@ is not supported in these commands, the entries configured this way will always
 have lifetime 2 times the current @var{ra-interval} of each interface.
 @end deffn
 
+@deffn Command {ipv6 nd prefix connected-none} {}
+Configure the router-scope setting NOT to generate Prefix Information TLVs for
+connected prefixes (each interface configuration may override this). This
+setting is the default.
+@end deffn
+
+@deffn Command {ipv6 nd prefix connected-all [@var{valid-lifetime}] [@var{preferred-lifetime}] [off-link] [no-autoconfig]} {}
+Configure the router-scope setting to generate Prefix Information TLVs for all
+connected prefixes except link-local ones (each interface configuration may
+override this).
+@end deffn
+
 @section Interface-scope RA commands
 @deffn {Interface Command} {ipv6 nd suppress-ra auto} {}
 Configure the interface to track router-scope @code{suppress-ra} setting. The
@@ -93,6 +105,25 @@ Default: not set, i.e. hosts do not assume a complete IP address is placed.
 @end itemize
 @end deffn
 
+@deffn {Interface Command} {ipv6 nd prefix connected-auto} {}
+@deffnx {Interface Command} {no ipv6 nd prefix (connected-all|connected-none)} {}
+Configure the interface to track router-scope setting of connected prefixes
+advertisement. The commands are not visible in interface configuration section
+of running-config text. This setting is the default.
+@end deffn
+
+@deffn {Interface Command} {ipv6 nd prefix connected-all [@var{valid-lifetime}] [@var{preferred-lifetime}] [off-link] [no-autoconfig]} {}
+Configure the interface to enable connected prefixes advertisement regardless
+of router-scope setting. This command is visible in interface configuration
+section of running-config text.
+@end deffn
+
+@deffn {Interface Command} {ipv6 nd prefix connected-none} {}
+Configure the interface to disable connected prefixes advertisement regardless
+of router-scope setting. This command is visible in interface configuration
+section of running-config text.
+@end deffn
+
 @deffn {Interface Command} {ipv6 nd ra-interval <1-1800>} {}
 @deffnx {Interface Command} {no ipv6 nd ra-interval [<1-1800>]} {}
 The  maximum  time allowed between sending unsolicited multicast router

+ 7 - 0
zebra/connected.c

@@ -68,6 +68,9 @@ connected_withdraw (struct connected *ifc)
   if (!CHECK_FLAG (ifc->conf, ZEBRA_IFC_CONFIGURED))
     {
       listnode_delete (ifc->ifp->connected, ifc);
+#ifdef HAVE_RTADV
+      rtadv_refresh_connected (ifc->ifp);
+#endif /* HAVE_RTADV */
       connected_free (ifc);
     }
 }
@@ -77,6 +80,10 @@ connected_announce (struct interface *ifp, struct connected *ifc)
 {
   if (!ifc)
     return;
+  
+#ifdef HAVE_RTADV
+  rtadv_refresh_connected (ifp);
+#endif /* HAVE_RTADV */
 
   if (!if_is_loopback(ifp) && ifc->address->family == AF_INET)
     {

+ 14 - 0
zebra/interface.c

@@ -573,6 +573,9 @@ if_delete_update (struct interface *ifp)
 		{
 		  listnode_delete (ifp->connected, ifc);
 		  connected_free (ifc);
+#ifdef RTADV
+		  rtadv_refresh_connected (ifp);
+#endif /* RTADV */
 		}
 	    }
 #endif /* HAVE_IPV6 */
@@ -2386,6 +2389,9 @@ ipv6_address_install (struct vty *vty, struct interface *ifp,
 
       /* Add to linked list. */
       listnode_add (ifp->connected, ifc);
+#ifdef RTADV
+      rtadv_refresh_connected (ifp);
+#endif /* RTADV */
     }
 
   /* This address is configured from zebra. */
@@ -2458,6 +2464,9 @@ ipv6_address_uninstall (struct vty *vty, struct interface *ifp,
     {
       listnode_delete (ifp->connected, ifc);
       connected_free (ifc);
+#ifdef RTADV
+      rtadv_refresh_connected (ifp);
+#endif /* RTADV */
       return CMD_WARNING;
     }
 
@@ -2473,6 +2482,11 @@ ipv6_address_uninstall (struct vty *vty, struct interface *ifp,
   UNSET_FLAG (ifc->conf, ZEBRA_IFC_QUEUED);
   /* This information will be propagated to the zclients when the
    * kernel notification is received. */
+
+#ifdef HAVE_RTADV
+  rtadv_refresh_connected (ifp);
+#endif /* HAVE_RTADV */
+
   return CMD_SUCCESS;
 }
 

+ 1 - 22
zebra/rib.h

@@ -29,6 +29,7 @@
 #include "table.h"
 #include "queue.h"
 #include "nexthop.h"
+#include "rtadv.h"
 
 #define DISTANCE_INFINITY  255
 
@@ -260,28 +261,6 @@ struct nexthop_vrfid
   vrf_id_t vrf_id;
 };
 
-
-#if defined (HAVE_RTADV)
-/* Structure which hold status of router advertisement. */
-struct rtadv
-{
-  int sock;
-
-  int adv_if_count;
-  int adv_msec_if_count;
-
-  struct thread *ra_read;
-  struct thread *ra_timer;
-  
-  /* router-scope setting; 0: disabled, 1: enabled */
-  u_char AdvSendAdvertisements;
-  /* router-scope RDNSS options */
-  struct list *AdvRDNSSList;
-  /* router-scope DNSSL options */
-  struct list *AdvDNSSLList;
-};
-#endif /* HAVE_RTADV */
-
 #ifdef HAVE_NETLINK
 /* Socket interface to kernel */
 struct nlsock

+ 585 - 5
zebra/rtadv.c

@@ -88,6 +88,15 @@ rtadv_ra_enabled (const struct rtadvconf *conf)
          conf->AdvSendAdvertisements;
 }
 
+static inline u_char
+rtadv_cp_enabled (const struct rtadvconf *conf)
+{
+  struct zebra_vrf *zvrf = vrf_info_lookup (VRF_DEFAULT);
+  struct rtadv *rtadv = &zvrf->rtadv;
+  return conf->ConnpfxEnabled == -1 ? rtadv->ConnpfxEnabled :
+         conf->ConnpfxEnabled;
+}
+
 static int
 rtadv_recv_packet (int sock, u_char *buf, int buflen,
 		   struct sockaddr_in6 *from, ifindex_t *ifindex,
@@ -208,6 +217,41 @@ rtadv_append_dnssl_tlvs (unsigned char *buf, const struct list *list, const unsi
   return len;
 }
 
+static unsigned
+rtadv_append_connprefix_tlvs (unsigned char *buf, const struct list *list,
+                              const struct rtadv_connprefix *rcp)
+{
+  struct listnode *node;
+  struct connected *conn;
+  unsigned len = 0;
+  struct prefix_ipv6 pfx;
+  struct nd_opt_prefix_info *pinfo;
+
+  pfx.family = AF_INET6;
+  for (ALL_LIST_ELEMENTS_RO (list, node, conn))
+    if (conn->address->family == AF_INET6 && ! IN6_IS_ADDR_LINKLOCAL (&conn->address->u.prefix6))
+    {
+      pinfo = (struct nd_opt_prefix_info *) (buf + len);
+      pinfo->nd_opt_pi_type = ND_OPT_PREFIX_INFORMATION;
+      pinfo->nd_opt_pi_len = 4;
+      pinfo->nd_opt_pi_prefix_len = conn->address->prefixlen;
+      pinfo->nd_opt_pi_flags_reserved = 0;
+      if (rcp->AdvOnLinkFlag)
+        pinfo->nd_opt_pi_flags_reserved |= ND_OPT_PI_FLAG_ONLINK;
+      if (rcp->AdvAutonomousFlag)
+        pinfo->nd_opt_pi_flags_reserved |= ND_OPT_PI_FLAG_AUTO;
+      pinfo->nd_opt_pi_valid_time = htonl (rcp->AdvValidLifetime);
+      pinfo->nd_opt_pi_preferred_time = htonl (rcp->AdvPreferredLifetime);
+      pinfo->nd_opt_pi_reserved2 = 0;
+      pfx.prefixlen = conn->address->prefixlen;
+      IPV6_ADDR_COPY (&pfx.prefix, &conn->address->u.prefix6);
+      apply_mask_ipv6 (&pfx); /* RFC4861 4.6.2 */
+      IPV6_ADDR_COPY (&pinfo->nd_opt_pi_prefix, &pfx.prefix);
+      len += sizeof (struct nd_opt_prefix_info);
+    }
+  return len;
+}
+
 /* Send router advertisement packet. */
 static void
 rtadv_send_packet (int sock, struct interface *ifp)
@@ -231,7 +275,8 @@ rtadv_send_packet (int sock, struct interface *ifp)
   struct listnode *node;
   u_int16_t pkt_RouterLifetime;
   struct zebra_vrf *zvrf = vrf_info_lookup (ifp->vrf_id);
-
+  struct rtadv *rtadv = &zvrf->rtadv;
+  
   /*
    * Allocate control message bufffer.  This is dynamic because
    * CMSG_SPACE is not guaranteed not to call a function.  Note that
@@ -343,6 +388,11 @@ rtadv_send_packet (int sock, struct interface *ifp)
                                   zif->rtadv.MaxRtrAdvInterval);
   
   /* Fill in prefix. */
+  if (rtadv_cp_enabled (&zif->rtadv))
+    len += rtadv_append_connprefix_tlvs (buf + len, ifp->connected,
+                                         zif->rtadv.ConnpfxEnabled == -1 
+                                           ? &rtadv->ConnpfxConfig
+                                           : &zif->rtadv.ConnpfxConfig);
   for (ALL_LIST_ELEMENTS_RO (zif->rtadv.AdvPrefixList, node, rprefix))
     {
       struct nd_opt_prefix_info *pinfo;
@@ -834,6 +884,128 @@ rtadv_set_top_enable_ra (const int val)
     rtadv_event (zvrf, RTADV_STOP, 0);
 }
 
+/* Process a request to change interface-scope ConnpfxEnabled parameter and
+ * leave interface timer reset, when the change is visible in the RA. */
+static void
+rtadv_set_if_cp_none (struct interface *ifp)
+{
+  struct zebra_if *zif = ifp->info;
+
+  switch (zif->rtadv.ConnpfxEnabled)
+  {
+  case 0: /* RA won't change */
+    return;
+  case 1: /* RA will change */
+    zif->rtadv.ConnpfxEnabled = 0;
+    zif->rtadv.AdvIntervalTimer = 0;
+    return;
+  case -1: /* RA may change */
+    if (rtadv_cp_enabled (&zif->rtadv))
+      zif->rtadv.AdvIntervalTimer = 0;
+    zif->rtadv.ConnpfxEnabled = 0;
+    return;
+  default:
+    assert (0);
+  }
+}
+
+static void
+rtadv_set_if_cp_auto (struct interface *ifp)
+{
+  struct zebra_if *zif = ifp->info;
+  struct zebra_vrf *zvrf = vrf_info_lookup (ifp->vrf_id);
+  struct rtadv *rtadv = &zvrf->rtadv;
+  
+  switch (zif->rtadv.ConnpfxEnabled)
+  {
+  case -1: /* RA won't change */
+    return;
+  case 0: /* RA may change */
+    zif->rtadv.ConnpfxEnabled = -1;
+    if (rtadv_cp_enabled (&zif->rtadv))
+      zif->rtadv.AdvIntervalTimer = 0;
+    return;
+  case 1: /* RA may change */
+    zif->rtadv.ConnpfxEnabled = -1;
+    if
+    (
+      ! rtadv_cp_enabled (&zif->rtadv) ||
+      memcmp (&rtadv->ConnpfxConfig, &zif->rtadv.ConnpfxConfig,
+              sizeof (struct rtadv_connprefix))
+    )
+      zif->rtadv.AdvIntervalTimer = 0;
+    return;
+  default:
+    assert (0);
+  }
+}
+
+static void
+rtadv_set_if_cp_all (struct interface *ifp, struct rtadv_connprefix *rcp)
+{
+  struct zebra_if *zif = ifp->info;
+  struct zebra_vrf *zvrf = vrf_info_lookup (ifp->vrf_id);
+  struct rtadv *rtadv = &zvrf->rtadv;
+  
+  switch (zif->rtadv.ConnpfxEnabled)
+  {
+  case 1: /* RA may change */
+    if (memcmp (rcp, &zif->rtadv.ConnpfxConfig, sizeof (struct rtadv_connprefix)))
+    {
+      zif->rtadv.ConnpfxConfig = *rcp;
+      zif->rtadv.AdvIntervalTimer = 0;
+    }
+    return;
+  case 0: /* RA will change */
+    zif->rtadv.ConnpfxConfig = *rcp;
+    zif->rtadv.AdvIntervalTimer = 0;
+    zif->rtadv.ConnpfxEnabled = 1;
+    return;
+  case -1: /* RA may change */
+    zif->rtadv.ConnpfxConfig = *rcp;
+    if (! rtadv_cp_enabled (&zif->rtadv)
+        || memcmp (&rtadv->ConnpfxConfig, &zif->rtadv.ConnpfxConfig, 
+                   sizeof (struct rtadv_connprefix)))
+      zif->rtadv.AdvIntervalTimer = 0;
+    zif->rtadv.ConnpfxEnabled = 1;
+    return;
+  default:
+    assert (0);
+  }
+}
+
+/* Process a request to change router-scope ConnpfxEnabled parameter and leave
+ * interface timer reset for each interface going to have changes in RA. */
+static void
+rtadv_set_top_enable_cp (const int val, const struct rtadv_connprefix *rcp)
+{
+  struct listnode *node;
+  struct interface *ifp;
+  struct zebra_vrf *zvrf = vrf_info_lookup (VRF_DEFAULT);
+  struct rtadv *rtadv = &zvrf->rtadv;
+  u_char sameconf = 1;
+
+  if (val == 1)
+  {
+    assert (rcp);
+    sameconf = ! memcmp (rcp, &rtadv->ConnpfxConfig, sizeof (*rcp));
+  }
+  if (rtadv->ConnpfxEnabled == val && sameconf)
+    return;
+  for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp))
+  {
+    struct zebra_if *zif = ifp->info;
+    u_char new_if_enabled = zif->rtadv.ConnpfxEnabled == -1
+                              ? val : zif->rtadv.ConnpfxEnabled;
+    if (rtadv_cp_enabled (&zif->rtadv) != new_if_enabled
+        || (zif->rtadv.ConnpfxEnabled == -1 && ! sameconf))
+      zif->rtadv.AdvIntervalTimer = 0;
+  }
+  rtadv->ConnpfxEnabled = val;
+  if (! sameconf)
+    rtadv->ConnpfxConfig = *rcp;
+}
+
 DEFUN (ipv6_nd_suppress_ra,
        ipv6_nd_suppress_ra_cmd,
        "ipv6 nd suppress-ra",
@@ -842,9 +1014,7 @@ DEFUN (ipv6_nd_suppress_ra,
        "Suppress Router Advertisement\n")
 {
   struct interface *ifp = vty->index;
-  struct zebra_vrf *zvrf;
-  zvrf = vrf_info_lookup (ifp->vrf_id);
-
+  
   if (if_is_loopback (ifp))
     {
       vty_out (vty, "Invalid interface%s", VTY_NEWLINE);
@@ -863,7 +1033,7 @@ DEFUN (ipv6_nd_suppress_ra_auto,
        "Follow router-scope setting\n")
 {
   struct interface *ifp = vty->index;
-
+  
   if (if_is_loopback (ifp))
   {
     vty_out (vty, "Invalid interface%s", VTY_NEWLINE);
@@ -1681,6 +1851,344 @@ DEFUN (no_ipv6_nd_prefix,
   return CMD_SUCCESS;
 }
 
+static int
+rtadv_parse_connprefix (struct vty *vty, const int argc, const char *argv[], struct rtadv_connprefix *rcp)
+{
+  int cursor = 0;
+
+  rcp->AdvOnLinkFlag = 1;
+  rcp->AdvAutonomousFlag = 1;
+  rcp->AdvValidLifetime = RTADV_VALID_LIFETIME;
+  rcp->AdvPreferredLifetime = RTADV_PREFERRED_LIFETIME;
+
+  if (argc > 0 && (isdigit (argv[0][0]) || argv[0][0] == 'i'))
+  {
+    rcp->AdvValidLifetime = strncmp (argv[0], "i", 1) == 0 ? UINT32_MAX :
+                            (u_int32_t) strtoll (argv[0], (char **)NULL, 10);
+    cursor++;
+  }
+  if (argc > 1 && (isdigit (argv[1][0]) || argv[1][0] == 'i'))
+  {
+    rcp->AdvPreferredLifetime = strncmp (argv[1], "i", 1) == 0 ? UINT32_MAX :
+                                (u_int32_t) strtoll (argv[1], (char **)NULL, 10);
+    cursor++;
+  }
+  if (rcp->AdvPreferredLifetime > rcp->AdvValidLifetime)
+  {
+    vty_out (vty, "Invalid preferred lifetime%s", VTY_NEWLINE);
+    return CMD_WARNING;
+  }
+  if (argc > cursor)
+  {
+    int i;
+    for (i = cursor; i < argc; i++)
+    {
+      if (strncmp (argv[i], "of", 2) == 0)
+        rcp->AdvOnLinkFlag = 0;
+      if (strncmp (argv[i], "no", 2) == 0)
+        rcp->AdvAutonomousFlag = 0;
+    }
+  }
+  return CMD_SUCCESS;
+}
+
+DEFUN (if_ipv6_nd_prefix_connectedall,
+       if_ipv6_nd_prefix_connectedall_val1_cmd,
+       "ipv6 nd prefix connected-all (<0-4294967295>|infinite) "
+       "(<0-4294967295>|infinite) (off-link|) (no-autoconfig|)",
+       IF_IPV6_STR
+       ND_STR
+       PFX_STR
+       "All current prefixes except link-local\n"
+       IPV6_PFX_VALID1_STR
+       IPV6_PFX_VALID2_STR
+       IPV6_PFX_PREF1_STR
+       IPV6_PFX_PREF2_STR
+       IPV6_PFX_OFFLINK_STR
+       IPV6_PFX_NOAUTO_STR)
+{
+  struct interface *ifp = (struct interface *) vty->index;
+  struct rtadv_connprefix rcp;
+
+  if (CMD_SUCCESS != rtadv_parse_connprefix (vty, argc, argv, &rcp))
+    return CMD_WARNING;
+  rtadv_set_if_cp_all (ifp, &rcp);
+  return CMD_SUCCESS;
+}
+
+ALIAS (if_ipv6_nd_prefix_connectedall,
+       if_ipv6_nd_prefix_connectedall_val2_cmd,
+       "ipv6 nd prefix connected-all (<0-4294967295>|infinite) "
+       "(<0-4294967295>|infinite) (no-autoconfig|) (off-link|)",
+       IF_IPV6_STR
+       ND_STR
+       PFX_STR
+       "All current prefixes except link-local\n"
+       IPV6_PFX_VALID1_STR
+       IPV6_PFX_VALID2_STR
+       IPV6_PFX_PREF1_STR
+       IPV6_PFX_PREF2_STR
+       IPV6_PFX_NOAUTO_STR
+       IPV6_PFX_OFFLINK_STR)
+
+ALIAS (if_ipv6_nd_prefix_connectedall,
+       if_ipv6_nd_prefix_connectedall_val3_cmd,
+       "ipv6 nd prefix connected-all (<0-4294967295>|infinite) "
+       "(<0-4294967295>|infinite) (no-autoconfig|)",
+       IF_IPV6_STR
+       ND_STR
+       PFX_STR
+       "All current prefixes except link-local\n"
+       IPV6_PFX_VALID1_STR
+       IPV6_PFX_VALID2_STR
+       IPV6_PFX_PREF1_STR
+       IPV6_PFX_PREF2_STR
+       IPV6_PFX_NOAUTO_STR)
+
+ALIAS (if_ipv6_nd_prefix_connectedall,
+       if_ipv6_nd_prefix_connectedall_val4_cmd,
+       "ipv6 nd prefix connected-all (<0-4294967295>|infinite) "
+       "(<0-4294967295>|infinite) (off-link|)",
+       IF_IPV6_STR
+       ND_STR
+       PFX_STR
+       "All current prefixes except link-local\n"
+       IPV6_PFX_VALID1_STR
+       IPV6_PFX_VALID2_STR
+       IPV6_PFX_PREF1_STR
+       IPV6_PFX_PREF2_STR
+       IPV6_PFX_OFFLINK_STR)
+
+ALIAS (if_ipv6_nd_prefix_connectedall,
+       if_ipv6_nd_prefix_connectedall_val5_cmd,
+       "ipv6 nd prefix connected-all (<0-4294967295>|infinite) (<0-4294967295>|infinite)",
+       IF_IPV6_STR
+       ND_STR
+       PFX_STR
+       "All current prefixes except link-local\n"
+       IPV6_PFX_VALID1_STR
+       IPV6_PFX_VALID2_STR
+       IPV6_PFX_PREF1_STR
+       IPV6_PFX_PREF2_STR)
+
+ALIAS (if_ipv6_nd_prefix_connectedall,
+       if_ipv6_nd_prefix_connectedall_val6_cmd,
+       "ipv6 nd prefix connected-all (no-autoconfig|) (off-link|)",
+       IF_IPV6_STR
+       ND_STR
+       PFX_STR
+       "All current prefixes except link-local\n"
+       IPV6_PFX_NOAUTO_STR
+       IPV6_PFX_OFFLINK_STR)
+
+ALIAS (if_ipv6_nd_prefix_connectedall,
+       if_ipv6_nd_prefix_connectedall_val7_cmd,
+       "ipv6 nd prefix connected-all (off-link|) (no-autoconfig|)",
+       IF_IPV6_STR
+       ND_STR
+       PFX_STR
+       "All current prefixes except link-local\n"
+       IPV6_PFX_OFFLINK_STR
+       IPV6_PFX_NOAUTO_STR)
+
+ALIAS (if_ipv6_nd_prefix_connectedall,
+       if_ipv6_nd_prefix_connectedall_val8_cmd,
+       "ipv6 nd prefix connected-all (no-autoconfig|)",
+       IF_IPV6_STR
+       ND_STR
+       PFX_STR
+       "All current prefixes except link-local\n"
+       IPV6_PFX_NOAUTO_STR)
+
+ALIAS (if_ipv6_nd_prefix_connectedall,
+       if_ipv6_nd_prefix_connectedall_val9_cmd,
+       "ipv6 nd prefix connected-all (off-link|)",
+       IF_IPV6_STR
+       ND_STR
+       PFX_STR
+       "All current prefixes except link-local\n"
+       IPV6_PFX_OFFLINK_STR)
+
+ALIAS (if_ipv6_nd_prefix_connectedall,
+       if_ipv6_nd_prefix_connectedall_noval_cmd,
+       "ipv6 nd prefix connected-all",
+       IF_IPV6_STR
+       ND_STR
+       PFX_STR
+       "All current prefixes except link-local\n")
+
+DEFUN (if_ipv6_nd_prefix_connectednone,
+       if_ipv6_nd_prefix_connectednone_cmd,
+       "ipv6 nd prefix connected-none",
+       IF_IPV6_STR
+       ND_STR
+       PFX_STR
+       "No current prefixes\n")
+{
+  struct interface *ifp = (struct interface *) vty->index;
+  rtadv_set_if_cp_none (ifp);
+  return CMD_SUCCESS;
+}
+
+DEFUN (if_no_ipv6_nd_prefix_connectedval,
+       if_no_ipv6_nd_prefix_connectedval_cmd,
+       "no ipv6 nd prefix (connected-all|connected-none)",
+       NO_STR
+       IF_IPV6_STR
+       ND_STR
+       PFX_STR
+       "All current prefixes except link-local\n"
+       "No current prefixes\n")
+{
+  struct interface *ifp = (struct interface *) vty->index;
+  rtadv_set_if_cp_auto (ifp);
+  return CMD_SUCCESS;
+}
+
+ALIAS (if_no_ipv6_nd_prefix_connectedval,
+       if_ipv6_nd_prefix_connectedauto_cmd,
+       "ipv6 nd prefix connected-auto",
+       IF_IPV6_STR
+       ND_STR
+       PFX_STR
+       "Follow router-scope setting (default)\n")
+
+DEFUN (top_ipv6_nd_prefix_connectedall,
+       top_ipv6_nd_prefix_connectedall_val1_cmd,
+       "ipv6 nd prefix connected-all (<0-4294967295>|infinite) "
+       "(<0-4294967295>|infinite) (off-link|) (no-autoconfig|)",
+       IPV6_STR
+       ND_STR
+       PFX_STR
+       "All current prefixes except link-local\n"
+       IPV6_PFX_VALID1_STR
+       IPV6_PFX_VALID2_STR
+       IPV6_PFX_PREF1_STR
+       IPV6_PFX_PREF2_STR
+       IPV6_PFX_OFFLINK_STR
+       IPV6_PFX_NOAUTO_STR)
+{
+  struct rtadv_connprefix rcp;
+
+  if (CMD_SUCCESS != rtadv_parse_connprefix (vty, argc, argv, &rcp))
+    return CMD_WARNING;
+  rtadv_set_top_enable_cp (1, &rcp);
+  return CMD_SUCCESS;
+}
+
+ALIAS (top_ipv6_nd_prefix_connectedall,
+       top_ipv6_nd_prefix_connectedall_val2_cmd,
+       "ipv6 nd prefix connected-all (<0-4294967295>|infinite) "
+       "(<0-4294967295>|infinite) (no-autoconfig|) (off-link|)",
+       IPV6_STR
+       ND_STR
+       PFX_STR
+       "All current prefixes except link-local\n"
+       IPV6_PFX_VALID1_STR
+       IPV6_PFX_VALID2_STR
+       IPV6_PFX_PREF1_STR
+       IPV6_PFX_PREF2_STR
+       IPV6_PFX_NOAUTO_STR
+       IPV6_PFX_OFFLINK_STR)
+
+ALIAS (top_ipv6_nd_prefix_connectedall,
+       top_ipv6_nd_prefix_connectedall_val3_cmd,
+       "ipv6 nd prefix connected-all (<0-4294967295>|infinite) "
+       "(<0-4294967295>|infinite) (no-autoconfig|)",
+       IPV6_STR
+       ND_STR
+       PFX_STR
+       "All current prefixes except link-local\n"
+       IPV6_PFX_VALID1_STR
+       IPV6_PFX_VALID2_STR
+       IPV6_PFX_PREF1_STR
+       IPV6_PFX_PREF2_STR
+       IPV6_PFX_NOAUTO_STR)
+
+ALIAS (top_ipv6_nd_prefix_connectedall,
+       top_ipv6_nd_prefix_connectedall_val4_cmd,
+       "ipv6 nd prefix connected-all (<0-4294967295>|infinite) "
+       "(<0-4294967295>|infinite) (off-link|)",
+       IPV6_STR
+       ND_STR
+       PFX_STR
+       "All current prefixes except link-local\n"
+       IPV6_PFX_VALID1_STR
+       IPV6_PFX_VALID2_STR
+       IPV6_PFX_PREF1_STR
+       IPV6_PFX_PREF2_STR
+       IPV6_PFX_OFFLINK_STR)
+
+ALIAS (top_ipv6_nd_prefix_connectedall,
+       top_ipv6_nd_prefix_connectedall_val5_cmd,
+       "ipv6 nd prefix connected-all (<0-4294967295>|infinite) (<0-4294967295>|infinite)",
+       IPV6_STR
+       ND_STR
+       PFX_STR
+       "All current prefixes except link-local\n"
+       IPV6_PFX_VALID1_STR
+       IPV6_PFX_VALID2_STR
+       IPV6_PFX_PREF1_STR
+       IPV6_PFX_PREF2_STR)
+
+ALIAS (top_ipv6_nd_prefix_connectedall,
+       top_ipv6_nd_prefix_connectedall_val6_cmd,
+       "ipv6 nd prefix connected-all (no-autoconfig|) (off-link|)",
+       IPV6_STR
+       ND_STR
+       PFX_STR
+       "All current prefixes except link-local\n"
+       IPV6_PFX_NOAUTO_STR
+       IPV6_PFX_OFFLINK_STR)
+
+ALIAS (top_ipv6_nd_prefix_connectedall,
+       top_ipv6_nd_prefix_connectedall_val7_cmd,
+       "ipv6 nd prefix connected-all (off-link|) (no-autoconfig|)",
+       IPV6_STR
+       ND_STR
+       PFX_STR
+       "All current prefixes except link-local\n"
+       IPV6_PFX_OFFLINK_STR
+       IPV6_PFX_NOAUTO_STR)
+
+ALIAS (top_ipv6_nd_prefix_connectedall,
+       top_ipv6_nd_prefix_connectedall_val8_cmd,
+       "ipv6 nd prefix connected-all (no-autoconfig|)",
+       IPV6_STR
+       ND_STR
+       PFX_STR
+       "All current prefixes except link-local\n"
+       IPV6_PFX_NOAUTO_STR)
+
+ALIAS (top_ipv6_nd_prefix_connectedall,
+       top_ipv6_nd_prefix_connectedall_val9_cmd,
+       "ipv6 nd prefix connected-all (off-link|)",
+       IPV6_STR
+       ND_STR
+       PFX_STR
+       "All current prefixes except link-local\n"
+       IPV6_PFX_OFFLINK_STR)
+
+ALIAS (top_ipv6_nd_prefix_connectedall,
+       top_ipv6_nd_prefix_connectedall_noval_cmd,
+       "ipv6 nd prefix connected-all",
+       IPV6_STR
+       ND_STR
+       PFX_STR
+       "All current prefixes except link-local\n")
+
+DEFUN (top_ipv6_nd_prefix_connectednone,
+       top_ipv6_nd_prefix_connectednone_cmd,
+       "ipv6 nd prefix connected-none",
+       IPV6_STR
+       ND_STR
+       PFX_STR
+       "No connected prefixes\n")
+{
+  rtadv_set_top_enable_cp (0, NULL);
+  return CMD_SUCCESS;
+}
+
 DEFUN (ipv6_nd_router_preference,
        ipv6_nd_router_preference_cmd,
        "ipv6 nd router-preference (high|medium|low)",
@@ -2192,6 +2700,33 @@ rtadv_print_dnssl_list (struct vty *vty, const struct list *list, const char *pr
   return write;
 }
 
+static int
+rtadv_print_connprefix (struct vty *vty, const char *prefix, const u_char enabled, const struct rtadv_connprefix rcp)
+{
+  if (enabled == 0)
+  {
+    vty_out (vty, "%sipv6 nd prefix connected-none%s", prefix, VTY_NEWLINE);
+    return 1;
+  }
+  if (enabled != 1)
+    return 0;
+  vty_out (vty, "%sipv6 nd prefix connected-all", prefix);
+  if (rcp.AdvValidLifetime == UINT32_MAX)
+    vty_out (vty, " infinite");
+  else if (rcp.AdvValidLifetime != RTADV_VALID_LIFETIME)
+    vty_out (vty, " %u", rcp.AdvValidLifetime);
+  if (rcp.AdvPreferredLifetime == UINT32_MAX)
+    vty_out (vty, " infinite");
+  else if (rcp.AdvPreferredLifetime != RTADV_PREFERRED_LIFETIME)
+    vty_out (vty, " %u", rcp.AdvPreferredLifetime);
+  if (! rcp.AdvAutonomousFlag)
+    vty_out (vty, " no-autoconfig");
+  if (! rcp.AdvOnLinkFlag)
+    vty_out (vty, " off-link");
+  vty_out (vty, "%s", VTY_NEWLINE);
+  return 1;
+}
+
 /* Write configuration about router advertisement. */
 void
 rtadv_config_write (struct vty *vty, struct interface *ifp)
@@ -2255,6 +2790,8 @@ rtadv_config_write (struct vty *vty, struct interface *ifp)
   if (zif->rtadv.AdvLinkMTU)
     vty_out (vty, " ipv6 nd mtu %d%s", zif->rtadv.AdvLinkMTU, VTY_NEWLINE);
 
+  rtadv_print_connprefix (vty, " ", zif->rtadv.ConnpfxEnabled, zif->rtadv.ConnpfxConfig);
+
   for (ALL_LIST_ELEMENTS_RO (zif->rtadv.AdvPrefixList, node, rprefix))
     {
       vty_out (vty, " ipv6 nd prefix %s",
@@ -2423,6 +2960,7 @@ top_rtadv_config_write (struct vty *vty)
   
   vty_out (vty, "%sipv6 nd suppress-ra%s", rtadv->AdvSendAdvertisements ? "no " : "", VTY_NEWLINE);
   write++;
+  write += rtadv_print_connprefix (vty, "", rtadv->ConnpfxEnabled, rtadv->ConnpfxConfig);
   write += rtadv_print_rdnss_list (vty, rtadv->AdvRDNSSList, "ipv6 nd rdnss");
   write += rtadv_print_dnssl_list (vty, rtadv->AdvDNSSLList, "ipv6 nd dnssl");
   return write;
@@ -2479,6 +3017,11 @@ rtadv_init (struct zebra_vrf *zvrf)
 {
   zvrf->rtadv.AdvRDNSSList = list_new();
   zvrf->rtadv.AdvDNSSLList = list_new();
+  zvrf->rtadv.ConnpfxConfig.AdvValidLifetime = RTADV_VALID_LIFETIME;
+  zvrf->rtadv.ConnpfxConfig.AdvPreferredLifetime = RTADV_PREFERRED_LIFETIME;
+  zvrf->rtadv.ConnpfxConfig.AdvAutonomousFlag = 1;
+  zvrf->rtadv.ConnpfxConfig.AdvOnLinkFlag = 1;
+
   zvrf->rtadv.sock = rtadv_make_socket (zvrf->vrf_id);
 }
 
@@ -2507,6 +3050,17 @@ rtadv_cmd_init (void)
   install_element (CONFIG_NODE, &top_no_ipv6_nd_rdnss_addr_cmd);
   install_element (CONFIG_NODE, &top_ipv6_nd_dnssl_domain_cmd);
   install_element (CONFIG_NODE, &top_no_ipv6_nd_dnssl_domain_cmd);
+  install_element (CONFIG_NODE, &top_ipv6_nd_prefix_connectedall_val1_cmd);
+  install_element (CONFIG_NODE, &top_ipv6_nd_prefix_connectedall_val2_cmd);
+  install_element (CONFIG_NODE, &top_ipv6_nd_prefix_connectedall_val3_cmd);
+  install_element (CONFIG_NODE, &top_ipv6_nd_prefix_connectedall_val4_cmd);
+  install_element (CONFIG_NODE, &top_ipv6_nd_prefix_connectedall_val5_cmd);
+  install_element (CONFIG_NODE, &top_ipv6_nd_prefix_connectedall_val6_cmd);
+  install_element (CONFIG_NODE, &top_ipv6_nd_prefix_connectedall_val7_cmd);
+  install_element (CONFIG_NODE, &top_ipv6_nd_prefix_connectedall_val8_cmd);
+  install_element (CONFIG_NODE, &top_ipv6_nd_prefix_connectedall_val9_cmd);
+  install_element (CONFIG_NODE, &top_ipv6_nd_prefix_connectedall_noval_cmd);
+  install_element (CONFIG_NODE, &top_ipv6_nd_prefix_connectednone_cmd);
   install_element (INTERFACE_NODE, &ipv6_nd_suppress_ra_cmd);
   install_element (INTERFACE_NODE, &no_ipv6_nd_suppress_ra_cmd);
   install_element (INTERFACE_NODE, &ipv6_nd_suppress_ra_auto_cmd);
@@ -2550,6 +3104,19 @@ rtadv_cmd_init (void)
   install_element (INTERFACE_NODE, &ipv6_nd_prefix_noval_rtaddr_cmd);
   install_element (INTERFACE_NODE, &ipv6_nd_prefix_prefix_cmd);
   install_element (INTERFACE_NODE, &no_ipv6_nd_prefix_cmd);
+  install_element (INTERFACE_NODE, &if_ipv6_nd_prefix_connectedall_val1_cmd);
+  install_element (INTERFACE_NODE, &if_ipv6_nd_prefix_connectedall_val2_cmd);
+  install_element (INTERFACE_NODE, &if_ipv6_nd_prefix_connectedall_val3_cmd);
+  install_element (INTERFACE_NODE, &if_ipv6_nd_prefix_connectedall_val4_cmd);
+  install_element (INTERFACE_NODE, &if_ipv6_nd_prefix_connectedall_val5_cmd);
+  install_element (INTERFACE_NODE, &if_ipv6_nd_prefix_connectedall_val6_cmd);
+  install_element (INTERFACE_NODE, &if_ipv6_nd_prefix_connectedall_val7_cmd);
+  install_element (INTERFACE_NODE, &if_ipv6_nd_prefix_connectedall_val8_cmd);
+  install_element (INTERFACE_NODE, &if_ipv6_nd_prefix_connectedall_val9_cmd);
+  install_element (INTERFACE_NODE, &if_ipv6_nd_prefix_connectedall_noval_cmd);
+  install_element (INTERFACE_NODE, &if_ipv6_nd_prefix_connectednone_cmd);
+  install_element (INTERFACE_NODE, &if_ipv6_nd_prefix_connectedauto_cmd);
+  install_element (INTERFACE_NODE, &if_no_ipv6_nd_prefix_connectedval_cmd);
   install_element (INTERFACE_NODE, &ipv6_nd_router_preference_cmd);
   install_element (INTERFACE_NODE, &no_ipv6_nd_router_preference_cmd);
   install_element (INTERFACE_NODE, &no_ipv6_nd_router_preference_val_cmd);
@@ -2566,6 +3133,16 @@ rtadv_cmd_init (void)
   install_element (INTERFACE_NODE, &no_ipv6_nd_dnssl_domain_lft_cmd);
 }
 
+/* This function is called after ifp->connected is changed. */
+void
+rtadv_refresh_connected (struct interface *ifp)
+{
+  struct zebra_if *zif = ifp->info;
+
+  if (rtadv_ra_enabled (&zif->rtadv) && rtadv_cp_enabled (&zif->rtadv))
+    zif->rtadv.AdvIntervalTimer = 0;
+}
+
 static int
 if_join_all_router (int sock, struct interface *ifp)
 {
@@ -2663,6 +3240,9 @@ rtadv_if_new_hook (struct rtadvconf *conf)
    * configured to follow router-scope parameter. Processing functions are left
    * to work around loopback interfaces by their own means. */
   conf->AdvSendAdvertisements = -1;
+  /* ConnpfxConfig is left uninitialized, "connected-all" DEFUN will take care. */
+  conf->ConnpfxEnabled = -1;
+
   conf->MaxRtrAdvInterval = RTADV_MAX_RTR_ADV_INTERVAL;
   conf->MinRtrAdvInterval = RTADV_MIN_RTR_ADV_INTERVAL;
   conf->AdvIntervalTimer = 0;

+ 43 - 1
zebra/rtadv.h

@@ -27,6 +27,20 @@
 #include "linklist.h"
 #include "if.h"
 
+/* zebra_vrf left incomplete, as it is defined in rib.h and rib.h at present
+ * needs (struct rtadv) as a complete type to embed it in (struct zebra_vrf)
+ */
+struct zebra_vrf;
+
+/* "ipv6 nd connected-prefix ..." feature parameters */
+struct rtadv_connprefix
+{
+  u_int32_t AdvValidLifetime;
+  u_int32_t AdvPreferredLifetime;
+  u_char AdvAutonomousFlag;
+  u_char AdvOnLinkFlag;
+};
+
 /* Router advertisement parameter.  From RFC4861, RFC6275 and RFC4191. */
 struct rtadvconf
 {
@@ -172,6 +186,11 @@ struct rtadvconf
 #define	RTADV_DNS_OBSOLETE_LIFETIME (0x00000000)
   /* a list of configured DNS Search List domains (RFC6106) */
   struct list *AdvDNSSLList;
+
+  /* interface-scope setting: 0: disabled, 1: enabled, -1: use current
+   * router-scope parameter. Default: -1 */
+  char ConnpfxEnabled;
+  struct rtadv_connprefix ConnpfxConfig;
 };
 
 /* NB: RTADV is defined in zebra/interface.h above */
@@ -295,6 +314,28 @@ struct rtadv_dnssl_entry
   u_int32_t lifetime;
 };
 
+/* Structure which hold status of router advertisement. */
+struct rtadv
+{
+  int sock;
+
+  int adv_if_count;
+  int adv_msec_if_count;
+
+  struct thread *ra_read;
+  struct thread *ra_timer;
+  
+  /* router-scope setting; 0: disabled, 1: enabled */
+  u_char AdvSendAdvertisements;
+  /* router-scope RDNSS options */
+  struct list *AdvRDNSSList;
+  /* router-scope DNSSL options */
+  struct list *AdvDNSSLList;
+
+  /* router-scope setting */
+  u_char ConnpfxEnabled;
+  struct rtadv_connprefix ConnpfxConfig;
+};
 #endif /* HAVE_RTADV */
 
 extern void rtadv_config_write (struct vty *, struct interface *);
@@ -307,6 +348,7 @@ extern void rtadv_cmd_init (void);
 #ifdef HAVE_RTADV
 extern void rtadv_if_dump_vty (struct vty *, struct interface *);
 extern void rtadv_if_new_hook (struct rtadvconf *);
+extern void rtadv_refresh_connected (struct interface *);
 #endif
 
-#endif /* _ZEBRA_RTADV_H */
+#endif /* _ZEBRA_RTADV_H */