case T_EC:
case T_LC:
case T_RD:
+ case T_MAC:
return 1;
default:
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,
| BOOL { $$ = T_BOOL; }
| IP { $$ = T_IP; }
| RD { $$ = T_RD; }
+ | MAC { $$ = T_MAC; }
| PREFIX { $$ = T_NET; }
| PAIR { $$ = T_PAIR; }
| QUAD { $$ = T_QUAD; }
case T_ENUM:
case T_EC:
case T_LC:
- case T_RD:
case T_IP:
+ case T_RD:
+ case T_MAC:
$$ = T_SET;
break;
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);
| 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 ']' {
[T_LC] = "lc",
[T_LCLIST] = "lclist",
[T_RD] = "rd",
+ [T_MAC] = "mac",
[T_ROUTE] = "route",
[T_ROUTES_BLOCK] = "block of routes",
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:
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;
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,
lcomm lc;
vpn_rd rd;
ip_addr ip;
+ mac_addr mac;
const net_addr *net;
const char *s;
const struct adata *bs;
]]);
/* 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; }));
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];
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]);
+/*
+ * 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
* --------------------------
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;