bgpd: encap: add encap SAFI (RFC5512)

Adds RFC5512 and Encapsulation Attribute.

Signed-off-by: Lou Berger <lberger@labn.net>
diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c
index b024d80..a780e9d 100644
--- a/bgpd/bgp_route.c
+++ b/bgpd/bgp_route.c
@@ -71,7 +71,7 @@
   if (!table)
     return NULL;
   
-  if (safi == SAFI_MPLS_VPN)
+  if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP))
     {
       prn = bgp_node_get (table, (struct prefix *) prd);
 
@@ -84,7 +84,7 @@
 
   rn = bgp_node_get (table, p);
 
-  if (safi == SAFI_MPLS_VPN)
+  if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP))
     rn->prn = prn;
 
   return rn;
@@ -980,13 +980,24 @@
 	attr->flag &= ~(ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC));
     }
 
+
+#define NEXTHOP_IS_V4 (\
+    (safi != SAFI_ENCAP && p->family == AF_INET) || \
+    (safi == SAFI_ENCAP && attr->extra->mp_nexthop_len == 4))
+
+#ifdef HAVE_IPV6
+#define NEXTHOP_IS_V6 (\
+    (safi != SAFI_ENCAP && p->family == AF_INET6) || \
+    (safi == SAFI_ENCAP && attr->extra->mp_nexthop_len == 16))
+#endif
+
   /* next-hop-set */
   if (transparent
       || (reflect && ! CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_NEXTHOP_SELF_ALL))
       || (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_NEXTHOP_UNCHANGED)
-	  && ((p->family == AF_INET && attr->nexthop.s_addr)
+	  && ((NEXTHOP_IS_V4 && attr->nexthop.s_addr)
 #ifdef HAVE_IPV6
-	      || (p->family == AF_INET6 && 
+	      || (NEXTHOP_IS_V6 &&
                   ! IN6_IS_ADDR_UNSPECIFIED(&attr->extra->mp_nexthop_global))
 #endif /* HAVE_IPV6 */
 	      )))
@@ -994,18 +1005,18 @@
       /* NEXT-HOP Unchanged. */
     }
   else if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_NEXTHOP_SELF)
-	   || (p->family == AF_INET && attr->nexthop.s_addr == 0)
+	   || (NEXTHOP_IS_V4 && attr->nexthop.s_addr == 0)
 #ifdef HAVE_IPV6
-	   || (p->family == AF_INET6 && 
+	   || (NEXTHOP_IS_V6 &&
                IN6_IS_ADDR_UNSPECIFIED(&attr->extra->mp_nexthop_global))
 #endif /* HAVE_IPV6 */
 	   || (peer->sort == BGP_PEER_EBGP
 	       && bgp_multiaccess_check_v4 (attr->nexthop, peer->host) == 0))
     {
       /* Set IPv4 nexthop. */
-      if (p->family == AF_INET)
+      if (NEXTHOP_IS_V4)
 	{
-	  if (safi == SAFI_MPLS_VPN)
+	  if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP))
 	    memcpy (&attr->extra->mp_nexthop_global_in, &peer->nexthop.v4,
 	            IPV4_MAX_BYTELEN);
 	  else
@@ -1013,7 +1024,7 @@
 	}
 #ifdef HAVE_IPV6
       /* Set IPv6 nexthop. */
-      if (p->family == AF_INET6)
+      if (NEXTHOP_IS_V6)
 	{
 	  /* IPv6 global nexthop must be included. */
 	  memcpy (&attr->extra->mp_nexthop_global, &peer->nexthop.v6_global, 
@@ -1024,7 +1035,7 @@
     }
 
 #ifdef HAVE_IPV6
-  if (p->family == AF_INET6)
+  if (p->family == AF_INET6 && safi != SAFI_ENCAP)
     {
       /* Left nexthop_local unchanged if so configured. */ 
       if ( CHECK_FLAG (peer->af_flags[afi][safi], 
@@ -1214,7 +1225,7 @@
     /* Set IPv4 nexthop. */
     if (p->family == AF_INET)
       {
-        if (safi == SAFI_MPLS_VPN)
+        if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP))
           memcpy (&attr->extra->mp_nexthop_global_in, &rsclient->nexthop.v4,
                   IPV4_MAX_BYTELEN);
         else
@@ -2657,10 +2668,12 @@
   struct attr attr;
   struct attr_extra extra;
 
+  memset(&extra, 0, sizeof(extra));
+
   if (! table)
     table = (rsclient) ? peer->rib[afi][safi] : peer->bgp->rib[afi][safi];
 
-  if (safi != SAFI_MPLS_VPN
+  if ((safi != SAFI_MPLS_VPN) && (safi != SAFI_ENCAP)
       && CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_DEFAULT_ORIGINATE))
     bgp_default_originate (peer, afi, safi, 0);
 
@@ -2678,6 +2691,8 @@
 	  else
 	    bgp_adj_out_unset (rn, peer, &rn->p, afi, safi);
 	}
+
+  bgp_attr_flush_encap(&attr);
 }
 
 void
@@ -2696,7 +2711,7 @@
   if (CHECK_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_ORF_WAIT_REFRESH))
     return;
 
-  if (safi != SAFI_MPLS_VPN)
+  if ((safi != SAFI_MPLS_VPN) && (safi != SAFI_ENCAP))
     bgp_announce_table (peer, afi, safi, NULL, 0);
   else
     for (rn = bgp_table_top (peer->bgp->rib[afi][safi]); rn;
@@ -2746,7 +2761,7 @@
   struct bgp_table *table;
   struct bgp_node *rn;
   
-  if (safi != SAFI_MPLS_VPN)
+  if ((safi != SAFI_MPLS_VPN) && (safi != SAFI_ENCAP))
     bgp_soft_reconfig_table_rsclient (rsclient, afi, safi, NULL, NULL);
 
   else
@@ -2805,7 +2820,7 @@
   if (peer->status != Established)
     return;
 
-  if (safi != SAFI_MPLS_VPN)
+  if ((safi != SAFI_MPLS_VPN) && (safi != SAFI_ENCAP))
     bgp_soft_reconfig_table (peer, afi, safi, NULL, NULL);
   else
     for (rn = bgp_table_top (peer->bgp->rib[afi][safi]); rn;
@@ -3022,7 +3037,7 @@
   switch (purpose)
     {
     case BGP_CLEAR_ROUTE_NORMAL:
-      if (safi != SAFI_MPLS_VPN)
+      if ((safi != SAFI_MPLS_VPN) && (safi != SAFI_ENCAP))
         bgp_clear_route_table (peer, afi, safi, NULL, NULL, purpose);
       else
         for (rn = bgp_table_top (peer->bgp->rib[afi][safi]); rn;
@@ -3197,13 +3212,27 @@
 	   */
 	  for (rn = bgp_table_top(bgp->rib[afi][SAFI_MPLS_VPN]); rn;
                rn = bgp_route_next (rn))
-	    if (rn->info)
+	    {
+	      if (rn->info)
                 {
-                   bgp_cleanup_table((struct bgp_table *)(rn->info), SAFI_MPLS_VPN);
-                   bgp_table_finish ((struct bgp_table **)&(rn->info));
-		   rn->info = NULL;
-		   bgp_unlock_node(rn);
+		  bgp_cleanup_table((struct bgp_table *)(rn->info), SAFI_MPLS_VPN);
+		  bgp_table_finish ((struct bgp_table **)&(rn->info));
+		  rn->info = NULL;
+		  bgp_unlock_node(rn);
                 }
+	    }
+
+	  for (rn = bgp_table_top(bgp->rib[afi][SAFI_ENCAP]); rn;
+               rn = bgp_route_next (rn))
+	    {
+	      if (rn->info)
+		{
+		  bgp_cleanup_table((struct bgp_table *)(rn->info), SAFI_ENCAP);
+		  bgp_table_finish ((struct bgp_table **)&(rn->info));
+		  rn->info = NULL;
+		  bgp_unlock_node(rn);
+		}
+	    }
 	}
     }
 }
@@ -3335,11 +3364,22 @@
 
   while (pnt < end)
     {
+      int	badlength;
       prefixlen = *pnt++;
       
       /* Prefix length check. */
-      if ((afi == AFI_IP && prefixlen > 32)
-	  || (afi == AFI_IP6 && prefixlen > 128))
+      badlength = 0;
+      if (safi == SAFI_ENCAP) {
+	if (prefixlen > 128)
+	  badlength = 1;
+      } else {
+        if ((afi == AFI_IP && prefixlen > 32) ||
+	    (afi == AFI_IP6 && prefixlen > 128)) {
+
+	    badlength = 1;
+	}
+      }
+      if (badlength)
 	{
 	  plog_err (peer->log, 
 		    "%s [Error] Update packet error (wrong prefix length %d)",
@@ -4082,7 +4122,7 @@
       for (rn = bgp_table_top (bgp->route[afi][safi]); rn; rn = bgp_route_next (rn))
 	if (rn->info != NULL)
 	  {      
-	    if (safi == SAFI_MPLS_VPN)
+	    if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP))
 	      {
 		table = rn->info;
 
@@ -4938,7 +4978,7 @@
   struct bgp_table *table;
 
   /* MPLS-VPN aggregation is not yet supported. */
-  if (safi == SAFI_MPLS_VPN)
+  if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP))
     return;
 
   table = bgp->aggregate[afi][safi];
@@ -4975,7 +5015,7 @@
   struct bgp_table *table;
 
   /* MPLS-VPN aggregation is not yet supported. */
-  if (safi == SAFI_MPLS_VPN)
+  if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP))
     return;
 
   table = bgp->aggregate[afi][safi];
@@ -5918,30 +5958,71 @@
   attr = binfo->attr;
   if (attr) 
     {
-      if (p->family == AF_INET)
-	{
-	  if (safi == SAFI_MPLS_VPN)
-	    vty_out (vty, "%-16s",
-                     inet_ntoa (attr->extra->mp_nexthop_global_in));
-	  else
-	    vty_out (vty, "%-16s", inet_ntoa (attr->nexthop));
-	}
-#ifdef HAVE_IPV6      
-      else if (p->family == AF_INET6)
-	{
-	  int len;
-	  char buf[BUFSIZ];
 
-	  len = vty_out (vty, "%s", 
-			 inet_ntop (AF_INET6, &attr->extra->mp_nexthop_global,
-			 buf, BUFSIZ));
-	  len = 16 - len;
-	  if (len < 1)
-	    vty_out (vty, "%s%*s", VTY_NEWLINE, 36, " ");
-	  else
-	    vty_out (vty, "%*s", len, " ");
+      /*
+       * NEXTHOP start
+       */
+
+      /*
+       * For ENCAP routes, nexthop address family is not
+       * neccessarily the same as the prefix address family.
+       * Both SAFI_MPLS_VPN and SAFI_ENCAP use the MP nexthop field
+       */
+      if ((safi == SAFI_ENCAP) || (safi == SAFI_MPLS_VPN)) {
+	if (attr->extra) {
+	    char	buf[BUFSIZ];
+	    int		af = NEXTHOP_FAMILY(attr->extra->mp_nexthop_len);
+
+	    switch (af) {
+		case AF_INET:
+		    vty_out (vty, "%s", inet_ntop(af,
+			&attr->extra->mp_nexthop_global_in, buf, BUFSIZ));
+		    break;
+#if HAVE_IPV6
+		case AF_INET6:
+		    vty_out (vty, "%s", inet_ntop(af,
+			&attr->extra->mp_nexthop_global, buf, BUFSIZ));
+		    break;
+#endif
+
+		default:
+		    vty_out(vty, "?");
+	    }
+	} else {
+	    vty_out(vty, "?");
 	}
+      } else {
+
+	  if (p->family == AF_INET)
+	    {
+		vty_out (vty, "%-16s", inet_ntoa (attr->nexthop));
+	    }
+#ifdef HAVE_IPV6      
+	  else if (p->family == AF_INET6)
+	    {
+	      int len;
+	      char buf[BUFSIZ];
+
+	      len = vty_out (vty, "%s",
+			     inet_ntop (AF_INET6, &attr->extra->mp_nexthop_global,
+			     buf, BUFSIZ));
+	      len = 16 - len;
+	      if (len < 1)
+		vty_out (vty, "%s%*s", VTY_NEWLINE, 36, " ");
+	      else
+		vty_out (vty, "%*s", len, " ");
+	    }
 #endif /* HAVE_IPV6 */
+         else
+	   {
+	     vty_out(vty, "?");
+	   }
+      }
+
+      /*
+       * NEXTHOP end
+       */
+
 
       if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC))
 	vty_out (vty, "%10u", attr->med);
@@ -5983,7 +6064,7 @@
     {
       if (p->family == AF_INET)
 	{
-	  if (safi == SAFI_MPLS_VPN)
+	  if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP))
 	    vty_out (vty, "%-16s",
                      inet_ntoa (attr->extra->mp_nexthop_global_in));
 	  else
@@ -6056,7 +6137,7 @@
     {
       if (p->family == AF_INET)
 	{
-	  if (safi == SAFI_MPLS_VPN)
+	  if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP))
 	    vty_out (vty, "%-16s",
                      inet_ntoa (attr->extra->mp_nexthop_global_in));
 	  else
@@ -6239,7 +6320,7 @@
       /* Line2 display Next-hop, Neighbor, Router-id */
       if (p->family == AF_INET)
 	{
-	  vty_out (vty, "    %s", safi == SAFI_MPLS_VPN ?
+	  vty_out (vty, "    %s", ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP)) ?
 		   inet_ntoa (attr->extra->mp_nexthop_global_in) :
 		   inet_ntoa (attr->nexthop));
 	}
@@ -6675,12 +6756,12 @@
   int no_advertise = 0;
   int local_as = 0;
   int first = 0;
+  int printrd = ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP));
 
   p = &rn->p;
   vty_out (vty, "BGP routing table entry for %s%s%s/%d%s",
-	   (safi == SAFI_MPLS_VPN ?
-	   prefix_rd2str (prd, buf1, RD_ADDRSTRLEN) : ""),
-	   safi == SAFI_MPLS_VPN ? ":" : "",
+	   (printrd ?  prefix_rd2str (prd, buf1, RD_ADDRSTRLEN) : ""),
+	   printrd ?  ":" : "",
 	   inet_ntop (p->family, &p->u.prefix, buf2, INET6_ADDRSTRLEN),
 	   p->prefixlen, VTY_NEWLINE);
 
@@ -6766,7 +6847,7 @@
 
   match.family = afi2family (afi);
 
-  if (safi == SAFI_MPLS_VPN)
+  if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP))
     {
       for (rn = bgp_table_top (rib); rn; rn = bgp_route_next (rn))
         {
@@ -6790,12 +6871,12 @@
                       if (header)
                         {
                           route_vty_out_detail_header (vty, bgp, rm, (struct prefix_rd *)&rn->p,
-                                                       AFI_IP, SAFI_MPLS_VPN);
+                                                       AFI_IP, safi);
 
                           header = 0;
                         }
                       display++;
-                      route_vty_out_detail (vty, bgp, &rm->p, ri, AFI_IP, SAFI_MPLS_VPN);
+                      route_vty_out_detail (vty, bgp, &rm->p, ri, AFI_IP, safi);
                     }
 
                   bgp_unlock_node (rm);
@@ -9835,22 +9916,28 @@
                    afi_str, VTY_NEWLINE);
           return CMD_WARNING;
         }
-      if (strncmp (safi_str, "m", 1) == 0)
-        safi = SAFI_MULTICAST;
-      else if (strncmp (safi_str, "u", 1) == 0)
-        safi = SAFI_UNICAST;
-      else if (strncmp (safi_str, "vpnv4", 5) == 0 || strncmp (safi_str, "vpnv6", 5) == 0)
-        safi = SAFI_MPLS_LABELED_VPN;
-      else
-        {
-          vty_out (vty, "%% Invalid subsequent address family %s%s",
+      switch (safi_str[0]) {
+	case 'm':
+	    safi = SAFI_MULTICAST;
+	    break;
+	case 'u':
+	    safi = SAFI_UNICAST;
+	    break;
+	case 'v':
+	    safi =  SAFI_MPLS_LABELED_VPN;
+	    break;
+	case 'e':
+	    safi = SAFI_ENCAP;
+	    break;
+	default:
+	    vty_out (vty, "%% Invalid subsequent address family %s%s",
                    safi_str, VTY_NEWLINE);
-          return CMD_WARNING;
-        }
+            return CMD_WARNING;
+      }
     }
   else
     {
-      vty_out (vty, "%% Invalid address family %s%s",
+      vty_out (vty, "%% Invalid address family \"%s\"%s",
                afi_str, VTY_NEWLINE);
       return CMD_WARNING;
     }
@@ -9860,30 +9947,23 @@
 
 DEFUN (show_bgp_statistics,
        show_bgp_statistics_cmd,
-       "show bgp (ipv4|ipv6) (unicast|multicast) statistics",
+       "show bgp (ipv4|ipv6) (encap|multicast|unicast|vpn) statistics",
        SHOW_STR
        BGP_STR
        "Address family\n"
        "Address family\n"
        "Address Family modifier\n"
        "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
        "BGP RIB advertisement statistics\n")
 {
   return bgp_table_stats_vty (vty, NULL, argv[0], argv[1]);
 }
 
-ALIAS (show_bgp_statistics,
-       show_bgp_statistics_vpnv4_cmd,
-       "show bgp (ipv4) (vpnv4) statistics",
-       SHOW_STR
-       BGP_STR
-       "Address family\n"
-       "Address Family modifier\n"
-       "BGP RIB advertisement statistics\n")
-
 DEFUN (show_bgp_statistics_view,
        show_bgp_statistics_view_cmd,
-       "show bgp view WORD (ipv4|ipv6) (unicast|multicast) statistics",
+       "show bgp view WORD (ipv4|ipv6) (encap|multicast|unicast|vpn) statistics",
        SHOW_STR
        BGP_STR
        "BGP view\n"
@@ -9891,20 +9971,24 @@
        "Address family\n"
        "Address Family modifier\n"
        "Address Family modifier\n"
+       "Address Family modifier\n"
+       "Address Family modifier\n"
        "BGP RIB advertisement statistics\n")
 {
   return bgp_table_stats_vty (vty, NULL, argv[0], argv[1]);
 }
 
+#if 0 /* added as options to above command */
 ALIAS (show_bgp_statistics_view,
-       show_bgp_statistics_view_vpnv4_cmd,
-       "show bgp view WORD (ipv4) (vpnv4) statistics",
+       show_bgp_statistics_view_encap_cmd,
+       "show bgp view WORD (ipv4) (encap) statistics",
        SHOW_STR
        BGP_STR
        "BGP view\n"
        "Address family\n"
        "Address Family modifier\n"
        "BGP RIB advertisement statistics\n")
+#endif
 
 enum bgp_pcounts
 {
@@ -12408,9 +12492,9 @@
 
   match.family = afi2family (afi);
 
-  if (safi == SAFI_MPLS_VPN)
+  if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP))
     {
-      for (rn = bgp_table_top (bgp->rib[AFI_IP][SAFI_MPLS_VPN]); rn; rn = bgp_route_next (rn))
+      for (rn = bgp_table_top (bgp->rib[AFI_IP][safi]); rn; rn = bgp_route_next (rn))
         {
           if (prd && memcmp (rn->p.u.val, prd->val, 8) != 0)
             continue;
@@ -12527,6 +12611,7 @@
 			       SAFI_UNICAST, NULL, 0);
 }
 
+/* also used for encap safi */
 static int
 bgp_config_write_network_vpnv4 (struct vty *vty, struct bgp *bgp,
 				afi_t afi, safi_t safi, int *write)
@@ -12578,7 +12663,7 @@
   struct bgp_aggregate *bgp_aggregate;
   char buf[SU_ADDRSTRLEN];
   
-  if (afi == AFI_IP && safi == SAFI_MPLS_VPN)
+  if (afi == AFI_IP && ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP)))
     return bgp_config_write_network_vpnv4 (vty, bgp, afi, safi, write);
 
   /* Network configuration. */
@@ -13302,9 +13387,9 @@
   
   /* Statistics */
   install_element (ENABLE_NODE, &show_bgp_statistics_cmd);
-  install_element (ENABLE_NODE, &show_bgp_statistics_vpnv4_cmd);
+  //install_element (ENABLE_NODE, &show_bgp_statistics_vpnv4_cmd);
   install_element (ENABLE_NODE, &show_bgp_statistics_view_cmd);
-  install_element (ENABLE_NODE, &show_bgp_statistics_view_vpnv4_cmd);
+  //install_element (ENABLE_NODE, &show_bgp_statistics_view_vpnv4_cmd);
   
   /* old command */
   install_element (VIEW_NODE, &show_ipv6_bgp_cmd);