bgpd: wire up VPNv6 protocol processing

There wasn't much missing for VPNv6 to begin with; just a few bits of
de- & encoding and a few lists to be updated.

Signed-off-by: Lou Berger <lberger@labn.net>
Signed-off-by: David Lamparter <equinox@opensourcerouting.org>

[Editorial note: Signed-off-by may imply an authorship claim, but need not]

Edited-by: Paul Jakma <paul.jakma@hpe.com> / <paul@jakma.org>
diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c
index ce8f8a0..ecf1c31 100644
--- a/bgpd/bgp_attr.c
+++ b/bgpd/bgp_attr.c
@@ -1535,11 +1535,36 @@
       stream_get (&attre->mp_nexthop_global_in, s, 4);
       break;
 #ifdef HAVE_IPV6
+    case 24:
+      {
+        u_int32_t rd_high __attribute__((unused));
+        u_int32_t rd_low __attribute__((unused));
+
+        rd_high = stream_getl (s);
+        rd_low = stream_getl (s);
+      }
+      /* fall through */
     case 16:
       stream_get (&attre->mp_nexthop_global, s, 16);
       break;
     case 32:
+    case 48:
+      if (attre->mp_nexthop_len == 48) {
+        u_int32_t rd_high __attribute__((unused));
+        u_int32_t rd_low __attribute__((unused));
+
+        rd_high = stream_getl (s);
+        rd_low = stream_getl (s);
+      }
       stream_get (&attre->mp_nexthop_global, s, 16);
+
+      if (attre->mp_nexthop_len == 48) {
+        u_int32_t rd_high __attribute__((unused));
+        u_int32_t rd_low __attribute__((unused));
+
+        rd_high = stream_getl (s);
+        rd_low = stream_getl (s);
+      }
       stream_get (&attre->mp_nexthop_local, s, 16);
       if (! IN6_IS_ADDR_LINKLOCAL (&attre->mp_nexthop_local))
 	{
diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c
index bbc739b..78a71e7 100644
--- a/bgpd/bgp_mplsvpn.c
+++ b/bgpd/bgp_mplsvpn.c
@@ -92,13 +92,13 @@
 }
 
 int
-bgp_nlri_parse_vpnv4 (struct peer *peer, struct attr *attr, 
-		      struct bgp_nlri *packet)
+bgp_nlri_parse_vpn (struct peer *peer, struct attr *attr,
+                    struct bgp_nlri *packet)
 {
   u_char *pnt;
   u_char *lim;
   struct prefix p;
-  int psize;
+  int psize = 0;
   int prefixlen;
   u_int16_t type;
   struct rd_as rd_as;
@@ -191,11 +191,11 @@
               psize - VPN_PREFIXLEN_MIN_BYTES);
 
       if (attr)
-	bgp_update (peer, &p, attr, AFI_IP, SAFI_MPLS_VPN,
-		    ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, tagpnt, 0);
+        bgp_update (peer, &p, attr, packet->afi, SAFI_MPLS_VPN,
+                    ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, tagpnt, 0);
       else
-	bgp_withdraw (peer, &p, attr, AFI_IP, SAFI_MPLS_VPN,
-		      ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, tagpnt);
+        bgp_withdraw (peer, &p, attr, packet->afi, SAFI_MPLS_VPN,
+                      ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, tagpnt);
     }
   /* Packet length consistency check. */
   if (pnt != lim)
@@ -480,6 +480,7 @@
 bgp_show_mpls_vpn (struct vty *vty, struct prefix_rd *prd, enum bgp_show_type type,
 		   void *output_arg, int tags)
 {
+  afi_t afi = AFI_IP;
   struct bgp *bgp;
   struct bgp_table *table;
   struct bgp_node *rn;
@@ -497,7 +498,13 @@
       return CMD_WARNING;
     }
   
-  for (rn = bgp_table_top (bgp->rib[AFI_IP][SAFI_MPLS_VPN]); rn; rn = bgp_route_next (rn))
+  if ((afi != AFI_IP) && (afi != AFI_IP6))
+    {
+      vty_out (vty, "Afi %d not supported%s", afi, VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  for (rn = bgp_table_top (bgp->rib[afi][SAFI_MPLS_VPN]); rn; rn = bgp_route_next (rn))
     {
       if (prd && memcmp (rn->p.u.val, prd->val, 8) != 0)
 	continue;
diff --git a/bgpd/bgp_mplsvpn.h b/bgpd/bgp_mplsvpn.h
index 0cb9dec..4e9b317 100644
--- a/bgpd/bgp_mplsvpn.h
+++ b/bgpd/bgp_mplsvpn.h
@@ -43,7 +43,7 @@
 };
 
 extern void bgp_mplsvpn_init (void);
-extern int bgp_nlri_parse_vpnv4 (struct peer *, struct attr *, struct bgp_nlri *);
+extern int bgp_nlri_parse_vpn (struct peer *, struct attr *, struct bgp_nlri *);
 extern u_int32_t decode_label (u_char *);
 extern int str2prefix_rd (const char *, struct prefix_rd *);
 extern int str2tag (const char *, u_char *);
diff --git a/bgpd/bgp_open.c b/bgpd/bgp_open.c
index 7a5f5ce..302e4ce 100644
--- a/bgpd/bgp_open.c
+++ b/bgpd/bgp_open.c
@@ -842,7 +842,8 @@
 	  && ! peer->afc_nego[AFI_IP][SAFI_MULTICAST]
 	  && ! peer->afc_nego[AFI_IP][SAFI_MPLS_VPN]
 	  && ! peer->afc_nego[AFI_IP6][SAFI_UNICAST]
-	  && ! peer->afc_nego[AFI_IP6][SAFI_MULTICAST])
+	  && ! peer->afc_nego[AFI_IP6][SAFI_MULTICAST]
+	  && ! peer->afc_nego[AFI_IP6][SAFI_MPLS_VPN])
 	{
 	  plog_err (peer->log, "%s [Error] Configured AFI/SAFIs do not "
 		    "overlap with received MP capabilities",
@@ -1012,6 +1013,18 @@
       stream_putc (s, 0);
       stream_putc (s, SAFI_MULTICAST);
     }
+  /* IPv6 VPN. */
+  if (peer->afc[AFI_IP6][SAFI_MPLS_VPN])
+    {
+      peer->afc_adv[AFI_IP6][SAFI_MPLS_VPN] = 1;
+      stream_putc (s, BGP_OPEN_OPT_CAP);
+      stream_putc (s, CAPABILITY_CODE_MP_LEN + 2);
+      stream_putc (s, CAPABILITY_CODE_MP);
+      stream_putc (s, CAPABILITY_CODE_MP_LEN);
+      stream_putw (s, AFI_IP6);
+      stream_putc (s, 0);
+      stream_putc (s, SAFI_MPLS_LABELED_VPN);
+    }
 #endif /* HAVE_IPV6 */
 
   /* Route refresh. */
diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c
index ec60069..16e1435 100644
--- a/bgpd/bgp_packet.c
+++ b/bgpd/bgp_packet.c
@@ -1797,12 +1797,12 @@
   if (peer->afc[AFI_IP][SAFI_MULTICAST])
     {
       if (mp_update.length
-	  && mp_update.afi == AFI_IP 
+	  && mp_update.afi == AFI_IP
 	  && mp_update.safi == SAFI_MULTICAST)
 	bgp_nlri_parse (peer, NLRI_ATTR_ARG, &mp_update);
 
       if (mp_withdraw.length
-	  && mp_withdraw.afi == AFI_IP 
+	  && mp_withdraw.afi == AFI_IP
 	  && mp_withdraw.safi == SAFI_MULTICAST)
 	bgp_nlri_parse (peer, NULL, &mp_withdraw);
 
@@ -1886,12 +1886,12 @@
       if (mp_update.length 
 	  && mp_update.afi == AFI_IP 
 	  && mp_update.safi == SAFI_MPLS_LABELED_VPN)
-	bgp_nlri_parse_vpnv4 (peer, NLRI_ATTR_ARG, &mp_update);
+	bgp_nlri_parse_vpn (peer, NLRI_ATTR_ARG, &mp_update);
 
       if (mp_withdraw.length 
 	  && mp_withdraw.afi == AFI_IP 
 	  && mp_withdraw.safi == SAFI_MPLS_LABELED_VPN)
-	bgp_nlri_parse_vpnv4 (peer, NULL, &mp_withdraw);
+	bgp_nlri_parse_vpn (peer, NULL, &mp_withdraw);
 
       if (! withdraw_len
 	  && mp_withdraw.afi == AFI_IP
@@ -1905,6 +1905,30 @@
 		  peer->host);
 	}
     }
+  if (peer->afc[AFI_IP6][SAFI_MPLS_VPN])
+    {
+      if (mp_update.length
+	  && mp_update.afi == AFI_IP6
+	  && mp_update.safi == SAFI_MPLS_LABELED_VPN)
+	bgp_nlri_parse_vpn (peer, NLRI_ATTR_ARG, &mp_update);
+
+      if (mp_withdraw.length
+	  && mp_withdraw.afi == AFI_IP6
+	  && mp_withdraw.safi == SAFI_MPLS_LABELED_VPN)
+	bgp_nlri_parse_vpn (peer, NULL, &mp_withdraw);
+
+      if (! withdraw_len
+	  && mp_withdraw.afi == AFI_IP6
+	  && mp_withdraw.safi == SAFI_MPLS_LABELED_VPN
+	  && mp_withdraw.length == 0)
+	{
+	  /* End-of-RIB received */
+
+	  if (BGP_DEBUG (update, UPDATE_IN))
+	    zlog (peer->log, LOG_DEBUG, "rcvd End-of-RIB for VPNv4 Unicast from %s",
+		  peer->host);
+	}
+    }
 
   /* Everything is done.  We unintern temporary structures which
      interned in bgp_attr_parse(). */
diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c
index efbd8db..6f3af9e 100644
--- a/bgpd/bgp_vty.c
+++ b/bgpd/bgp_vty.c
@@ -7303,11 +7303,13 @@
   else if (afi == AFI_IP && safi == SAFI_MULTICAST)
     return "IPv4 Multicast";
   else if (afi == AFI_IP && safi == SAFI_MPLS_VPN)
-    return "VPNv4 Unicast";
+    return "VPN-IPv4 Unicast";
   else if (afi == AFI_IP6 && safi == SAFI_UNICAST)
     return "IPv6 Unicast";
   else if (afi == AFI_IP6 && safi == SAFI_MULTICAST)
     return "IPv6 Multicast";
+  else if (afi == AFI_IP6 && safi == SAFI_MPLS_VPN)
+    return "VPN-IPv6 Unicast";
   else
     return "Unknown";
 }
@@ -7650,6 +7652,8 @@
 	  || p->afc_recv[AFI_IP6][SAFI_UNICAST]
 	  || p->afc_adv[AFI_IP6][SAFI_MULTICAST]
 	  || p->afc_recv[AFI_IP6][SAFI_MULTICAST]
+	  || p->afc_adv[AFI_IP6][SAFI_MPLS_VPN]
+	  || p->afc_recv[AFI_IP6][SAFI_MPLS_VPN]
 #endif /* HAVE_IPV6 */
 	  || p->afc_adv[AFI_IP][SAFI_MPLS_VPN]
 	  || p->afc_recv[AFI_IP][SAFI_MPLS_VPN])
diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c
index 2325f07..c3c0dba 100644
--- a/bgpd/bgpd.c
+++ b/bgpd/bgpd.c
@@ -978,6 +978,8 @@
 		  PEER_FLAG_REFLECTOR_CLIENT);
       UNSET_FLAG (peer->af_flags[AFI_IP6][SAFI_MULTICAST],
 		  PEER_FLAG_REFLECTOR_CLIENT);
+      UNSET_FLAG (peer->af_flags[AFI_IP6][SAFI_MPLS_VPN],
+		  PEER_FLAG_REFLECTOR_CLIENT);
     }
 
   /* local-as reset */
@@ -1394,7 +1396,8 @@
       || peer->af_group[AFI_IP][SAFI_MULTICAST]
       || peer->af_group[AFI_IP][SAFI_MPLS_VPN]
       || peer->af_group[AFI_IP6][SAFI_UNICAST]
-      || peer->af_group[AFI_IP6][SAFI_MULTICAST])
+      || peer->af_group[AFI_IP6][SAFI_MULTICAST]
+      || peer->af_group[AFI_IP6][SAFI_MPLS_VPN])
     return 1;
   return 0;
 }
@@ -2335,7 +2338,8 @@
       || peer->afc[AFI_IP][SAFI_MULTICAST]
       || peer->afc[AFI_IP][SAFI_MPLS_VPN]
       || peer->afc[AFI_IP6][SAFI_UNICAST]
-      || peer->afc[AFI_IP6][SAFI_MULTICAST])
+      || peer->afc[AFI_IP6][SAFI_MULTICAST]
+      || peer->afc[AFI_IP6][SAFI_MPLS_VPN])
     return 1;
   return 0;
 }
@@ -2348,7 +2352,8 @@
       || peer->afc_nego[AFI_IP][SAFI_MULTICAST]
       || peer->afc_nego[AFI_IP][SAFI_MPLS_VPN]
       || peer->afc_nego[AFI_IP6][SAFI_UNICAST]
-      || peer->afc_nego[AFI_IP6][SAFI_MULTICAST])
+      || peer->afc_nego[AFI_IP6][SAFI_MULTICAST]
+      || peer->afc_nego[AFI_IP6][SAFI_MPLS_VPN])
     return 1;
   return 0;
 }