]> git.proxmox.com Git - mirror_frr.git/blobdiff - bgpd/bgp_attr_evpn.c
bgpd: Allow shortened 'no set large-community' and 'no set large-comm-list'
[mirror_frr.git] / bgpd / bgp_attr_evpn.c
index 76c0e51198c854842dfe9980065337e262870600..88e520fdc508c24de4fdd1014124cd9cf3dee105 100644 (file)
@@ -1,22 +1,22 @@
 /* Ethernet-VPN Attribute handling file
  Copyright (C) 2016 6WIND
-
-This file is part of Free Range Routing.
-
-Free Range Routing is free software; you can redistribute it and/or modify it
-under the terms of the GNU General Public License as published by the
-Free Software Foundation; either version 2, or (at your option) any
-later version.
-
-Free Range Routing is distributed in the hope that it will be useful, but
-WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Free Range Routing; see the file COPYING.  If not, write to the Free
-Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
-02111-1307, USA.  */
* Copyright (C) 2016 6WIND
+ *
+ * This file is part of FRRouting.
+ *
+ * FRRouting is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRRouting is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
 
 #include <zebra.h>
 
@@ -27,111 +27,26 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 #include "memory.h"
 #include "stream.h"
 
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_route.h"
 #include "bgpd/bgp_attr_evpn.h"
+#include "bgpd/bgp_ecommunity.h"
+#include "bgpd/bgp_evpn.h"
+#include "bgpd/bgp_evpn_private.h"
 
-static uint8_t convertchartohexa (uint8_t *hexa, int *error)
+void bgp_add_routermac_ecom(struct attr *attr, struct ethaddr *routermac)
 {
-  if( (*hexa == '0') || (*hexa == '1') || (*hexa == '2') ||
-      (*hexa == '3') || (*hexa == '4') || (*hexa == '5') ||
-      (*hexa == '6') || (*hexa == '7') || (*hexa == '8') ||
-      (*hexa == '9'))
-    return (uint8_t)(*hexa)-'0';
-  if((*hexa == 'a') || (*hexa == 'A'))
-    return 0xa;
-  if((*hexa == 'b') || (*hexa == 'B'))
-    return 0xb;
-  if((*hexa == 'c') || (*hexa == 'C'))
-    return 0xc;
-  if((*hexa == 'd') || (*hexa == 'D'))
-    return 0xd;
-  if((*hexa == 'e') || (*hexa == 'E'))
-    return 0xe;
-  if((*hexa == 'f') || (*hexa == 'F'))
-    return 0xf;
-  *error = -1;
-  return 0;
-}
+       struct ecommunity_val routermac_ecom;
 
-/* converts to internal representation of mac address
- * returns 1 on success, 0 otherwise 
- * format accepted: AA:BB:CC:DD:EE:FF
- * if mac parameter is null, then check only
- */
-int
-str2mac (const char *str, char *mac)
-{
-  unsigned int k=0, i, j;
-  uint8_t *ptr, *ptr2;
-  size_t len;
-  uint8_t car;
-
-  if (!str)
-    return 0;
-
-  if (str[0] == ':' && str[1] == '\0')
-    return 1;
-
-  i = 0;
-  ptr = (uint8_t *)str;
-  while (i < 6)
-    {
-      uint8_t temp[5];
-      int error = 0;
-      ptr2 = (uint8_t *)strchr((const char *)ptr, ':');
-      if (ptr2 == NULL)
-       {
-         /* if last occurence return ok */
-         if(i != 5)
-            {
-              zlog_err("[%s]: format non recognized",mac);
-              return 0;
-            }
-          len = strlen((char *)ptr);
-       } 
-      else
-        {
-          len = ptr2 - ptr;
-        }
-      if(len > 5)
-        {
-          zlog_err("[%s]: format non recognized",mac);
-         return 0;
-        }
-      memcpy(temp, ptr, len);
-      for(j=0;j< len;j++)
-       {
-         if (k >= MAC_LEN)
-           return 0;
-          if(mac)
-            mac[k] = 0;
-          car = convertchartohexa (&temp[j], &error);
-         if (error)
-           return 0;
-         if(mac)
-            mac[k] = car << 4;
-         j++;
-          if(j == len)
-            return 0;
-          car = convertchartohexa (&temp[j], &error) & 0xf;
-         if (error)
-           return 0;
-         if(mac)
-            mac[k] |= car & 0xf;
-         k++;
-         i++;
-       }
-      ptr = ptr2;
-      if(ptr == NULL)
-        break;
-      ptr++;
-    }
-  if(mac && 0)
-    {
-      zlog_err("leave correct : %02x:%02x:%02x:%02x:%02x:%02x",
-               mac[0] & 0xff, mac[1] & 0xff, mac[2] & 0xff,
-               mac[3] & 0xff, mac[4] & 0xff, mac[5] & 0xff);
-    }
-  return 1;
+       memset(&routermac_ecom, 0, sizeof(struct ecommunity_val));
+       routermac_ecom.val[0] = ECOMMUNITY_ENCODE_EVPN;
+       routermac_ecom.val[1] = ECOMMUNITY_EVPN_SUBTYPE_ROUTERMAC;
+       memcpy(&routermac_ecom.val[2], routermac->octet, ETH_ALEN);
+       if (!attr->ecommunity)
+               attr->ecommunity = ecommunity_new();
+       ecommunity_add_val(attr->ecommunity, &routermac_ecom);
+       ecommunity_str(attr->ecommunity);
 }
 
 /* converts to an esi
@@ -139,125 +54,228 @@ str2mac (const char *str, char *mac)
  * format accepted: AA:BB:CC:DD:EE:FF:GG:HH:II:JJ
  * if id is null, check only is done
  */
-int
-str2esi (const char *str, struct eth_segment_id *id)
+int str2esi(const char *str, struct eth_segment_id *id)
+{
+       unsigned int a[ESI_LEN];
+       int i;
+
+       if (!str)
+               return 0;
+       if (sscanf(str, "%2x:%2x:%2x:%2x:%2x:%2x:%2x:%2x:%2x:%2x", a + 0, a + 1,
+                  a + 2, a + 3, a + 4, a + 5, a + 6, a + 7, a + 8, a + 9)
+           != ESI_LEN) {
+               /* error in incoming str length */
+               return 0;
+       }
+       /* valid mac address */
+       if (!id)
+               return 1;
+       for (i = 0; i < ESI_LEN; ++i)
+               id->val[i] = a[i] & 0xff;
+       return 1;
+}
+
+char *esi2str(struct eth_segment_id *id)
+{
+       char *ptr;
+       uint8_t *val;
+
+       if (!id)
+               return NULL;
+
+       val = id->val;
+       ptr = (char *)XMALLOC(MTYPE_TMP,
+                             (ESI_LEN * 2 + ESI_LEN - 1 + 1) * sizeof(char));
+
+       snprintf(ptr, (ESI_LEN * 2 + ESI_LEN - 1 + 1),
+                "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", val[0],
+                val[1], val[2], val[3], val[4], val[5], val[6], val[7], val[8],
+                val[9]);
+
+       return ptr;
+}
+
+char *ecom_mac2str(char *ecom_mac)
+{
+       char *en;
+
+       en = ecom_mac;
+       en += 2;
+
+       return prefix_mac2str((struct ethaddr *)en, NULL, 0);
+}
+
+/* Fetch router-mac from extended community */
+void bgp_attr_rmac(struct attr *attr, struct ethaddr *rmac)
 {
-  unsigned int k=0, i, j;
-  uint8_t *ptr, *ptr2;
-  size_t len;
-  uint8_t car;
-
-  if (!str)
-    return 0;
-  if (str[0] == ':' && str[1] == '\0')
-    return 1;
-
-  i = 0;
-  ptr = (uint8_t *)str;
-  while (i < 10)
-    {
-      uint8_t temp[5];
-      int error = 0;
-      ptr2 = (uint8_t *)strchr((const char *)ptr, ':');
-      if (ptr2 == NULL)
-       {
-         /* if last occurence return ok */
-         if(i != 9)
-            {
-              zlog_err("[%s]: format non recognized",str);
-              return 0;
-            }
-          len = strlen((char *)ptr);
+       int i = 0;
+       struct ecommunity *ecom;
+
+       ecom = attr->ecommunity;
+       if (!ecom || !ecom->size)
+               return;
+
+       /* If there is a router mac extended community, set RMAC in attr */
+       for (i = 0; i < ecom->size; i++) {
+               uint8_t *pnt = NULL;
+               uint8_t type = 0;
+               uint8_t sub_type = 0;
+
+               pnt = (ecom->val + (i * ECOMMUNITY_SIZE));
+               type = *pnt++;
+               sub_type = *pnt++;
+
+               if (!(type == ECOMMUNITY_ENCODE_EVPN
+                     && sub_type == ECOMMUNITY_EVPN_SUBTYPE_ROUTERMAC))
+                       continue;
+
+               memcpy(rmac, pnt, ETH_ALEN);
        }
-      else
-        {
-          len = ptr2 - ptr;
-        }
-      memcpy(temp, ptr, len);
-      if(len > 5)
-        {
-          zlog_err("[%s]: format non recognized",str);
-         return 0;
-        }
-      for(j=0;j< len;j++)
-       {
-         if (k >= ESI_LEN)
-           return 0;
-          if(id)
-            id->val[k] = 0;
-          car = convertchartohexa (&temp[j], &error);
-          if (error)
-            return 0;
-          if(id)
-            id->val[k] = car << 4;
-          j++;
-          if(j == len)
-            return 0;
-          car = convertchartohexa (&temp[j], &error) & 0xf;
-          if (error)
-            return 0;
-          if(id)
-            id->val[k] |= car & 0xf;
-         k++;
-         i++;
+}
+
+/*
+ * return true if attr contains default gw extended community
+ */
+uint8_t bgp_attr_default_gw(struct attr *attr)
+{
+       struct ecommunity *ecom;
+       int i;
+
+       ecom = attr->ecommunity;
+       if (!ecom || !ecom->size)
+               return 0;
+
+       /* If there is a default gw extendd community return true otherwise
+        * return 0 */
+       for (i = 0; i < ecom->size; i++) {
+               uint8_t *pnt;
+               uint8_t type, sub_type;
+
+               pnt = (ecom->val + (i * ECOMMUNITY_SIZE));
+               type = *pnt++;
+               sub_type = *pnt++;
+
+               if ((type == ECOMMUNITY_ENCODE_OPAQUE
+                    && sub_type == ECOMMUNITY_EVPN_SUBTYPE_DEF_GW))
+                       return 1;
        }
-      ptr = ptr2;
-      if(ptr == NULL)
-        break;
-      ptr++;
-    }
-  if(id && 0)
-    {
-      zlog_err("leave correct : %02x:%02x:%02x:%02x:%02x",
-               id->val[0], id->val[1], id->val[2], id->val[3], id->val[4]);
-      zlog_err("%02x:%02x:%02x:%02x:%02x",
-               id->val[5], id->val[6], id->val[7], id->val[8], id->val[9]);
-    }
-  return 1;
+
+       return 0;
 }
 
-char *
-esi2str (struct eth_segment_id *id)
+/*
+ * Fetch and return the sequence number from MAC Mobility extended
+ * community, if present, else 0.
+ */
+uint32_t bgp_attr_mac_mobility_seqnum(struct attr *attr, uint8_t *sticky)
 {
-  char *ptr;
-  u_char *val;
+       struct ecommunity *ecom;
+       int i;
+       uint8_t flags = 0;
 
-  if(!id)
-    return NULL;
+       ecom = attr->ecommunity;
+       if (!ecom || !ecom->size)
+               return 0;
 
-  val = id->val;
-  ptr = (char *) malloc ((ESI_LEN*2+ESI_LEN-1+1)*sizeof(char));
+       /* If there is a MAC Mobility extended community, return its
+        * sequence number.
+        * TODO: RFC is silent on handling of multiple MAC mobility extended
+        * communities for the same route. We will bail out upon the first
+        * one.
+        */
+       for (i = 0; i < ecom->size; i++) {
+               uint8_t *pnt;
+               uint8_t type, sub_type;
+               uint32_t seq_num;
 
-  snprintf (ptr, (ESI_LEN*2+ESI_LEN-1+1),
-            "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
-            val[0], val[1], val[2], val[3], val[4],
-            val[5], val[6], val[7], val[8], val[9]);
+               pnt = (ecom->val + (i * ECOMMUNITY_SIZE));
+               type = *pnt++;
+               sub_type = *pnt++;
+               if (!(type == ECOMMUNITY_ENCODE_EVPN
+                     && sub_type == ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY))
+                       continue;
+               flags = *pnt++;
 
-  return ptr;
+               if (flags & ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY_FLAG_STICKY)
+                       *sticky = 1;
+               else
+                       *sticky = 0;
+
+               pnt++;
+               pnt = ptr_get_be32(pnt, &seq_num);
+               (void)pnt; /* consume value */
+               return seq_num;
+       }
+
+       return 0;
 }
 
-char *
-mac2str (char *mac)
+/*
+ * return true if attr contains router flag extended community
+ */
+void bgp_attr_evpn_na_flag(struct attr *attr, uint8_t *router_flag)
 {
-  char *ptr;
+       struct ecommunity *ecom;
+       int i;
+       uint8_t val;
 
-  if(!mac)
-    return NULL;
+       ecom = attr->ecommunity;
+       if (!ecom || !ecom->size)
+               return;
 
-  ptr = (char *) malloc ((MAC_LEN*2+MAC_LEN-1+1)*sizeof(char));
+       /* If there is a evpn na extendd community set router_flag */
+       for (i = 0; i < ecom->size; i++) {
+               uint8_t *pnt;
+               uint8_t type, sub_type;
 
-  snprintf (ptr, (MAC_LEN*2+MAC_LEN-1+1), "%02x:%02x:%02x:%02x:%02x:%02x",
-           (uint8_t) mac[0], (uint8_t)mac[1], (uint8_t)mac[2], (uint8_t)mac[3],
-           (uint8_t)mac[4], (uint8_t)mac[5]);
+               pnt = (ecom->val + (i * ECOMMUNITY_SIZE));
+               type = *pnt++;
+               sub_type = *pnt++;
 
-  return ptr;
+               if (type == ECOMMUNITY_ENCODE_EVPN &&
+                   sub_type == ECOMMUNITY_EVPN_SUBTYPE_ND) {
+                       val = *pnt++;
+                       if (val & ECOMMUNITY_EVPN_SUBTYPE_ND_ROUTER_FLAG) {
+                               *router_flag = 1;
+                               break;
+                       }
+               }
+       }
 }
 
-char *ecom_mac2str(char *ecom_mac)
+/* dst prefix must be AF_INET or AF_INET6 prefix, to forge EVPN prefix */
+extern int bgp_build_evpn_prefix(int evpn_type, uint32_t eth_tag,
+                                struct prefix *dst)
 {
-  char *en;
+       struct evpn_addr *p_evpn_p;
+       struct prefix p2;
+       struct prefix *src = &p2;
 
-  en = ecom_mac;
-  en+=2;
-  return mac2str(en);
+       if (!dst || dst->family == 0)
+               return -1;
+       /* store initial prefix in src */
+       prefix_copy(src, dst);
+       memset(dst, 0, sizeof(struct prefix));
+       p_evpn_p = &(dst->u.prefix_evpn);
+       dst->family = AF_EVPN;
+       p_evpn_p->route_type = evpn_type;
+       if (evpn_type == BGP_EVPN_IP_PREFIX_ROUTE) {
+               p_evpn_p->prefix_addr.eth_tag = eth_tag;
+               p_evpn_p->prefix_addr.ip_prefix_length = p2.prefixlen;
+               if (src->family == AF_INET) {
+                       SET_IPADDR_V4(&p_evpn_p->prefix_addr.ip);
+                       memcpy(&p_evpn_p->prefix_addr.ip.ipaddr_v4,
+                              &src->u.prefix4,
+                              sizeof(struct in_addr));
+                       dst->prefixlen = (uint8_t)PREFIX_LEN_ROUTE_TYPE_5_IPV4;
+               } else {
+                       SET_IPADDR_V6(&p_evpn_p->prefix_addr.ip);
+                       memcpy(&p_evpn_p->prefix_addr.ip.ipaddr_v6,
+                              &src->u.prefix6,
+                              sizeof(struct in6_addr));
+                       dst->prefixlen = (uint8_t)PREFIX_LEN_ROUTE_TYPE_5_IPV6;
+               }
+       } else
+               return -1;
+       return 0;
 }