]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Filter: Ethernet and EVPN support
authorOndrej Zajicek <santiago@crfreenet.org>
Thu, 25 Jan 2024 17:39:40 +0000 (18:39 +0100)
committerOndrej Zajicek <santiago@crfreenet.org>
Sun, 15 Jun 2025 00:51:51 +0000 (02:51 +0200)
filter/config.Y
filter/data.c
filter/data.h
filter/f-inst.c
filter/test.conf
proto/aggregator/aggregator.c

index 562530e2d154e84db07f88ecedc7b0a80185e484..f468489fa588ae60269e3da91a465a9233a76bf6 100644 (file)
@@ -126,6 +126,7 @@ f_valid_set_type(int type)
   case T_EC:
   case T_LC:
   case T_RD:
+  case T_MAC:
     return 1;
 
   default:
@@ -357,7 +358,7 @@ CF_DECLS
 CF_KEYWORDS_EXCLUSIVE(IN)
 CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
        ACCEPT, REJECT, ERROR,
-       INT, BOOL, IP, PREFIX, RD, PAIR, QUAD, EC, LC, ENUM,
+       INT, BOOL, IP, PREFIX, RD, MAC, PAIR, QUAD, EC, LC, ENUM,
        SET, STRING, BYTESTRING, BGPMASK, BGPPATH, CLIST, ECLIST, LCLIST,
        IF, THEN, ELSE, CASE,
        FOR, DO,
@@ -455,6 +456,7 @@ type:
  | BOOL { $$ = T_BOOL; }
  | IP { $$ = T_IP; }
  | RD { $$ = T_RD; }
+ | MAC { $$ = T_MAC; }
  | PREFIX { $$ = T_NET; }
  | PAIR { $$ = T_PAIR; }
  | QUAD { $$ = T_QUAD; }
@@ -476,8 +478,9 @@ type:
          case T_ENUM:
          case T_EC:
          case T_LC:
-         case T_RD:
          case T_IP:
+         case T_RD:
+         case T_MAC:
               $$ = T_SET;
               break;
 
@@ -647,6 +650,7 @@ set_atom0:
    NUM    { $$.type = T_INT; $$.val.i = $1; }
  | fipa   { $$ = $1; }
  | VPN_RD { $$.type = T_RD; $$.val.rd = $1; }
+ | MAC_   { $$.type = T_MAC; $$.val.mac = $1; }
  | ENUM_TOKEN { $$.type = pair_a($1); $$.val.i = pair_b($1); }
  | '(' term ')' {
      $$ = cf_eval($2, T_VOID);
@@ -807,6 +811,7 @@ constant:
  | BYTETEXT { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_BYTESTRING, .val.bs = $1, }); }
  | fipa     { $$ = f_new_inst(FI_CONSTANT, $1); }
  | VPN_RD   { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_RD, .val.rd = $1, }); }
+ | MAC_     { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_MAC, .val.mac = $1, }); }
  | net_     { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_NET, .val.net = $1, }); }
  | '[' ']' { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_SET, .val.t = NULL, }); }
  | '[' set_items ']' {
index bc6351cfed7424e7df25958658f692178b9177bd..a1d9812c8b8fee4e61fd0b9bff2abf1447271eea 100644 (file)
@@ -58,6 +58,7 @@ static const char * const f_type_str[] = {
   [T_LC]       = "lc",
   [T_LCLIST]   = "lclist",
   [T_RD]       = "rd",
+  [T_MAC]      = "mac",
 
   [T_ROUTE] = "route",
   [T_ROUTES_BLOCK] = "block of routes",
@@ -208,6 +209,8 @@ val_compare(const struct f_val *v1, const struct f_val *v2)
     return lcomm_cmp(v1->val.lc, v2->val.lc);
   case T_IP:
     return ipa_compare(v1->val.ip, v2->val.ip);
+  case T_MAC:
+    return mac_compare(v1->val.mac, v2->val.mac);
   case T_NET:
     return net_compare(v1->val.net, v2->val.net);
   case T_STRING:
@@ -635,6 +638,7 @@ val_format(const struct f_val *v, buffer *buf)
   case T_EC:   ec_format(buf2, v->val.ec); buffer_print(buf, "%s", buf2); return;
   case T_LC:   lc_format(buf2, v->val.lc); buffer_print(buf, "%s", buf2); return;
   case T_RD:   rd_format(v->val.rd, buf2, 1024); buffer_print(buf, "%s", buf2); return;
+  case T_MAC:  buffer_print(buf, "%6b", v->val.mac.addr); return;
   case T_PREFIX_SET: trie_format(v->val.ti, buf); return;
   case T_SET:  tree_format(v->val.t, buf); return;
   case T_ENUM: buffer_print(buf, "(enum %x)%u", v->type, v->val.i); return;
index 5e529748af3a7ca2821ebef63f4dde599f557dbb..c0aa5e864520e303a8067f3d77cb6b4690d9d669 100644 (file)
@@ -64,6 +64,7 @@ enum f_type {
   T_RD = 0x2a,         /* Route distinguisher for VPN addresses */
   T_PATH_MASK_ITEM = 0x2b,     /* Path mask item for path mask constructors */
   T_BYTESTRING = 0x2c,
+  T_MAC = 0x2d,
 
   T_ROUTE = 0x78,
   T_ROUTES_BLOCK = 0x79,
@@ -88,6 +89,7 @@ struct f_val {
     lcomm lc;
     vpn_rd rd;
     ip_addr ip;
+    mac_addr mac;
     const net_addr *net;
     const char *s;
     const struct adata *bs;
index f94580a662e7acaf5868b7730e2b1d0645050231..29fb84f473d18cabe43cb608bb40f9c941c3a067 100644 (file)
   ]]);
 
   /* Convert prefix to IP */
-  METHOD_R(T_NET, ip, T_IP, ip, net_prefix(v1.val.net));
+  METHOD(T_NET, ip, 0, [[
+    const net_addr_union *net = (void *) v1.val.net;
+
+    if ((net->n.type == NET_EVPN) && (net->evpn.subtype == NET_EVPN_MAC))
+      RESULT(T_IP, ip, (net->n.length == sizeof(net_addr_evpn_mac_ip)) ? net->evpn.mac_ip.ip : IPA_NONE);
+    else
+      RESULT(T_IP, ip, net_prefix(v1.val.net));
+  ]]);
+
+  /* Get route distinguisher */
+  METHOD(T_NET, rd, 0, [[
+    if (!net_type_match(v1.val.net, NB_VPN | NB_EVPN))
+      runtime( "VPN or EVPN address expected" );
 
-  INST(FI_ROUTE_DISTINGUISHER, 1, 1) {
-    ARG(1, T_NET);
-    METHOD_CONSTRUCTOR("rd");
-    if (!net_is_vpn(v1.val.net))
-      runtime( "VPN address expected" );
     RESULT(T_RD, rd, net_rd(v1.val.net));
-  }
+  ]]);
+
+  /* Get MAC address */
+  METHOD(T_NET, mac, 0, [[
+    const net_addr_union *net = (void *) v1.val.net;
+
+    if (net->n.type == NET_ETH)
+      RESULT(T_MAC, mac, net->eth.mac);
+    else if ((net->n.type == NET_EVPN) && (net->evpn.subtype == NET_EVPN_MAC))
+      RESULT(T_MAC, mac, net->evpn.mac.mac);
+    else
+      runtime( "Ethernet or EVPN MAC expected" );
+  ]]);
+
+  /* Get VLAN ID */
+  METHOD(T_NET, vlan_id, 0, [[
+    if (v1.val.net->type != NET_ETH)
+      runtime( "Ethernet address expected" );
+
+    const net_addr_eth *eth = (void *) v1.val.net;
+    RESULT(T_INT, i, eth->vid);
+  ]]);
+
+  /* Get EVPN type */
+  METHOD(T_NET, evpn_type, 0, [[
+    if (v1.val.net->type != NET_EVPN)
+      runtime( "EVPN address expected" );
+
+    const net_addr_evpn *evpn = (void *) v1.val.net;
+    RESULT(T_ENUM_NET_EVPN_TYPE, i, evpn->subtype);
+  ]]);
+
+  /* Get EVPN tag */
+  METHOD(T_NET, evpn_tag, 0, [[
+    if (v1.val.net->type != NET_EVPN)
+      runtime( "EVPN address expected" );
+
+    const net_addr_evpn *evpn = (void *) v1.val.net;
+    if (evpn->subtype > NET_EVPN_IMET)
+      runtime( "EVPN EAD/MAC/IMET address expected" );
+
+    RESULT(T_INT, i, evpn->tag);
+  ]]);
+
+  /* Get EVPN ESI */
+  METHOD(T_NET, evpn_esi, 0, [[
+    if (v1.val.net->type != NET_EVPN)
+      runtime( "EVPN address expected" );
+
+    const net_addr_evpn *evpn = (void *) v1.val.net;
+    const evpn_esi *esi;
+
+    switch (evpn->subtype)
+    {
+    case NET_EVPN_EAD:
+      esi = &evpn->ead.esi;
+      break;
+
+    case NET_EVPN_ES:
+      esi = &evpn->es.esi;
+      break;
+
+    default:
+      runtime( "EVPN EAD/ES address expected" );
+    }
+
+    struct adata *bs;
+    bs = falloc(sizeof(struct adata) + sizeof(evpn_esi));
+    bs->length = sizeof(evpn_esi);
+    memcpy(bs->data, esi, sizeof(evpn_esi));
+
+    RESULT(T_BYTESTRING, bs, bs);
+  ]]);
+
+  /* Get EVPN outer IP */
+  METHOD(T_NET, router_ip, 0, [[
+    if (v1.val.net->type != NET_EVPN)
+      runtime( "EVPN address expected" );
+
+    const net_addr_evpn *evpn = (void *) v1.val.net;
+
+    switch (evpn->subtype)
+    {
+    case NET_EVPN_IMET:
+      RESULT(T_IP, ip, evpn->imet.rtr);
+      break;
+
+    case NET_EVPN_ES:
+      RESULT(T_IP, ip, evpn->es.rtr);
+      break;
+
+    default:
+      runtime( "EVPN IMET/ES address expected" );
+    }
+  ]]);
 
   /* Get first ASN from AS PATH */
   METHOD_R(T_PATH, first, T_INT, i, ({ u32 as = 0; as_path_get_first(v1.val.ad, &as); as; }));
index 3df9b6975262d2ed905f5f5de6204f00f9d21fb6..f5d84f7d6714f040bcdbd0072682a1721562cdad 100644 (file)
@@ -568,6 +568,7 @@ function t_enum()
        enum bgp_origin ev7 = ORIGIN_IGP;
        enum ra_preference ev8 = RA_PREF_LOW;
        enum mpls_policy ev9 = MPLS_POLICY_STATIC;
+       enum net_evpn_type eva = NET_EVPN_MAC;
 
        enum net_type set es = [NET_IP6, NET_VPN6];
 
@@ -578,6 +579,7 @@ function t_enum()
 
        bt_assert(ev0 = RTS_STATIC);
        bt_assert(ev6 = NET_IP6);
+       bt_assert(eva = NET_EVPN_MAC);
 
        bt_assert(RTS_STATIC ~ [RTS_STATIC, RTS_DEVICE]);
        bt_assert(RTS_BGP !~ [RTS_STATIC, RTS_DEVICE]);
@@ -2000,6 +2002,107 @@ bt_test_suite(t_net_sadr, "Testing IPv6 SADR nets");
 
 
 
+/*
+ *     Testing Ethernet nets
+ *     ---------------------
+ */
+
+function t_net_eth()
+{
+       prefix p;
+
+       p = eth 12:3f:c9:48:9c:9b;
+       bt_assert(format(p) = "12:3f:c9:48:9c:9b");
+       bt_assert(p.type = NET_ETH);
+       bt_assert(p.mac = 12:3f:c9:48:9c:9b);
+       bt_assert(p.vlan_id = 0);
+
+       p = eth 16:36:f8:80:5b:48 vlan 100;
+       bt_assert(format(p) = "16:36:f8:80:5b:48 vlan 100");
+       bt_assert(p.type = NET_ETH);
+       bt_assert(p.mac = 16:36:f8:80:5b:48);
+       bt_assert(p.vlan_id = 100);
+}
+
+bt_test_suite(t_net_eth, "Testing Ethernet nets");
+
+
+
+
+/*
+ *     Testing EVPN nets
+ *     -----------------
+ */
+
+function t_net_evpn()
+{
+       prefix p;
+
+       p = evpn ead 10:1010 210 00:10:20:30:40:50:60:70:80:90;
+       bt_assert(format(p) = "evpn ead 10:1010 210 00:10:20:30:40:50:60:70:80:90");
+       bt_assert(p.type = NET_EVPN);
+       bt_assert(p.evpn_type = NET_EVPN_EAD);
+       bt_assert(p.rd = 10:1010);
+       bt_assert(p.evpn_tag = 210);
+       bt_assert(p.evpn_esi = 00:10:20:30:40:50:60:70:80:90);
+
+       p = evpn mac 10:1020 220 12:a6:54:da:bc:bf *;
+       bt_assert(format(p) = "evpn mac 10:1020 220 12:a6:54:da:bc:bf *");
+       bt_assert(p.type = NET_EVPN);
+       bt_assert(p.evpn_type = NET_EVPN_MAC);
+       bt_assert(p.rd = 10:1020);
+       bt_assert(p.evpn_tag = 220);
+       bt_assert(p.mac = 12:a6:54:da:bc:bf);
+       bt_assert(p.ip = ::);
+
+       p = evpn mac 10:1020 224 16:c2:8d:50:86:c5 192.0.2.10;
+       bt_assert(format(p) = "evpn mac 10:1020 224 16:c2:8d:50:86:c5 192.0.2.10");
+       bt_assert(p.type = NET_EVPN);
+       bt_assert(p.evpn_type = NET_EVPN_MAC);
+       bt_assert(p.rd = 10:1020);
+       bt_assert(p.evpn_tag = 224);
+       bt_assert(p.mac = 16:c2:8d:50:86:c5);
+       bt_assert(p.ip = 192.0.2.10);
+
+       p = evpn mac 10:1020 226 2a:3b:24:3d:f0:a0 2001:db8:10:20::1;
+       bt_assert(format(p) = "evpn mac 10:1020 226 2a:3b:24:3d:f0:a0 2001:db8:10:20::1");
+       bt_assert(p.type = NET_EVPN);
+       bt_assert(p.evpn_type = NET_EVPN_MAC);
+       bt_assert(p.rd = 10:1020);
+       bt_assert(p.evpn_tag = 226);
+       bt_assert(p.mac = 2a:3b:24:3d:f0:a0);
+       bt_assert(p.ip = 2001:db8:10:20::1);
+
+       p = evpn imet 10:1030 234 192.0.2.20;
+       bt_assert(format(p) = "evpn imet 10:1030 234 192.0.2.20");
+       bt_assert(p.type = NET_EVPN);
+       bt_assert(p.evpn_type = NET_EVPN_IMET);
+       bt_assert(p.rd = 10:1030);
+       bt_assert(p.evpn_tag = 234);
+       bt_assert(p.router_ip = 192.0.2.20);
+
+       p = evpn imet 10:1030 236 2001:db8:10:20::2;
+       bt_assert(format(p) = "evpn imet 10:1030 236 2001:db8:10:20::2");
+       bt_assert(p.type = NET_EVPN);
+       bt_assert(p.evpn_type = NET_EVPN_IMET);
+       bt_assert(p.rd = 10:1030);
+       bt_assert(p.evpn_tag = 236);
+       bt_assert(p.router_ip = 2001:db8:10:20::2);
+
+       p = evpn es 10:1040 00:10:20:30:40:50:60:70:80:90 192.0.2.40;
+       bt_assert(format(p) = "evpn es 10:1040 00:10:20:30:40:50:60:70:80:90 192.0.2.40");
+       bt_assert(p.type = NET_EVPN);
+       bt_assert(p.evpn_type = NET_EVPN_ES);
+       bt_assert(p.rd = 10:1040);
+       bt_assert(p.evpn_esi = 00:10:20:30:40:50:60:70:80:90);
+       bt_assert(p.router_ip = 192.0.2.40);
+}
+
+bt_test_suite(t_net_evpn, "Testing EVPN nets");
+
+
+
+
 /*
  *     Testing defined() function
  *     --------------------------
index e5c2a176826c1a42f0bb55f914eccae4ff39f032..738369119bf98677f8965c045fa645bd3189420d 100644 (file)
@@ -540,6 +540,9 @@ aggregator_rt_notify(struct proto *P, struct channel *src_ch, net *net, rte *new
        case T_IP:
          MX(ip);
          break;
+       case T_MAC:
+         MX(mac);
+         break;
        case T_NET:
          mem_hash_mix_num(&haux, net_hash(IT(net)));
          break;