diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index dd18797637..b03b408f7d 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -2606,10 +2606,9 @@ size_t bgp_packet_mpattr_start(struct stream *s, struct peer *peer, afi_t afi, stream_putc(s, pkt_safi); /* SAFI */ /* Nexthop AFI */ - if (afi == AFI_IP && safi == SAFI_UNICAST) { + if (afi == AFI_IP + && (safi == SAFI_UNICAST || safi == SAFI_LABELED_UNICAST)) nh_afi = peer_cap_enhe(peer, afi, safi) ? AFI_IP6 : AFI_IP; - } else if (safi == SAFI_LABELED_UNICAST) - nh_afi = afi; else nh_afi = BGP_NEXTHOP_AFI_FROM_NHLEN(attr->mp_nexthop_len); @@ -2800,9 +2799,8 @@ static void bgp_packet_mpattr_tea(struct bgp *bgp, struct peer *peer, if (attrlenfield > 0xff) { /* 2-octet length field */ - stream_putc(s, - BGP_ATTR_FLAG_TRANS | BGP_ATTR_FLAG_OPTIONAL - | BGP_ATTR_FLAG_EXTLEN); + stream_putc(s, BGP_ATTR_FLAG_TRANS | BGP_ATTR_FLAG_OPTIONAL + | BGP_ATTR_FLAG_EXTLEN); stream_putc(s, attrtype); stream_putw(s, attrlenfield & 0xffff); } else { @@ -3040,15 +3038,14 @@ bgp_size_t bgp_packet_attribute(struct bgp *bgp, struct peer *peer, if (CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_SEND_COMMUNITY) && (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_COMMUNITIES))) { if (attr->community->size * 4 > 255) { - stream_putc(s, - BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS - | BGP_ATTR_FLAG_EXTLEN); + stream_putc(s, BGP_ATTR_FLAG_OPTIONAL + | BGP_ATTR_FLAG_TRANS + | BGP_ATTR_FLAG_EXTLEN); stream_putc(s, BGP_ATTR_COMMUNITIES); stream_putw(s, attr->community->size * 4); } else { - stream_putc(s, - BGP_ATTR_FLAG_OPTIONAL - | BGP_ATTR_FLAG_TRANS); + stream_putc(s, BGP_ATTR_FLAG_OPTIONAL + | BGP_ATTR_FLAG_TRANS); stream_putc(s, BGP_ATTR_COMMUNITIES); stream_putc(s, attr->community->size * 4); } @@ -3062,15 +3059,14 @@ bgp_size_t bgp_packet_attribute(struct bgp *bgp, struct peer *peer, PEER_FLAG_SEND_LARGE_COMMUNITY) && (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LARGE_COMMUNITIES))) { if (attr->lcommunity->size * 12 > 255) { - stream_putc(s, - BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS - | BGP_ATTR_FLAG_EXTLEN); + stream_putc(s, BGP_ATTR_FLAG_OPTIONAL + | BGP_ATTR_FLAG_TRANS + | BGP_ATTR_FLAG_EXTLEN); stream_putc(s, BGP_ATTR_LARGE_COMMUNITIES); stream_putw(s, attr->lcommunity->size * 12); } else { - stream_putc(s, - BGP_ATTR_FLAG_OPTIONAL - | BGP_ATTR_FLAG_TRANS); + stream_putc(s, BGP_ATTR_FLAG_OPTIONAL + | BGP_ATTR_FLAG_TRANS); stream_putc(s, BGP_ATTR_LARGE_COMMUNITIES); stream_putc(s, attr->lcommunity->size * 12); } @@ -3122,16 +3118,14 @@ bgp_size_t bgp_packet_attribute(struct bgp *bgp, struct peer *peer, if (peer->sort == BGP_PEER_IBGP || peer->sort == BGP_PEER_CONFED) { if (attr->ecommunity->size * 8 > 255) { - stream_putc(s, - BGP_ATTR_FLAG_OPTIONAL - | BGP_ATTR_FLAG_TRANS - | BGP_ATTR_FLAG_EXTLEN); + stream_putc(s, BGP_ATTR_FLAG_OPTIONAL + | BGP_ATTR_FLAG_TRANS + | BGP_ATTR_FLAG_EXTLEN); stream_putc(s, BGP_ATTR_EXT_COMMUNITIES); stream_putw(s, attr->ecommunity->size * 8); } else { - stream_putc(s, - BGP_ATTR_FLAG_OPTIONAL - | BGP_ATTR_FLAG_TRANS); + stream_putc(s, BGP_ATTR_FLAG_OPTIONAL + | BGP_ATTR_FLAG_TRANS); stream_putc(s, BGP_ATTR_EXT_COMMUNITIES); stream_putc(s, attr->ecommunity->size * 8); } @@ -3197,9 +3191,8 @@ bgp_size_t bgp_packet_attribute(struct bgp *bgp, struct peer *peer, label_index = attr->label_index; if (label_index != BGP_INVALID_LABEL_INDEX) { - stream_putc(s, - BGP_ATTR_FLAG_OPTIONAL - | BGP_ATTR_FLAG_TRANS); + stream_putc(s, BGP_ATTR_FLAG_OPTIONAL + | BGP_ATTR_FLAG_TRANS); stream_putc(s, BGP_ATTR_PREFIX_SID); stream_putc(s, 10); stream_putc(s, BGP_PREFIX_SID_LABEL_INDEX); @@ -3227,9 +3220,8 @@ bgp_size_t bgp_packet_attribute(struct bgp *bgp, struct peer *peer, */ aspath = aspath_delete_confed_seq(aspath); - stream_putc(s, - BGP_ATTR_FLAG_TRANS | BGP_ATTR_FLAG_OPTIONAL - | BGP_ATTR_FLAG_EXTLEN); + stream_putc(s, BGP_ATTR_FLAG_TRANS | BGP_ATTR_FLAG_OPTIONAL + | BGP_ATTR_FLAG_EXTLEN); stream_putc(s, BGP_ATTR_AS4_PATH); aspath_sizep = stream_get_endp(s); stream_putw(s, 0); @@ -3414,15 +3406,14 @@ void bgp_dump_routes_attr(struct stream *s, struct attr *attr, /* Community attribute. */ if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_COMMUNITIES)) { if (attr->community->size * 4 > 255) { - stream_putc(s, - BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS - | BGP_ATTR_FLAG_EXTLEN); + stream_putc(s, BGP_ATTR_FLAG_OPTIONAL + | BGP_ATTR_FLAG_TRANS + | BGP_ATTR_FLAG_EXTLEN); stream_putc(s, BGP_ATTR_COMMUNITIES); stream_putw(s, attr->community->size * 4); } else { - stream_putc(s, - BGP_ATTR_FLAG_OPTIONAL - | BGP_ATTR_FLAG_TRANS); + stream_putc(s, BGP_ATTR_FLAG_OPTIONAL + | BGP_ATTR_FLAG_TRANS); stream_putc(s, BGP_ATTR_COMMUNITIES); stream_putc(s, attr->community->size * 4); } @@ -3432,15 +3423,14 @@ void bgp_dump_routes_attr(struct stream *s, struct attr *attr, /* Large Community attribute. */ if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LARGE_COMMUNITIES)) { if (attr->lcommunity->size * 12 > 255) { - stream_putc(s, - BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS - | BGP_ATTR_FLAG_EXTLEN); + stream_putc(s, BGP_ATTR_FLAG_OPTIONAL + | BGP_ATTR_FLAG_TRANS + | BGP_ATTR_FLAG_EXTLEN); stream_putc(s, BGP_ATTR_LARGE_COMMUNITIES); stream_putw(s, attr->lcommunity->size * 12); } else { - stream_putc(s, - BGP_ATTR_FLAG_OPTIONAL - | BGP_ATTR_FLAG_TRANS); + stream_putc(s, BGP_ATTR_FLAG_OPTIONAL + | BGP_ATTR_FLAG_TRANS); stream_putc(s, BGP_ATTR_LARGE_COMMUNITIES); stream_putc(s, attr->lcommunity->size * 12); } @@ -3485,9 +3475,8 @@ void bgp_dump_routes_attr(struct stream *s, struct attr *attr, /* Prefix SID */ if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_PREFIX_SID)) { if (attr->label_index != BGP_INVALID_LABEL_INDEX) { - stream_putc(s, - BGP_ATTR_FLAG_OPTIONAL - | BGP_ATTR_FLAG_TRANS); + stream_putc(s, BGP_ATTR_FLAG_OPTIONAL + | BGP_ATTR_FLAG_TRANS); stream_putc(s, BGP_ATTR_PREFIX_SID); stream_putc(s, 10); stream_putc(s, BGP_PREFIX_SID_LABEL_INDEX); diff --git a/bgpd/bgp_attr_evpn.c b/bgpd/bgp_attr_evpn.c index 2f0b566ccf..6ead059261 100644 --- a/bgpd/bgp_attr_evpn.c +++ b/bgpd/bgp_attr_evpn.c @@ -169,7 +169,7 @@ extern int bgp_build_evpn_prefix(int evpn_type, uint32_t eth_tag, prefix_copy(src, dst); memset(dst, 0, sizeof(struct prefix)); p_evpn_p = &(dst->u.prefix_evpn); - dst->family = AF_ETHERNET; + dst->family = AF_EVPN; p_evpn_p->route_type = evpn_type; if (evpn_type == BGP_EVPN_IP_PREFIX_ROUTE) { p_evpn_p->eth_tag = eth_tag; diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index fe311832a2..0560dc46f9 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -347,9 +347,9 @@ static int bgp_zebra_send_remote_macip(struct bgp *bgp, struct bgpevpn *vpn, s = zclient->obuf; stream_reset(s); - zclient_create_header( - s, add ? ZEBRA_REMOTE_MACIP_ADD : ZEBRA_REMOTE_MACIP_DEL, - bgp->vrf_id); + zclient_create_header(s, add ? ZEBRA_REMOTE_MACIP_ADD + : ZEBRA_REMOTE_MACIP_DEL, + bgp->vrf_id); stream_putl(s, vpn->vni); stream_put(s, &p->prefix.mac.octet, ETH_ALEN); /* Mac Addr */ /* IP address length and IP address, if any. */ @@ -400,9 +400,9 @@ static int bgp_zebra_send_remote_vtep(struct bgp *bgp, struct bgpevpn *vpn, s = zclient->obuf; stream_reset(s); - zclient_create_header( - s, add ? ZEBRA_REMOTE_VTEP_ADD : ZEBRA_REMOTE_VTEP_DEL, - bgp->vrf_id); + zclient_create_header(s, add ? ZEBRA_REMOTE_VTEP_ADD + : ZEBRA_REMOTE_VTEP_DEL, + bgp->vrf_id); stream_putl(s, vpn->vni); if (IS_EVPN_PREFIX_IPADDR_V4(p)) stream_put_in_addr(s, &p->prefix.ip.ipaddr_v4); @@ -472,7 +472,7 @@ static void add_mac_mobility_to_attr(u_int32_t seq_num, struct attr *attr) { struct ecommunity ecom_tmp; struct ecommunity_val eval; - struct ecommunity *ecom_mm; + u_int8_t *ecom_val_ptr; int i; u_int8_t *pnt; int type = 0; @@ -482,7 +482,7 @@ static void add_mac_mobility_to_attr(u_int32_t seq_num, struct attr *attr) encode_mac_mobility_extcomm(0, seq_num, &eval); /* Find current MM ecommunity */ - ecom_mm = NULL; + ecom_val_ptr = NULL; if (attr->ecommunity) { for (i = 0; i < attr->ecommunity->size; i++) { @@ -493,17 +493,17 @@ static void add_mac_mobility_to_attr(u_int32_t seq_num, struct attr *attr) if (type == ECOMMUNITY_ENCODE_EVPN && sub_type == ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY) { - ecom_mm = (struct ecommunity *) - attr->ecommunity->val - + (i * 8); + ecom_val_ptr = + (u_int8_t *)(attr->ecommunity->val + + (i * 8)); break; } } } /* Update the existing MM ecommunity */ - if (ecom_mm) { - memcpy(ecom_mm->val, eval.val, sizeof(char) * ECOMMUNITY_SIZE); + if (ecom_val_ptr) { + memcpy(ecom_val_ptr, eval.val, sizeof(char) * ECOMMUNITY_SIZE); } /* Add MM to existing */ else { @@ -704,7 +704,7 @@ static int evpn_route_is_sticky(struct bgp *bgp, struct bgp_node *rn) static int update_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, afi_t afi, safi_t safi, struct bgp_node *rn, struct attr *attr, int add, int vni_table, - struct bgp_info **ri) + struct bgp_info **ri, u_char flags) { struct bgp_info *tmp_ri; struct bgp_info *local_ri, *remote_ri; @@ -751,8 +751,11 @@ static int update_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, * remote, we have to initiate appropriate MAC mobility steps. * This * is applicable when updating the VNI routing table. + * We need to skip mobility steps for g/w macs (local mac on g/w + * SVI) advertised in EVPN. + * This will ensure that local routes are preferred for g/w macs */ - if (remote_ri) { + if (remote_ri && !CHECK_FLAG(flags, ZEBRA_MAC_TYPE_GW)) { u_int32_t cur_seqnum; /* Add MM extended community to route. */ @@ -811,7 +814,7 @@ static int update_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, * and schedule for processing. */ static int update_evpn_route(struct bgp *bgp, struct bgpevpn *vpn, - struct prefix_evpn *p, u_char sticky) + struct prefix_evpn *p, u_char flags) { struct bgp_node *rn; struct attr attr; @@ -828,7 +831,7 @@ static int update_evpn_route(struct bgp *bgp, struct bgpevpn *vpn, attr.nexthop = vpn->originator_ip; attr.mp_nexthop_global_in = vpn->originator_ip; attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4; - attr.sticky = sticky; + attr.sticky = CHECK_FLAG(flags, ZEBRA_MAC_TYPE_STICKY) ? 1 : 0; /* Set up RT and ENCAP extended community. */ build_evpn_route_extcomm(vpn, &attr); @@ -839,7 +842,7 @@ static int update_evpn_route(struct bgp *bgp, struct bgpevpn *vpn, /* Create or update route entry. */ route_change = update_evpn_route_entry(bgp, vpn, afi, safi, rn, &attr, - 1, 1, &ri); + 1, 1, &ri, flags); assert(ri); attr_new = ri->attr; @@ -860,7 +863,7 @@ static int update_evpn_route(struct bgp *bgp, struct bgpevpn *vpn, rn = bgp_afi_node_get(bgp->rib[afi][safi], afi, safi, (struct prefix *)p, &vpn->prd); update_evpn_route_entry(bgp, vpn, afi, safi, rn, attr_new, 1, 0, - &global_ri); + &global_ri, flags); /* Schedule for processing and unlock node. */ bgp_process(bgp, rn, afi, safi); @@ -998,10 +1001,10 @@ static int update_all_type2_routes(struct bgp *bgp, struct bgpevpn *vpn) if (evpn_route_is_sticky(bgp, rn)) update_evpn_route_entry(bgp, vpn, afi, safi, rn, - &attr_sticky, 0, 1, &ri); + &attr_sticky, 0, 1, &ri, 0); else update_evpn_route_entry(bgp, vpn, afi, safi, rn, &attr, - 0, 1, &ri); + 0, 1, &ri, 0); /* If a local route exists for this prefix, we need to update * the global routing table too. @@ -1022,7 +1025,7 @@ static int update_all_type2_routes(struct bgp *bgp, struct bgpevpn *vpn) (struct prefix *)evp, &vpn->prd); assert(rd_rn); update_evpn_route_entry(bgp, vpn, afi, safi, rd_rn, attr_new, 0, - 0, &global_ri); + 0, &global_ri, 0); /* Schedule for processing and unlock node. */ bgp_process(bgp, rd_rn, afi, safi); @@ -1190,6 +1193,12 @@ static int handle_tunnel_ip_change(struct bgp *bgp, struct bgpevpn *vpn, { struct prefix_evpn p; + /* If VNI is not live, we only need to update the originator ip */ + if (!is_vni_live(vpn)) { + vpn->originator_ip = originator_ip; + return 0; + } + /* Need to withdraw type-3 route as the originator IP is part * of the key. */ @@ -1631,8 +1640,8 @@ static int update_advertise_vni_routes(struct bgp *bgp, struct bgpevpn *vpn) global_rn = bgp_afi_node_get(bgp->rib[afi][safi], afi, safi, (struct prefix *)&p, &vpn->prd); - update_evpn_route_entry(bgp, vpn, afi, safi, global_rn, attr, 1, 0, - &ri); + update_evpn_route_entry(bgp, vpn, afi, safi, global_rn, attr, 1, 0, &ri, + 0); /* Schedule for processing and unlock node. */ bgp_process(bgp, global_rn, afi, safi); @@ -1665,7 +1674,7 @@ static int update_advertise_vni_routes(struct bgp *bgp, struct bgpevpn *vpn) (struct prefix *)evp, &vpn->prd); assert(global_rn); update_evpn_route_entry(bgp, vpn, afi, safi, global_rn, attr, 1, - 0, &global_ri); + 0, &global_ri, 0); /* Schedule for processing and unlock node. */ bgp_process(bgp, global_rn, afi, safi); @@ -1798,7 +1807,7 @@ static int process_type2_route(struct peer *peer, afi_t afi, safi_t safi, /* Make EVPN prefix. */ memset(&p, 0, sizeof(struct prefix_evpn)); - p.family = AF_ETHERNET; + p.family = AF_EVPN; p.prefixlen = EVPN_TYPE_2_ROUTE_PREFIXLEN; p.prefix.route_type = BGP_EVPN_MAC_IP_ROUTE; @@ -1887,7 +1896,7 @@ static int process_type3_route(struct peer *peer, afi_t afi, safi_t safi, /* Make EVPN prefix. */ memset(&p, 0, sizeof(struct prefix_evpn)); - p.family = AF_ETHERNET; + p.family = AF_EVPN; p.prefixlen = EVPN_TYPE_3_ROUTE_PREFIXLEN; p.prefix.route_type = BGP_EVPN_IMET_ROUTE; @@ -1952,7 +1961,7 @@ static int process_type5_route(struct peer *peer, afi_t afi, safi_t safi, /* Make EVPN prefix. */ memset(&p, 0, sizeof(struct prefix_evpn)); - p.family = AF_ETHERNET; + p.family = AF_EVPN; p.prefix.route_type = BGP_EVPN_IP_PREFIX_ROUTE; /* Additional information outside of prefix - ESI and GW IP */ @@ -2021,7 +2030,7 @@ static void evpn_mpattr_encode_type5(struct stream *s, struct prefix *p, struct evpn_addr *p_evpn_p; memset(&temp, 0, 16); - if (p->family != AF_ETHERNET) + if (p->family != AF_EVPN) return; p_evpn_p = &(p->u.prefix_evpn); @@ -2204,7 +2213,7 @@ char *bgp_evpn_route2str(struct prefix_evpn *p, char *buf, int len) PREFIX2STR_BUFFER)); } } else { - /* Currently, this is to cater to other AF_ETHERNET code. */ + /* For EVPN route types not supported yet. */ } return (buf); @@ -2586,7 +2595,7 @@ int bgp_evpn_local_macip_del(struct bgp *bgp, vni_t vni, struct ethaddr *mac, * Handle add of a local MACIP. */ int bgp_evpn_local_macip_add(struct bgp *bgp, vni_t vni, struct ethaddr *mac, - struct ipaddr *ip, u_char sticky) + struct ipaddr *ip, u_char flags) { struct bgpevpn *vpn; struct prefix_evpn p; @@ -2606,13 +2615,15 @@ int bgp_evpn_local_macip_add(struct bgp *bgp, vni_t vni, struct ethaddr *mac, /* Create EVPN type-2 route and schedule for processing. */ build_evpn_type2_prefix(&p, mac, ip); - if (update_evpn_route(bgp, vpn, &p, sticky)) { + if (update_evpn_route(bgp, vpn, &p, flags)) { char buf[ETHER_ADDR_STRLEN]; char buf2[INET6_ADDRSTRLEN]; zlog_err( - "%u:Failed to create Type-2 route, VNI %u %sMAC %s IP %s", - bgp->vrf_id, vpn->vni, sticky ? "sticky" : "", + "%u:Failed to create Type-2 route, VNI %u %s MAC %s IP %s", + bgp->vrf_id, vpn->vni, + CHECK_FLAG(flags, ZEBRA_MAC_TYPE_STICKY) ? "sticky gateway" + : "", prefix_mac2str(mac, buf, sizeof(buf)), ipaddr2str(ip, buf2, sizeof(buf2))); return -1; @@ -2671,14 +2682,15 @@ int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni, /* Lookup VNI. If present and no change, exit. */ vpn = bgp_evpn_lookup_vni(bgp, vni); - if (vpn && is_vni_live(vpn)) { - if (IPV4_ADDR_SAME(&vpn->originator_ip, &originator_ip)) + if (vpn) { + if (is_vni_live(vpn) + && IPV4_ADDR_SAME(&vpn->originator_ip, &originator_ip)) /* Probably some other param has changed that we don't * care about. */ return 0; /* Local tunnel endpoint IP address has changed */ - return handle_tunnel_ip_change(bgp, vpn, originator_ip); + handle_tunnel_ip_change(bgp, vpn, originator_ip); } /* Create or update as appropriate. */ @@ -2692,6 +2704,10 @@ int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni, } } + /* if the VNI is live already, there is nothibng more to do */ + if (is_vni_live(vpn)) + return 0; + /* Mark as "live" */ SET_FLAG(vpn->flags, VNI_FLAG_LIVE); diff --git a/bgpd/bgp_evpn.h b/bgpd/bgp_evpn.h index f4c7e68a5d..e9b7857212 100644 --- a/bgpd/bgp_evpn.h +++ b/bgpd/bgp_evpn.h @@ -42,7 +42,7 @@ extern int bgp_evpn_local_macip_del(struct bgp *bgp, vni_t vni, struct ethaddr *mac, struct ipaddr *ip); extern int bgp_evpn_local_macip_add(struct bgp *bgp, vni_t vni, struct ethaddr *mac, struct ipaddr *ip, - u_char sticky); + u_char flags); extern int bgp_evpn_local_vni_del(struct bgp *bgp, vni_t vni); extern int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni, struct in_addr originator_ip); diff --git a/bgpd/bgp_evpn_private.h b/bgpd/bgp_evpn_private.h index 095dfa1b15..9dc459cd4e 100644 --- a/bgpd/bgp_evpn_private.h +++ b/bgpd/bgp_evpn_private.h @@ -58,6 +58,9 @@ struct bgpevpn { #define VNI_FLAG_IMPRT_CFGD 0x8 /* Import RT is user configured */ #define VNI_FLAG_EXPRT_CFGD 0x10 /* Export RT is user configured */ + /* Flag to indicate if we are advertising the g/w mac ip for this VNI*/ + u_int8_t advertise_gw_macip; + /* Id for deriving the RD automatically for this VNI */ u_int16_t rd_id; @@ -171,7 +174,7 @@ static inline void build_evpn_type2_prefix(struct prefix_evpn *p, struct ipaddr *ip) { memset(p, 0, sizeof(struct prefix_evpn)); - p->family = AF_ETHERNET; + p->family = AF_EVPN; p->prefixlen = EVPN_TYPE_2_ROUTE_PREFIXLEN; p->prefix.route_type = BGP_EVPN_MAC_IP_ROUTE; memcpy(&p->prefix.mac.octet, mac->octet, ETH_ALEN); @@ -184,7 +187,7 @@ static inline void build_evpn_type3_prefix(struct prefix_evpn *p, struct in_addr originator_ip) { memset(p, 0, sizeof(struct prefix_evpn)); - p->family = AF_ETHERNET; + p->family = AF_EVPN; p->prefixlen = EVPN_TYPE_3_ROUTE_PREFIXLEN; p->prefix.route_type = BGP_EVPN_IMET_ROUTE; p->prefix.ip.ipa_type = IPADDR_V4; diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c index 1225354c0a..948c7f50f2 100644 --- a/bgpd/bgp_evpn_vty.c +++ b/bgpd/bgp_evpn_vty.c @@ -195,6 +195,8 @@ static void display_vni(struct vty *vty, struct bgpevpn *vpn) vty_out(vty, " RD: %s\n", prefix_rd2str(&vpn->prd, buf1, RD_ADDRSTRLEN)); vty_out(vty, " Originator IP: %s\n", inet_ntoa(vpn->originator_ip)); + vty_out(vty, " Advertise-gw-macip : %s\n", + vpn->advertise_gw_macip ? "Yes" : "No"); vty_out(vty, " Import Route Target:\n"); for (ALL_LIST_ELEMENTS(vpn->import_rtl, node, nnode, ecom)) { @@ -1641,6 +1643,51 @@ static void evpn_show_all_vnis(struct vty *vty, struct bgp *bgp) vty); } +/* + * evpn - enable advertisement of default g/w + */ +static void evpn_set_advertise_default_gw(struct bgp *bgp, struct bgpevpn *vpn) +{ + if (!vpn) { + if (bgp->advertise_gw_macip) + return; + + bgp->advertise_gw_macip = 1; + bgp_zebra_advertise_gw_macip(bgp, bgp->advertise_gw_macip, 0); + } else { + if (vpn->advertise_gw_macip) + return; + + vpn->advertise_gw_macip = 1; + bgp_zebra_advertise_gw_macip(bgp, vpn->advertise_gw_macip, + vpn->vni); + } + return; +} + +/* + * evpn - disable advertisement of default g/w + */ +static void evpn_unset_advertise_default_gw(struct bgp *bgp, + struct bgpevpn *vpn) +{ + if (!vpn) { + if (!bgp->advertise_gw_macip) + return; + + bgp->advertise_gw_macip = 0; + bgp_zebra_advertise_gw_macip(bgp, bgp->advertise_gw_macip, 0); + } else { + if (!vpn->advertise_gw_macip) + return; + + vpn->advertise_gw_macip = 0; + bgp_zebra_advertise_gw_macip(bgp, vpn->advertise_gw_macip, + vpn->vni); + } + return; +} + /* * EVPN (VNI advertisement) enabled. Register with zebra. */ @@ -1700,6 +1747,9 @@ static void write_vni_config(struct vty *vty, struct bgpevpn *vpn, int *write) } } + if (vpn->advertise_gw_macip) + vty_out(vty, " advertise-default-gw\n"); + vty_out(vty, " exit-vni\n"); } } @@ -1712,6 +1762,77 @@ static void write_vni_config_for_entry(struct hash_backet *backet, } #if defined(HAVE_CUMULUS) +DEFUN (bgp_evpn_advertise_default_gw_vni, + bgp_evpn_advertise_default_gw_vni_cmd, + "advertise-default-gw", + "Advertise defualt g/w mac-ip routes in EVPN for a VNI\n") +{ + struct bgp *bgp = VTY_GET_CONTEXT(bgp); + VTY_DECLVAR_CONTEXT_SUB(bgpevpn, vpn); + + if (!bgp) + return CMD_WARNING; + + if (!vpn) + return CMD_WARNING; + + evpn_set_advertise_default_gw(bgp, vpn); + + return CMD_SUCCESS; +} + +DEFUN (no_bgp_evpn_advertise_default_vni_gw, + no_bgp_evpn_advertise_default_gw_vni_cmd, + "no advertise-default-gw", + NO_STR + "Withdraw default g/w mac-ip routes from EVPN for a VNI\n") +{ + struct bgp *bgp = VTY_GET_CONTEXT(bgp); + VTY_DECLVAR_CONTEXT_SUB(bgpevpn, vpn); + + if (!bgp) + return CMD_WARNING; + + if (!vpn) + return CMD_WARNING; + + evpn_unset_advertise_default_gw(bgp, vpn); + + return CMD_SUCCESS; +} + + +DEFUN (bgp_evpn_advertise_default_gw, + bgp_evpn_advertise_default_gw_cmd, + "advertise-default-gw", + "Advertise All defualt g/w mac-ip routes in EVPN\n") +{ + struct bgp *bgp = VTY_GET_CONTEXT(bgp); + + if (!bgp) + return CMD_WARNING; + + evpn_set_advertise_default_gw(bgp, NULL); + + return CMD_SUCCESS; +} + +DEFUN (no_bgp_evpn_advertise_default_gw, + no_bgp_evpn_advertise_default_gw_cmd, + "no advertise-default-gw", + NO_STR + "Withdraw All default g/w mac-ip routes from EVPN\n") +{ + struct bgp *bgp = VTY_GET_CONTEXT(bgp); + + if (!bgp) + return CMD_WARNING; + + evpn_unset_advertise_default_gw(bgp, NULL); + + return CMD_SUCCESS; +} + DEFUN (bgp_evpn_advertise_all_vni, bgp_evpn_advertise_all_vni_cmd, "advertise-all-vni", @@ -1739,86 +1860,95 @@ DEFUN (no_bgp_evpn_advertise_all_vni, return CMD_SUCCESS; } -DEFUN (show_bgp_evpn_vni, - show_bgp_evpn_vni_cmd, - "show bgp evpn vni", +/* + * Display VNI information - for all or a specific VNI + */ +DEFUN (show_bgp_l2vpn_evpn_vni, + show_bgp_l2vpn_evpn_vni_cmd, + "show bgp l2vpn evpn vni [(1-16777215)]", SHOW_STR BGP_STR + L2VPN_HELP_STR EVPN_HELP_STR - "Show VNI\n") -{ - struct bgp *bgp; - - bgp = bgp_get_default(); - if (!bgp) - return CMD_WARNING; - - vty_out(vty, "Advertise All VNI flag: %s\n", - bgp->advertise_all_vni ? "Enabled" : "Disabled"); - - evpn_show_all_vnis(vty, bgp); - return CMD_SUCCESS; -} - -DEFUN (show_bgp_evpn_vni_num, - show_bgp_evpn_vni_num_cmd, - "show bgp evpn vni (1-16777215)", - SHOW_STR - BGP_STR - "Address family modifier\n" "Show VNI\n" "VNI number\n") { - vni_t vni; struct bgp *bgp; + vni_t vni; + int idx = 0; bgp = bgp_get_default(); if (!bgp) return CMD_WARNING; - vni = strtoul(argv[4]->arg, NULL, 10); + if (!argv_find(argv, argc, "evpn", &idx)) + return CMD_WARNING; + + if (argc == ((idx + 1) + 1)) { + vty_out(vty, "Advertise gateway macip flag: %s\n", + bgp->advertise_gw_macip ? "Enabled" : "Disabled"); + + /* Display all VNIs */ + vty_out(vty, "Advertise All VNI flag: %s\n", + bgp->advertise_all_vni ? "Enabled" : "Disabled"); + evpn_show_all_vnis(vty, bgp); + } else { + /* Display specific VNI */ + vni = strtoul(argv[argc - 1]->arg, NULL, 10); + evpn_show_vni(vty, bgp, vni); + } - evpn_show_vni(vty, bgp, vni); return CMD_SUCCESS; } -/* `show bgp evpn summary' commands. */ -DEFUN (show_bgp_evpn_summary, - show_bgp_evpn_summary_cmd, - "show bgp evpn summary [json]", +/* + * Display EVPN neighbor summary. + */ +DEFUN (show_bgp_l2vpn_evpn_summary, + show_bgp_l2vpn_evpn_summary_cmd, + "show bgp l2vpn evpn summary [json]", SHOW_STR BGP_STR - "EVPN\n" + L2VPN_HELP_STR + EVPN_HELP_STR "Summary of BGP neighbor status\n" - "JavaScript Object Notation\n") + JSON_STR) { u_char uj = use_json(argc, argv); return bgp_show_summary_vty(vty, NULL, AFI_L2VPN, SAFI_EVPN, uj); } -/* Show bgp evpn route */ -DEFUN (show_bgp_evpn_route, - show_bgp_evpn_route_cmd, - "show bgp evpn route [type ]", +/* + * Display global EVPN routing table. + */ +DEFUN (show_bgp_l2vpn_evpn_route, + show_bgp_l2vpn_evpn_route_cmd, + "show bgp l2vpn evpn route [type ]", SHOW_STR BGP_STR - "Address Family Modifier\n" - "Display EVPN route information\n" + L2VPN_HELP_STR + EVPN_HELP_STR + "EVPN route information\n" "Specify Route type\n" "MAC-IP (Type-2) route\n" "Multicast (Type-3) route\n") { struct bgp *bgp; + int idx = 0; int type = 0; bgp = bgp_get_default(); if (!bgp) return CMD_WARNING; - if (argc == 6) { - if (strncmp(argv[5]->arg, "ma", 2) == 0) + if (!argv_find(argv, argc, "evpn", &idx)) + return CMD_WARNING; + + if (argc == ((idx + 1) + 3)) { + /* Specific type is requested */ + if (strncmp(argv[argc - 1]->arg, "ma", 2) == 0) type = BGP_EVPN_MAC_IP_ROUTE; - else if (strncmp(argv[5]->arg, "mu", 2) == 0) + else if (strncmp(argv[argc - 1]->arg, "mu", 2) == 0) type = BGP_EVPN_IMET_ROUTE; else return CMD_WARNING; @@ -1828,13 +1958,17 @@ DEFUN (show_bgp_evpn_route, return CMD_SUCCESS; } -DEFUN (show_bgp_evpn_route_rd, - show_bgp_evpn_route_rd_cmd, - "show bgp evpn route rd ASN:nn_or_IP-address:nn [type ]", +/* + * Display global EVPN routing table for specific RD. + */ +DEFUN (show_bgp_l2vpn_evpn_route_rd, + show_bgp_l2vpn_evpn_route_rd_cmd, + "show bgp l2vpn evpn route rd ASN:nn_or_IP-address:nn [type ]", SHOW_STR BGP_STR - "Address Family Modifier\n" - "Display EVPN route information\n" + L2VPN_HELP_STR + EVPN_HELP_STR + "EVPN route information\n" "Route Distinguisher\n" "ASN:XX or A.B.C.D:XX\n" "Specify Route type\n" @@ -1844,22 +1978,27 @@ DEFUN (show_bgp_evpn_route_rd, struct bgp *bgp; int ret; struct prefix_rd prd; + int idx = 0; int type = 0; bgp = bgp_get_default(); if (!bgp) return CMD_WARNING; - ret = str2prefix_rd(argv[5]->arg, &prd); + if (!argv_find(argv, argc, "evpn", &idx)) + return CMD_WARNING; + + ret = str2prefix_rd(argv[idx + 3]->arg, &prd); if (!ret) { vty_out(vty, "%% Malformed Route Distinguisher\n"); return CMD_WARNING; } - if (argc == 8) { - if (strncmp(argv[7]->arg, "ma", 2) == 0) + if (argc == ((idx + 1) + 5)) { + /* Specific type is requested */ + if (strncmp(argv[argc - 1]->arg, "ma", 2) == 0) type = BGP_EVPN_MAC_IP_ROUTE; - else if (strncmp(argv[7]->arg, "mu", 2) == 0) + else if (strncmp(argv[argc - 1]->arg, "mu", 2) == 0) type = BGP_EVPN_IMET_ROUTE; else return CMD_WARNING; @@ -1869,13 +2008,17 @@ DEFUN (show_bgp_evpn_route_rd, return CMD_SUCCESS; } -DEFUN (show_bgp_evpn_route_rd_macip, - show_bgp_evpn_route_rd_macip_cmd, - "show bgp evpn route rd ASN:nn_or_IP-address:nn mac WORD [ip WORD]", +/* + * Display global EVPN routing table for specific RD and MACIP. + */ +DEFUN (show_bgp_l2vpn_evpn_route_rd_macip, + show_bgp_l2vpn_evpn_route_rd_macip_cmd, + "show bgp l2vpn evpn route rd ASN:nn_or_IP-address:nn mac WORD [ip WORD]", SHOW_STR BGP_STR - "Address Family Modifier\n" - "Display EVPN route information\n" + L2VPN_HELP_STR + EVPN_HELP_STR + "EVPN route information\n" "Route Distinguisher\n" "ASN:XX or A.B.C.D:XX\n" "MAC\n" @@ -1888,23 +2031,28 @@ DEFUN (show_bgp_evpn_route_rd_macip, struct prefix_rd prd; struct ethaddr mac; struct ipaddr ip; + int idx = 0; bgp = bgp_get_default(); if (!bgp) return CMD_WARNING; - ret = str2prefix_rd(argv[5]->arg, &prd); + if (!argv_find(argv, argc, "evpn", &idx)) + return CMD_WARNING; + + ret = str2prefix_rd(argv[idx + 3]->arg, &prd); if (!ret) { vty_out(vty, "%% Malformed Route Distinguisher\n"); return CMD_WARNING; } - if (!prefix_str2mac(argv[7]->arg, &mac)) { + if (!prefix_str2mac(argv[idx + 5]->arg, &mac)) { vty_out(vty, "%% Malformed MAC address\n"); return CMD_WARNING; } memset(&ip, 0, sizeof(ip)); - if (argc == 10 && argv[9]->arg != NULL) { - if (str2ipaddr(argv[9]->arg, &ip) != 0) { + if (argc == (idx + 1 + 7) && argv[argc - 1]->arg != NULL) { + /* Specific MAC+IP requested */ + if (str2ipaddr(argv[argc - 1]->arg, &ip) != 0) { vty_out(vty, "%% Malformed IP address\n"); return CMD_WARNING; } @@ -1914,13 +2062,17 @@ DEFUN (show_bgp_evpn_route_rd_macip, return CMD_SUCCESS; } -DEFUN (show_bgp_evpn_route_vni, - show_bgp_evpn_route_vni_cmd, - "show bgp evpn route vni (1-16777215) [ | vtep A.B.C.D>]", +/* + * Display per-VNI EVPN routing table. + */ +DEFUN (show_bgp_l2vpn_evpn_route_vni, + show_bgp_l2vpn_evpn_route_vni_cmd, + "show bgp l2vpn evpn route vni (1-16777215) [ | vtep A.B.C.D>]", SHOW_STR BGP_STR - "Address Family Modifier\n" - "Display EVPN route information\n" + L2VPN_HELP_STR + EVPN_HELP_STR + "EVPN route information\n" "VXLAN Network Identifier\n" "VNI number\n" "Specify Route type\n" @@ -1933,25 +2085,29 @@ DEFUN (show_bgp_evpn_route_vni, struct bgp *bgp; struct in_addr vtep_ip; int type = 0; + int idx = 0; bgp = bgp_get_default(); if (!bgp) return CMD_WARNING; + if (!argv_find(argv, argc, "evpn", &idx)) + return CMD_WARNING; + vtep_ip.s_addr = 0; - vni = strtoul(argv[5]->arg, NULL, 10); + vni = strtoul(argv[idx + 3]->arg, NULL, 10); - if (argc == 8 && argv[6]->arg) { - if (strncmp(argv[6]->arg, "type", 4) == 0) { - if (strncmp(argv[7]->arg, "ma", 2) == 0) + if (argc == (idx + 1 + 5) && argv[idx + 4]->arg) { + if (strncmp(argv[idx + 4]->arg, "type", 4) == 0) { + if (strncmp(argv[idx + 5]->arg, "ma", 2) == 0) type = BGP_EVPN_MAC_IP_ROUTE; - else if (strncmp(argv[7]->arg, "mu", 2) == 0) + else if (strncmp(argv[idx + 5]->arg, "mu", 2) == 0) type = BGP_EVPN_IMET_ROUTE; else return CMD_WARNING; - } else if (strncmp(argv[6]->arg, "vtep", 4) == 0) { - if (!inet_aton(argv[7]->arg, &vtep_ip)) { + } else if (strncmp(argv[idx + 4]->arg, "vtep", 4) == 0) { + if (!inet_aton(argv[idx + 5]->arg, &vtep_ip)) { vty_out(vty, "%% Malformed VTEP IP address\n"); return CMD_WARNING; } @@ -1963,13 +2119,17 @@ DEFUN (show_bgp_evpn_route_vni, return CMD_SUCCESS; } -DEFUN (show_bgp_evpn_route_vni_macip, - show_bgp_evpn_route_vni_macip_cmd, - "show bgp evpn route vni (1-16777215) mac WORD [ip WORD]", +/* + * Display per-VNI EVPN routing table for specific MACIP. + */ +DEFUN (show_bgp_l2vpn_evpn_route_vni_macip, + show_bgp_l2vpn_evpn_route_vni_macip_cmd, + "show bgp l2vpn evpn route vni (1-16777215) mac WORD [ip WORD]", SHOW_STR BGP_STR - "Address Family Modifier\n" - "Display EVPN route information\n" + L2VPN_HELP_STR + EVPN_HELP_STR + "EVPN route information\n" "VXLAN Network Identifier\n" "VNI number\n" "MAC\n" @@ -1981,19 +2141,23 @@ DEFUN (show_bgp_evpn_route_vni_macip, struct bgp *bgp; struct ethaddr mac; struct ipaddr ip; + int idx = 0; bgp = bgp_get_default(); if (!bgp) return CMD_WARNING; - vni = strtoul(argv[5]->arg, NULL, 10); - if (!prefix_str2mac(argv[7]->arg, &mac)) { + if (!argv_find(argv, argc, "evpn", &idx)) + return CMD_WARNING; + + vni = strtoul(argv[idx + 3]->arg, NULL, 10); + if (!prefix_str2mac(argv[idx + 5]->arg, &mac)) { vty_out(vty, "%% Malformed MAC address\n"); return CMD_WARNING; } memset(&ip, 0, sizeof(ip)); - if (argc == 10 && argv[9]->arg != NULL) { - if (str2ipaddr(argv[9]->arg, &ip) != 0) { + if (argc == (idx + 1 + 7) && argv[idx + 7]->arg != NULL) { + if (str2ipaddr(argv[idx + 7]->arg, &ip) != 0) { vty_out(vty, "%% Malformed IP address\n"); return CMD_WARNING; } @@ -2003,13 +2167,17 @@ DEFUN (show_bgp_evpn_route_vni_macip, return CMD_SUCCESS; } -DEFUN (show_bgp_evpn_route_vni_multicast, - show_bgp_evpn_route_vni_multicast_cmd, - "show bgp evpn route vni (1-16777215) multicast A.B.C.D", +/* + * Display per-VNI EVPN routing table for specific multicast IP (remote VTEP). + */ +DEFUN (show_bgp_l2vpn_evpn_route_vni_multicast, + show_bgp_l2vpn_evpn_route_vni_multicast_cmd, + "show bgp l2vpn evpn route vni (1-16777215) multicast A.B.C.D", SHOW_STR BGP_STR - "Address Family Modifier\n" - "Display EVPN route information\n" + L2VPN_HELP_STR + EVPN_HELP_STR + "EVPN route information\n" "VXLAN Network Identifier\n" "VNI number\n" "Multicast (Type-3) route\n" @@ -2019,13 +2187,17 @@ DEFUN (show_bgp_evpn_route_vni_multicast, struct bgp *bgp; int ret; struct in_addr orig_ip; + int idx = 0; bgp = bgp_get_default(); if (!bgp) return CMD_WARNING; - vni = strtoul(argv[5]->arg, NULL, 10); - ret = inet_aton(argv[7]->arg, &orig_ip); + if (!argv_find(argv, argc, "evpn", &idx)) + return CMD_WARNING; + + vni = strtoul(argv[idx + 3]->arg, NULL, 10); + ret = inet_aton(argv[idx + 5]->arg, &orig_ip); if (!ret) { vty_out(vty, "%% Malformed Originating Router IP address\n"); return CMD_WARNING; @@ -2035,13 +2207,17 @@ DEFUN (show_bgp_evpn_route_vni_multicast, return CMD_SUCCESS; } -DEFUN (show_bgp_evpn_route_vni_all, - show_bgp_evpn_route_vni_all_cmd, - "show bgp evpn route vni all [vtep A.B.C.D]", +/* + * Display per-VNI EVPN routing table - for all VNIs. + */ +DEFUN (show_bgp_l2vpn_evpn_route_vni_all, + show_bgp_l2vpn_evpn_route_vni_all_cmd, + "show bgp l2vpn evpn route vni all [vtep A.B.C.D]", SHOW_STR BGP_STR - "Address Family Modifier\n" - "Display EVPN route information\n" + L2VPN_HELP_STR + EVPN_HELP_STR + "EVPN route information\n" "VXLAN Network Identifier\n" "All VNIs\n" "Remote VTEP\n" @@ -2049,14 +2225,18 @@ DEFUN (show_bgp_evpn_route_vni_all, { struct bgp *bgp; struct in_addr vtep_ip; + int idx = 0; bgp = bgp_get_default(); if (!bgp) return CMD_WARNING; + if (!argv_find(argv, argc, "evpn", &idx)) + return CMD_WARNING; + vtep_ip.s_addr = 0; - if (argc == 8 && argv[7]->arg) { - if (!inet_aton(argv[7]->arg, &vtep_ip)) { + if (argc == (idx + 1 + 5) && argv[idx + 5]->arg) { + if (!inet_aton(argv[idx + 5]->arg, &vtep_ip)) { vty_out(vty, "%% Malformed VTEP IP address\n"); return CMD_WARNING; } @@ -2066,12 +2246,16 @@ DEFUN (show_bgp_evpn_route_vni_all, return CMD_SUCCESS; } -DEFUN (show_bgp_evpn_import_rt, - show_bgp_evpn_import_rt_cmd, - "show bgp evpn import-rt", +/* + * Display EVPN import route-target hash table + */ +DEFUN (show_bgp_l2vpn_evpn_import_rt, + show_bgp_l2vpn_evpn_import_rt_cmd, + "show bgp l2vpn evpn import-rt", SHOW_STR BGP_STR - "Address family modifier\n" + L2VPN_HELP_STR + EVPN_HELP_STR "Show import route target\n") { struct bgp *bgp; @@ -2084,6 +2268,97 @@ DEFUN (show_bgp_evpn_import_rt, return CMD_SUCCESS; } +#if defined(HAVE_CUMULUS) +ALIAS_HIDDEN(show_bgp_l2vpn_evpn_vni, show_bgp_evpn_vni_cmd, + "show bgp evpn vni [(1-16777215)]", SHOW_STR BGP_STR EVPN_HELP_STR + "Show VNI\n" + "VNI number\n") + +ALIAS_HIDDEN(show_bgp_l2vpn_evpn_summary, show_bgp_evpn_summary_cmd, + "show bgp evpn summary [json]", SHOW_STR BGP_STR EVPN_HELP_STR + "Summary of BGP neighbor status\n" + JSON_STR) + +ALIAS_HIDDEN(show_bgp_l2vpn_evpn_route, show_bgp_evpn_route_cmd, + "show bgp evpn route [type ]", + SHOW_STR BGP_STR EVPN_HELP_STR + "EVPN route information\n" + "Specify Route type\n" + "MAC-IP (Type-2) route\n" + "Multicast (Type-3) route\n") + +ALIAS_HIDDEN( + show_bgp_l2vpn_evpn_route_rd, show_bgp_evpn_route_rd_cmd, + "show bgp evpn route rd ASN:nn_or_IP-address:nn [type ]", + SHOW_STR BGP_STR EVPN_HELP_STR + "EVPN route information\n" + "Route Distinguisher\n" + "ASN:XX or A.B.C.D:XX\n" + "Specify Route type\n" + "MAC-IP (Type-2) route\n" + "Multicast (Type-3) route\n") + +ALIAS_HIDDEN( + show_bgp_l2vpn_evpn_route_rd_macip, show_bgp_evpn_route_rd_macip_cmd, + "show bgp evpn route rd ASN:nn_or_IP-address:nn mac WORD [ip WORD]", + SHOW_STR BGP_STR EVPN_HELP_STR + "EVPN route information\n" + "Route Distinguisher\n" + "ASN:XX or A.B.C.D:XX\n" + "MAC\n" + "MAC address (e.g., 00:e0:ec:20:12:62)\n" + "IP\n" + "IP address (IPv4 or IPv6)\n") + +ALIAS_HIDDEN( + show_bgp_l2vpn_evpn_route_vni, show_bgp_evpn_route_vni_cmd, + "show bgp evpn route vni (1-16777215) [ | vtep A.B.C.D>]", + SHOW_STR BGP_STR EVPN_HELP_STR + "EVPN route information\n" + "VXLAN Network Identifier\n" + "VNI number\n" + "Specify Route type\n" + "MAC-IP (Type-2) route\n" + "Multicast (Type-3) route\n" + "Remote VTEP\n" + "Remote VTEP IP address\n") + +ALIAS_HIDDEN(show_bgp_l2vpn_evpn_route_vni_macip, + show_bgp_evpn_route_vni_macip_cmd, + "show bgp evpn route vni (1-16777215) mac WORD [ip WORD]", + SHOW_STR BGP_STR EVPN_HELP_STR + "EVPN route information\n" + "VXLAN Network Identifier\n" + "VNI number\n" + "MAC\n" + "MAC address (e.g., 00:e0:ec:20:12:62)\n" + "IP\n" + "IP address (IPv4 or IPv6)\n") + +ALIAS_HIDDEN(show_bgp_l2vpn_evpn_route_vni_multicast, + show_bgp_evpn_route_vni_multicast_cmd, + "show bgp evpn route vni (1-16777215) multicast A.B.C.D", + SHOW_STR BGP_STR EVPN_HELP_STR + "EVPN route information\n" + "VXLAN Network Identifier\n" + "VNI number\n" + "Multicast (Type-3) route\n" + "Originating Router IP address\n") + +ALIAS_HIDDEN(show_bgp_l2vpn_evpn_route_vni_all, show_bgp_evpn_route_vni_all_cmd, + "show bgp evpn route vni all [vtep A.B.C.D]", + SHOW_STR BGP_STR EVPN_HELP_STR + "EVPN route information\n" + "VXLAN Network Identifier\n" + "All VNIs\n" + "Remote VTEP\n" + "Remote VTEP IP address\n") + +ALIAS_HIDDEN(show_bgp_l2vpn_evpn_import_rt, show_bgp_evpn_import_rt_cmd, + "show bgp evpn import-rt", + SHOW_STR BGP_STR EVPN_HELP_STR "Show import route target\n") +#endif + DEFUN_NOSH (bgp_evpn_vni, bgp_evpn_vni_cmd, "vni (1-16777215)", @@ -2290,11 +2565,11 @@ DEFUN (bgp_evpn_vni_rt, if (rt_type == RT_TYPE_BOTH || rt_type == RT_TYPE_IMPORT) { ecomadd = ecommunity_str2com(argv[2]->arg, ECOMMUNITY_ROUTE_TARGET, 0); - ecommunity_str(ecomadd); if (!ecomadd) { vty_out(vty, "%% Malformed Route Target list\n"); return CMD_WARNING; } + ecommunity_str(ecomadd); /* Do nothing if we already have this import route-target */ if (!bgp_evpn_rt_matches_existing(vpn->import_rtl, ecomadd)) @@ -2305,11 +2580,11 @@ DEFUN (bgp_evpn_vni_rt, if (rt_type == RT_TYPE_BOTH || rt_type == RT_TYPE_EXPORT) { ecomadd = ecommunity_str2com(argv[2]->arg, ECOMMUNITY_ROUTE_TARGET, 0); - ecommunity_str(ecomadd); if (!ecomadd) { vty_out(vty, "%% Malformed Route Target list\n"); return CMD_WARNING; } + ecommunity_str(ecomadd); /* Do nothing if we already have this export route-target */ if (!bgp_evpn_rt_matches_existing(vpn->export_rtl, ecomadd)) @@ -2372,11 +2647,11 @@ DEFUN (no_bgp_evpn_vni_rt, } ecomdel = ecommunity_str2com(argv[3]->arg, ECOMMUNITY_ROUTE_TARGET, 0); - ecommunity_str(ecomdel); if (!ecomdel) { vty_out(vty, "%% Malformed Route Target list\n"); return CMD_WARNING; } + ecommunity_str(ecomdel); if (rt_type == RT_TYPE_IMPORT) { if (!bgp_evpn_rt_matches_existing(vpn->import_rtl, ecomdel)) { @@ -2484,6 +2759,11 @@ void bgp_config_write_evpn_info(struct vty *vty, struct bgp *bgp, afi_t afi, bgp_config_write_family_header(vty, afi, safi, write); vty_out(vty, " advertise-all-vni\n"); } + + if (bgp->advertise_gw_macip) { + bgp_config_write_family_header(vty, afi, safi, write); + vty_out(vty, " advertise-default-gw\n"); + } } void bgp_ethernetvpn_init(void) @@ -2509,10 +2789,24 @@ void bgp_ethernetvpn_init(void) #if defined(HAVE_CUMULUS) install_element(BGP_EVPN_NODE, &bgp_evpn_advertise_all_vni_cmd); install_element(BGP_EVPN_NODE, &no_bgp_evpn_advertise_all_vni_cmd); + install_element(BGP_EVPN_NODE, &bgp_evpn_advertise_default_gw_cmd); + install_element(BGP_EVPN_NODE, &no_bgp_evpn_advertise_default_gw_cmd); + + /* "show bgp l2vpn evpn" commands. */ + install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_vni_cmd); + install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_summary_cmd); + install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_cmd); + install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_rd_cmd); + install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_rd_macip_cmd); + install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_vni_cmd); + install_element(VIEW_NODE, + &show_bgp_l2vpn_evpn_route_vni_multicast_cmd); + install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_vni_macip_cmd); + install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_vni_all_cmd); + install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_import_rt_cmd); /* "show bgp evpn" commands. */ install_element(VIEW_NODE, &show_bgp_evpn_vni_cmd); - install_element(VIEW_NODE, &show_bgp_evpn_vni_num_cmd); install_element(VIEW_NODE, &show_bgp_evpn_summary_cmd); install_element(VIEW_NODE, &show_bgp_evpn_route_cmd); install_element(VIEW_NODE, &show_bgp_evpn_route_rd_cmd); @@ -2532,5 +2826,9 @@ void bgp_ethernetvpn_init(void) install_element(BGP_EVPN_VNI_NODE, &bgp_evpn_vni_rt_cmd); install_element(BGP_EVPN_VNI_NODE, &no_bgp_evpn_vni_rt_cmd); install_element(BGP_EVPN_VNI_NODE, &no_bgp_evpn_vni_rt_without_val_cmd); + install_element(BGP_EVPN_VNI_NODE, + &bgp_evpn_advertise_default_gw_vni_cmd); + install_element(BGP_EVPN_VNI_NODE, + &no_bgp_evpn_advertise_default_gw_vni_cmd); #endif } diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index e4e421510f..35f793f861 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -1580,10 +1580,18 @@ int subgroup_announce_check(struct bgp_node *rn, struct bgp_info *ri, /* Route map & unsuppress-map apply. */ if (ROUTE_MAP_OUT_NAME(filter) || (ri->extra && ri->extra->suppress)) { struct bgp_info info; + struct bgp_info_extra dummy_info_extra; struct attr dummy_attr; info.peer = peer; info.attr = attr; + + if (ri->extra) { + memcpy(&dummy_info_extra, ri->extra, + sizeof(struct bgp_info_extra)); + info.extra = &dummy_info_extra; + } + /* don't confuse inbound and outbound setting */ RESET_FLAG(attr->rmap_change_flags); @@ -6238,6 +6246,9 @@ static void route_vty_out_route(struct prefix *p, struct vty *vty) } else len += vty_out(vty, "/%d", p->prefixlen); } else if (p->family == AF_ETHERNET) { + prefix2str(p, buf, PREFIX_STRLEN); + len = vty_out(vty, "%s", buf); + } else if (p->family == AF_EVPN) { #if defined(HAVE_CUMULUS) len = vty_out(vty, "%s", bgp_evpn_route2str((struct prefix_evpn *)p, buf, @@ -6505,15 +6516,14 @@ void route_vty_out(struct vty *vty, struct prefix *p, struct bgp_info *binfo, len = vty_out( vty, "%s", binfo->peer->conf_if); - len = - 7 - len; /* len of IPv6 - addr + max - len of def - ifname */ + len = 16 - len; /* len of IPv6 + addr + max + len of def + ifname */ if (len < 1) vty_out(vty, "\n%*s", - 45, " "); + 36, " "); else vty_out(vty, "%*s", len, " "); @@ -6801,7 +6811,7 @@ void route_vty_out_tag(struct vty *vty, struct prefix *p, if (attr) { if (((p->family == AF_INET) && ((safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP))) - || (safi == SAFI_EVPN && p->family == AF_ETHERNET + || (safi == SAFI_EVPN && !BGP_ATTR_NEXTHOP_AFI_IP6(attr)) || (!BGP_ATTR_NEXTHOP_AFI_IP6(attr))) { if (safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP @@ -6826,7 +6836,7 @@ void route_vty_out_tag(struct vty *vty, struct prefix *p, } } else if (((p->family == AF_INET6) && ((safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP))) - || (safi == SAFI_EVPN && p->family == AF_ETHERNET + || (safi == SAFI_EVPN && BGP_ATTR_NEXTHOP_AFI_IP6(attr)) || (BGP_ATTR_NEXTHOP_AFI_IP6(attr))) { char buf_a[BUFSIZ]; @@ -7326,7 +7336,8 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct prefix *p, /* Line2 display Next-hop, Neighbor, Router-id */ /* Display the nexthop */ - if ((p->family == AF_INET || p->family == AF_ETHERNET) + if ((p->family == AF_INET || p->family == AF_ETHERNET || + p->family == AF_EVPN) && (safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP || safi == SAFI_EVPN || !BGP_ATTR_NEXTHOP_AFI_IP6(attr))) { @@ -10232,6 +10243,10 @@ static int bgp_show_neighbor_route(struct vty *vty, struct peer *peer, afi_t afi, safi_t safi, enum bgp_show_type type, u_char use_json) { + /* labeled-unicast routes live in the unicast table */ + if (safi == SAFI_LABELED_UNICAST) + safi = SAFI_UNICAST; + if (!peer || !peer->afc[afi][safi]) { if (use_json) { json_object *json_no = NULL; diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index 285bb9a80c..a8e111d361 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -54,6 +54,9 @@ #include "bgpd/bgp_lcommunity.h" #include "bgpd/bgp_vty.h" #include "bgpd/bgp_debug.h" +#include "bgpd/bgp_evpn.h" +#include "bgpd/bgp_evpn_private.h" +#include "bgpd/bgp_evpn_vty.h" #if ENABLE_BGP_VNC #include "bgpd/rfapi/bgp_rfapi_cfg.h" @@ -572,6 +575,106 @@ struct route_map_rule_cmd route_match_ip_route_source_prefix_list_cmd = { route_match_ip_route_source_prefix_list_compile, route_match_ip_route_source_prefix_list_free}; +/* `match mac address MAC_ACCESS_LIST' */ + +/* Match function should return 1 if match is success else return + zero. */ +static route_map_result_t route_match_mac_address(void *rule, + struct prefix *prefix, + route_map_object_t type, + void *object) +{ + struct access_list *alist; + struct prefix p; + + if (type == RMAP_BGP) { + alist = access_list_lookup(AFI_L2VPN, (char *)rule); + if (alist == NULL) + return RMAP_NOMATCH; + + if (prefix->u.prefix_evpn.route_type != BGP_EVPN_MAC_IP_ROUTE) + return RMAP_NOMATCH; + + p.family = AF_ETHERNET; + p.prefixlen = ETH_ALEN * 8; + p.u.prefix_eth = prefix->u.prefix_evpn.mac; + + return (access_list_apply(alist, &p) + == FILTER_DENY + ? RMAP_NOMATCH + : RMAP_MATCH); + } + + return RMAP_NOMATCH; +} + +/* Route map `mac address' match statement. `arg' should be + access-list name. */ +static void *route_match_mac_address_compile(const char *arg) +{ + return XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg); +} + +/* Free route map's compiled `ip address' value. */ +static void route_match_mac_address_free(void *rule) +{ + XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); +} + +/* Route map commands for mac address matching. */ +struct route_map_rule_cmd route_match_mac_address_cmd = { + "mac address", route_match_mac_address, route_match_mac_address_compile, + route_match_mac_address_free}; + +/* `match vni' */ + +/* Match function should return 1 if match is success else return + zero. */ +static route_map_result_t route_match_vni(void *rule, struct prefix *prefix, + route_map_object_t type, void *object) +{ + vni_t vni = 0; + struct bgp_info *bgp_info = NULL; + + if (type == RMAP_BGP) { + vni = *((vni_t *)rule); + bgp_info = (struct bgp_info *)object; + + if (vni == label2vni(&bgp_info->extra->label)) + return RMAP_MATCH; + } + + return RMAP_NOMATCH; +} + +/* Route map `vni' match statement. */ +static void *route_match_vni_compile(const char *arg) +{ + vni_t *vni = NULL; + char *end = NULL; + + vni = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(vni_t)); + if (!vni) + return NULL; + + *vni = strtoul(arg, &end, 10); + if (*end != '\0') + return NULL; + + return vni; +} + +/* Free route map's compiled `vni' value. */ +static void route_match_vni_free(void *rule) +{ + XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); +} + +/* Route map commands for vni matching. */ +struct route_map_rule_cmd route_match_evpn_vni_cmd = { + "evpn vni", route_match_vni, route_match_vni_compile, + route_match_vni_free}; + /* `match local-preference LOCAL-PREF' */ /* Match function return 1 if match is success else return zero. */ @@ -2994,6 +3097,55 @@ static void bgp_route_map_event(route_map_event_t event, const char *rmap_name) route_map_notify_dependencies(rmap_name, RMAP_EVENT_MATCH_ADDED); } +DEFUN (match_mac_address, + match_mac_address_cmd, + "match mac address WORD", + MATCH_STR + "mac address\n" + "Match address of route\n" + "MAC Access-list name\n") +{ + return bgp_route_match_add(vty, "mac address", argv[3]->arg, + RMAP_EVENT_FILTER_ADDED); +} + +DEFUN (no_match_mac_address, + no_match_mac_address_cmd, + "no match mac address WORD", + NO_STR + MATCH_STR + "mac\n" + "Match address of route\n" + "MAC acess-list name\n") +{ + return bgp_route_match_delete(vty, "mac address", argv[4]->arg, + RMAP_EVENT_FILTER_DELETED); +} + +DEFUN (match_evpn_vni, + match_evpn_vni_cmd, + "match evpn vni (1-16777215)", + MATCH_STR + EVPN_HELP_STR + "Match VNI\n" + "VNI ID\n") +{ + return bgp_route_match_add(vty, "evpn vni", argv[3]->arg, + RMAP_EVENT_MATCH_ADDED); +} + +DEFUN (no_match_evpn_vni, + no_match_evpn_vni_cmd, + "no match evpn vni (1-16777215)", + NO_STR + MATCH_STR + EVPN_HELP_STR + "Match VNI\n" + "VNI ID\n") +{ + return bgp_route_match_delete(vty, "evpn vni", argv[4]->arg, + RMAP_EVENT_MATCH_DELETED); +} DEFUN (match_peer, match_peer_cmd, @@ -4351,6 +4503,8 @@ void bgp_route_map_init(void) route_map_install_match(&route_match_probability_cmd); route_map_install_match(&route_match_interface_cmd); route_map_install_match(&route_match_tag_cmd); + route_map_install_match(&route_match_mac_address_cmd); + route_map_install_match(&route_match_evpn_vni_cmd); route_map_install_set(&route_set_ip_nexthop_cmd); route_map_install_set(&route_set_local_pref_cmd); @@ -4381,6 +4535,10 @@ void bgp_route_map_init(void) install_element(RMAP_NODE, &no_match_ip_route_source_cmd); install_element(RMAP_NODE, &match_ip_route_source_prefix_list_cmd); install_element(RMAP_NODE, &no_match_ip_route_source_prefix_list_cmd); + install_element(RMAP_NODE, &match_mac_address_cmd); + install_element(RMAP_NODE, &no_match_mac_address_cmd); + install_element(RMAP_NODE, &match_evpn_vni_cmd); + install_element(RMAP_NODE, &no_match_evpn_vni_cmd); install_element(RMAP_NODE, &match_aspath_cmd); install_element(RMAP_NODE, &no_match_aspath_cmd); diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 0220a7e55d..01c27920f5 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -12047,6 +12047,8 @@ void bgp_vty_init(void) install_element(BGP_VPNV4_NODE, &no_neighbor_route_map_cmd); install_element(BGP_VPNV6_NODE, &neighbor_route_map_cmd); install_element(BGP_VPNV6_NODE, &no_neighbor_route_map_cmd); + install_element(BGP_EVPN_NODE, &neighbor_route_map_cmd); + install_element(BGP_EVPN_NODE, &no_neighbor_route_map_cmd); /* "neighbor unsuppress-map" commands. */ install_element(BGP_NODE, &neighbor_unsuppress_map_hidden_cmd); diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index 5071be909e..2fc75ea5a2 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -2034,6 +2034,29 @@ void bgp_zebra_terminate_radv(struct bgp *bgp, struct peer *peer) zclient_send_interface_radv_req(zclient, bgp->vrf_id, peer->ifp, 0, 0); } +int bgp_zebra_advertise_gw_macip(struct bgp *bgp, int advertise, vni_t vni) +{ + struct stream *s = NULL; + + /* Check socket. */ + if (!zclient || zclient->sock < 0) + return 0; + + /* Don't try to register if Zebra doesn't know of this instance. */ + if (!IS_BGP_INST_KNOWN_TO_ZEBRA(bgp)) + return 0; + + s = zclient->obuf; + stream_reset(s); + + zclient_create_header(s, ZEBRA_ADVERTISE_DEFAULT_GW, bgp->vrf_id); + stream_putc(s, advertise); + stream_put3(s, vni); + stream_putw_at(s, 0, stream_get_endp(s)); + + return zclient_send_message(zclient); +} + int bgp_zebra_advertise_all_vni(struct bgp *bgp, int advertise) { struct stream *s; @@ -2120,7 +2143,7 @@ static int bgp_zebra_process_local_macip(int command, struct zclient *zclient, int ipa_len; char buf[ETHER_ADDR_STRLEN]; char buf1[INET6_ADDRSTRLEN]; - u_char sticky; + u_char flags; memset(&ip, 0, sizeof(ip)); s = zclient->ibuf; @@ -2140,21 +2163,20 @@ static int bgp_zebra_process_local_macip(int command, struct zclient *zclient, (ipa_len == IPV4_MAX_BYTELEN) ? IPADDR_V4 : IPADDR_V6; stream_get(&ip.ip.addr, s, ipa_len); } - sticky = stream_getc(s); + flags = stream_getc(s); bgp = bgp_lookup_by_vrf_id(vrf_id); if (!bgp) return 0; if (BGP_DEBUG(zebra, ZEBRA)) - zlog_debug("%u:Recv MACIP %s %sMAC %s IP %s VNI %u", vrf_id, - (command == ZEBRA_MACIP_ADD) ? "Add" : "Del", - sticky ? "sticky " : "", - prefix_mac2str(&mac, buf, sizeof(buf)), + zlog_debug("%u:Recv MACIP %s flags 0x%x MAC %s IP %s VNI %u", + vrf_id, (command == ZEBRA_MACIP_ADD) ? "Add" : "Del", + flags, prefix_mac2str(&mac, buf, sizeof(buf)), ipaddr2str(&ip, buf1, sizeof(buf1)), vni); if (command == ZEBRA_MACIP_ADD) - return bgp_evpn_local_macip_add(bgp, vni, &mac, &ip, sticky); + return bgp_evpn_local_macip_add(bgp, vni, &mac, &ip, flags); else return bgp_evpn_local_macip_del(bgp, vni, &mac, &ip); } diff --git a/bgpd/bgp_zebra.h b/bgpd/bgp_zebra.h index 8e55eb6d8d..11405d1c1b 100644 --- a/bgpd/bgp_zebra.h +++ b/bgpd/bgp_zebra.h @@ -21,6 +21,8 @@ #ifndef _QUAGGA_BGP_ZEBRA_H #define _QUAGGA_BGP_ZEBRA_H +#include "vxlan.h" + extern void bgp_zebra_init(struct thread_master *master); extern void bgp_zebra_destroy(void); extern int bgp_if_update_all(void); @@ -57,6 +59,7 @@ extern struct interface *if_lookup_by_ipv6(struct in6_addr *, ifindex_t, extern struct interface *if_lookup_by_ipv6_exact(struct in6_addr *, ifindex_t, vrf_id_t); +extern int bgp_zebra_advertise_gw_macip(struct bgp *, int, vni_t); extern int bgp_zebra_advertise_all_vni(struct bgp *, int); extern int bgp_zebra_num_connects(void); diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 208a4e4b4e..bfdddc69b1 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -380,6 +380,9 @@ struct bgp { /* EVI hash table */ struct hash *vnihash; + /* EVPN enable - advertise gateway macip routes */ + int advertise_gw_macip; + /* EVPN enable - advertise local VNIs and their MACs etc. */ int advertise_all_vni; @@ -1488,7 +1491,8 @@ static inline int peer_group_af_configured(struct peer_group *group) || peer->afc[AFI_IP6][SAFI_MULTICAST] || peer->afc[AFI_IP6][SAFI_LABELED_UNICAST] || peer->afc[AFI_IP6][SAFI_MPLS_VPN] - || peer->afc[AFI_IP6][SAFI_ENCAP] || peer->afc[AFI_IP6][SAFI_EVPN]) + || peer->afc[AFI_IP6][SAFI_ENCAP] + || peer->afc[AFI_L2VPN][SAFI_EVPN]) return 1; return 0; } diff --git a/ldpd/ldp_vty_cmds.c b/ldpd/ldp_vty_cmds.c index 3f39ad926c..db92b93628 100644 --- a/ldpd/ldp_vty_cmds.c +++ b/ldpd/ldp_vty_cmds.c @@ -21,6 +21,8 @@ #include "command.h" #include "vty.h" +#include "json.h" + #include "ldpd/ldpd.h" #include "ldpd/ldp_vty.h" #include "ldpd/ldp_vty_cmds_clippy.c" @@ -586,7 +588,7 @@ DEFPY (ldp_show_mpls_ldp_binding, "IPv6 Address Family\n" "Label Information Base (LIB) information\n" "Show detailed information\n" - "JavaScript Object Notation\n") + JSON_STR) { return (ldp_vty_show_binding(vty, af, detail, json)); } @@ -601,7 +603,7 @@ DEFPY (ldp_show_mpls_ldp_discovery, "IPv6 Address Family\n" "Discovery Hello Information\n" "Show detailed information\n" - "JavaScript Object Notation\n") + JSON_STR) { return (ldp_vty_show_discovery(vty, af, detail, json)); } @@ -615,7 +617,7 @@ DEFPY (ldp_show_mpls_ldp_interface, "IPv4 Address Family\n" "IPv6 Address Family\n" "interface information\n" - "JavaScript Object Notation\n") + JSON_STR) { return (ldp_vty_show_interface(vty, af, json)); } @@ -627,7 +629,7 @@ DEFPY (ldp_show_mpls_ldp_capabilities, "MPLS information\n" "Label Distribution Protocol\n" "Display LDP Capabilities information\n" - "JavaScript Object Notation\n") + JSON_STR) { return (ldp_vty_show_capabilities(vty, json)); } @@ -640,7 +642,7 @@ DEFPY (ldp_show_mpls_ldp_neighbor, "Label Distribution Protocol\n" "Neighbor information\n" "Show detailed information\n" - "JavaScript Object Notation\n") + JSON_STR) { return (ldp_vty_show_neighbor(vty, 0, detail, json)); } @@ -653,7 +655,7 @@ DEFPY (ldp_show_mpls_ldp_neighbor_capabilities, "Label Distribution Protocol\n" "Neighbor information\n" "Display neighbor capability information\n" - "JavaScript Object Notation\n") + JSON_STR) { return (ldp_vty_show_neighbor(vty, 1, NULL, json)); } @@ -665,7 +667,7 @@ DEFPY (ldp_show_l2vpn_atom_binding, "Show information about Layer2 VPN\n" "Show Any Transport over MPLS information\n" "Show AToM label binding information\n" - "JavaScript Object Notation\n") + JSON_STR) { return (ldp_vty_show_atom_binding(vty, json)); } @@ -677,7 +679,7 @@ DEFPY (ldp_show_l2vpn_atom_vc, "Show information about Layer2 VPN\n" "Show Any Transport over MPLS information\n" "Show AToM virtual circuit information\n" - "JavaScript Object Notation\n") + JSON_STR) { return (ldp_vty_show_atom_vc(vty, json)); } diff --git a/lib/command.c b/lib/command.c index 09ffa6ce56..077a72398d 100644 --- a/lib/command.c +++ b/lib/command.c @@ -101,6 +101,7 @@ const char *node_names[] = { "ipv4 access list", // ACCESS_NODE, "ipv4 prefix list", // PREFIX_NODE, "ipv6 access list", // ACCESS_IPV6_NODE, + "MAC access list", // ACCESS_MAC_NODE, "ipv6 prefix list", // PREFIX_IPV6_NODE, "as list", // AS_LIST_NODE, "community list", // COMMUNITY_LIST_NODE, diff --git a/lib/command.h b/lib/command.h index d0c9f0eaf9..8f12e2aabd 100644 --- a/lib/command.h +++ b/lib/command.h @@ -123,6 +123,7 @@ enum node_type { ACCESS_NODE, /* Access list node. */ PREFIX_NODE, /* Prefix list node. */ ACCESS_IPV6_NODE, /* Access list node. */ + ACCESS_MAC_NODE, /* MAC access list node*/ PREFIX_IPV6_NODE, /* Prefix list node. */ AS_LIST_NODE, /* AS list node. */ COMMUNITY_LIST_NODE, /* Community list node. */ diff --git a/lib/filter.c b/lib/filter.c index 0262234c78..cb6f743c01 100644 --- a/lib/filter.c +++ b/lib/filter.c @@ -90,6 +90,14 @@ struct access_master { void (*delete_hook)(struct access_list *); }; +/* Static structure for mac access_list's master. */ +static struct access_master access_master_mac = { + {NULL, NULL}, + {NULL, NULL}, + NULL, + NULL, +}; + /* Static structure for IPv4 access_list's master. */ static struct access_master access_master_ipv4 = { {NULL, NULL}, @@ -112,6 +120,8 @@ static struct access_master *access_master_get(afi_t afi) return &access_master_ipv4; else if (afi == AFI_IP6) return &access_master_ipv6; + else if (afi == AFI_L2VPN) + return &access_master_mac; return NULL; } @@ -173,7 +183,7 @@ static int filter_match_cisco(struct filter *mfilter, struct prefix *p) /* If filter match to the prefix then return 1. */ static int filter_match_zebra(struct filter *mfilter, struct prefix *p) { - struct filter_zebra *filter; + struct filter_zebra *filter = NULL; filter = &mfilter->u.zfilter; @@ -365,9 +375,7 @@ static struct access_list *access_list_get(afi_t afi, const char *name) enum filter_type access_list_apply(struct access_list *access, void *object) { struct filter *filter; - struct prefix *p; - - p = (struct prefix *)object; + struct prefix *p = (struct prefix *)object; if (access == NULL) return FILTER_DENY; @@ -390,6 +398,7 @@ void access_list_add_hook(void (*func)(struct access_list *access)) { access_master_ipv4.add_hook = func; access_master_ipv6.add_hook = func; + access_master_mac.add_hook = func; } /* Delete hook function. */ @@ -397,6 +406,7 @@ void access_list_delete_hook(void (*func)(struct access_list *access)) { access_master_ipv4.delete_hook = func; access_master_ipv6.delete_hook = func; + access_master_mac.delete_hook = func; } /* Add new filter to the end of specified access_list. */ @@ -515,10 +525,10 @@ static struct filter *filter_lookup_zebra(struct access_list *access, filter = &mfilter->u.zfilter; if (filter->exact == new->exact - && mfilter->type - == mnew->type &&prefix_same(&filter->prefix, - &new->prefix)) - return mfilter; + && mfilter->type == mnew->type) { + if (prefix_same(&filter->prefix, &new->prefix)) + return mfilter; + } } return NULL; } @@ -1252,6 +1262,12 @@ static int filter_set_zebra(struct vty *vty, const char *name_str, "IPv6 address prefix/prefixlen is malformed\n"); return CMD_WARNING_CONFIG_FAILED; } + } else if (afi == AFI_L2VPN) { + ret = str2prefix_eth(prefix_str, (struct prefix_eth *)&p); + if (ret <= 0) { + vty_out(vty, "MAC address is malformed\n"); + return CMD_WARNING; + } } else return CMD_WARNING_CONFIG_FAILED; @@ -1274,7 +1290,6 @@ static int filter_set_zebra(struct vty *vty, const char *name_str, access_list_filter_add(access, mfilter); } else { struct filter *delete_filter; - delete_filter = filter_lookup_zebra(access, mfilter); if (delete_filter) access_list_filter_delete(access, delete_filter); @@ -1285,6 +1300,64 @@ static int filter_set_zebra(struct vty *vty, const char *name_str, return CMD_SUCCESS; } +DEFUN (mac_access_list, + mac_access_list_cmd, + "mac access-list WORD MAC", + "Add a mac access-list\n" + "Add an access list entry\n" + "MAC zebra access-list name\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "MAC address to match. e.g. 00:01:00:01:00:01\n") +{ + return filter_set_zebra(vty, argv[2]->arg, argv[3]->arg, AFI_L2VPN, + argv[4]->arg, 0, 1); +} + +DEFUN (no_mac_access_list, + no_mac_access_list_cmd, + "no mac access-list WORD MAC", + NO_STR + "Remove a mac access-list\n" + "Remove an access list entry\n" + "MAC zebra access-list name\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "MAC address to match. e.g. 00:01:00:01:00:01\n") +{ + return filter_set_zebra(vty, argv[3]->arg, argv[4]->arg, AFI_L2VPN, + argv[5]->arg, 0, 0); +} + +DEFUN (mac_access_list_any, + mac_access_list_any_cmd, + "mac access-list WORD any", + "Add a mac access-list\n" + "Add an access list entry\n" + "MAC zebra access-list name\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "MAC address to match. e.g. 00:01:00:01:00:01\n") +{ + return filter_set_zebra(vty, argv[2]->arg, argv[3]->arg, AFI_L2VPN, + "00:00:00:00:00:00", 0, 1); +} + +DEFUN (no_mac_access_list_any, + no_mac_access_list_any_cmd, + "no mac access-list WORD any", + NO_STR + "Remove a mac access-list\n" + "Remove an access list entry\n" + "MAC zebra access-list name\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "MAC address to match. e.g. 00:01:00:01:00:01\n") +{ + return filter_set_zebra(vty, argv[3]->arg, argv[4]->arg, AFI_L2VPN, + "00:00:00:00:00:00", 0, 0); +} + DEFUN (access_list_exact, access_list_exact_cmd, "access-list WORD A.B.C.D/M [exact-match]", @@ -1666,12 +1739,15 @@ static int filter_show(struct vty *vty, const char *name, afi_t afi) filter = &mfilter->u.cfilter; if (write) { - vty_out(vty, "%s IP%s access list %s\n", + vty_out(vty, "%s %s access list %s\n", mfilter->cisco ? (filter->extended ? "Extended" : "Standard") : "Zebra", - afi == AFI_IP6 ? "v6" : "", + (afi == AFI_IP) + ? ("IP") + : ((afi == AFI_IP6) ? ("IPv6 ") + : ("MAC ")), access->name); write = 0; } @@ -1710,12 +1786,15 @@ static int filter_show(struct vty *vty, const char *name, afi_t afi) filter = &mfilter->u.cfilter; if (write) { - vty_out(vty, "%s IP%s access list %s\n", + vty_out(vty, "%s %s access list %s\n", mfilter->cisco ? (filter->extended ? "Extended" : "Standard") : "Zebra", - afi == AFI_IP6 ? "v6" : "", + (afi == AFI_IP) + ? ("IP") + : ((afi == AFI_IP6) ? ("IPv6 ") + : ("MAC ")), access->name); write = 0; } @@ -1746,6 +1825,28 @@ static int filter_show(struct vty *vty, const char *name, afi_t afi) return CMD_SUCCESS; } +/* show MAC access list - this only has MAC filters for now*/ +DEFUN (show_mac_access_list, + show_mac_access_list_cmd, + "show mac access-list", + SHOW_STR + "mac access lists\n" + "List mac access lists\n") +{ + return filter_show(vty, NULL, AFI_L2VPN); +} + +DEFUN (show_mac_access_list_name, + show_mac_access_list_name_cmd, + "show mac access-list WORD", + SHOW_STR + "mac access lists\n" + "List mac access lists\n" + "mac address\n") +{ + return filter_show(vty, argv[3]->arg, AFI_L2VPN); +} + DEFUN (show_ip_access_list, show_ip_access_list_cmd, "show ip access-list", @@ -1844,10 +1945,17 @@ void config_write_access_zebra(struct vty *vty, struct filter *mfilter) if (p->prefixlen == 0 && !filter->exact) vty_out(vty, " any"); - else + else if (p->family == AF_INET6 || p->family == AF_INET) vty_out(vty, " %s/%d%s", inet_ntop(p->family, &p->u.prefix, buf, BUFSIZ), p->prefixlen, filter->exact ? " exact-match" : ""); + else if (p->family == AF_ETHERNET) { + if (p->prefixlen == 0) + vty_out(vty, " any"); + else + vty_out(vty, " %s", prefix_mac2str(&(p->u.prefix_eth), + buf, sizeof(buf))); + } vty_out(vty, "\n"); } @@ -1866,15 +1974,19 @@ static int config_write_access(struct vty *vty, afi_t afi) for (access = master->num.head; access; access = access->next) { if (access->remark) { vty_out(vty, "%saccess-list %s remark %s\n", - afi == AFI_IP ? "" : "ipv6 ", access->name, - access->remark); + (afi == AFI_IP) ? ("") + : ((afi == AFI_IP6) ? ("ipv6 ") + : ("mac ")), + access->name, access->remark); write++; } for (mfilter = access->head; mfilter; mfilter = mfilter->next) { vty_out(vty, "%saccess-list %s %s", - afi == AFI_IP ? "" : "ipv6 ", access->name, - filter_type_str(mfilter)); + (afi == AFI_IP) ? ("") + : ((afi == AFI_IP6) ? ("ipv6 ") + : ("mac ")), + access->name, filter_type_str(mfilter)); if (mfilter->cisco) config_write_access_cisco(vty, mfilter); @@ -1888,15 +2000,19 @@ static int config_write_access(struct vty *vty, afi_t afi) for (access = master->str.head; access; access = access->next) { if (access->remark) { vty_out(vty, "%saccess-list %s remark %s\n", - afi == AFI_IP ? "" : "ipv6 ", access->name, - access->remark); + (afi == AFI_IP) ? ("") + : ((afi == AFI_IP6) ? ("ipv6 ") + : ("mac ")), + access->name, access->remark); write++; } for (mfilter = access->head; mfilter; mfilter = mfilter->next) { vty_out(vty, "%saccess-list %s %s", - afi == AFI_IP ? "" : "ipv6 ", access->name, - filter_type_str(mfilter)); + (afi == AFI_IP) ? ("") + : ((afi == AFI_IP6) ? ("ipv6 ") + : ("mac ")), + access->name, filter_type_str(mfilter)); if (mfilter->cisco) config_write_access_cisco(vty, mfilter); @@ -1909,6 +2025,56 @@ static int config_write_access(struct vty *vty, afi_t afi) return write; } +static struct cmd_node access_mac_node = { + ACCESS_MAC_NODE, "", /* Access list has no interface. */ + 1}; + +static int config_write_access_mac(struct vty *vty) +{ + return config_write_access(vty, AFI_L2VPN); +} + +static void access_list_reset_mac(void) +{ + struct access_list *access; + struct access_list *next; + struct access_master *master; + + master = access_master_get(AFI_L2VPN); + if (master == NULL) + return; + + for (access = master->num.head; access; access = next) { + next = access->next; + access_list_delete(access); + } + for (access = master->str.head; access; access = next) { + next = access->next; + access_list_delete(access); + } + + assert(master->num.head == NULL); + assert(master->num.tail == NULL); + + assert(master->str.head == NULL); + assert(master->str.tail == NULL); +} + +/* Install vty related command. */ +static void access_list_init_mac(void) +{ + install_node(&access_mac_node, config_write_access_mac); + + install_element(ENABLE_NODE, &show_mac_access_list_cmd); + install_element(ENABLE_NODE, &show_mac_access_list_name_cmd); + + /* Zebra access-list */ + install_element(CONFIG_NODE, &mac_access_list_cmd); + install_element(CONFIG_NODE, &no_mac_access_list_cmd); + install_element(CONFIG_NODE, &mac_access_list_any_cmd); + install_element(CONFIG_NODE, &no_mac_access_list_any_cmd); +} + /* Access-list node. */ static struct cmd_node access_node = {ACCESS_NODE, "", /* Access list has no interface. */ @@ -2050,10 +2216,12 @@ void access_list_init() { access_list_init_ipv4(); access_list_init_ipv6(); + access_list_init_mac(); } void access_list_reset() { access_list_reset_ipv4(); access_list_reset_ipv6(); + access_list_reset_mac(); } diff --git a/lib/log.c b/lib/log.c index 49351a0656..5c89e7080e 100644 --- a/lib/log.c +++ b/lib/log.c @@ -917,6 +917,7 @@ static const struct zebra_desc_table command_types[] = { DESC_ENTRY(ZEBRA_GET_LABEL_CHUNK), DESC_ENTRY(ZEBRA_RELEASE_LABEL_CHUNK), DESC_ENTRY(ZEBRA_ADVERTISE_ALL_VNI), + DESC_ENTRY(ZEBRA_ADVERTISE_DEFAULT_GW), DESC_ENTRY(ZEBRA_VNI_ADD), DESC_ENTRY(ZEBRA_VNI_DEL), DESC_ENTRY(ZEBRA_REMOTE_VTEP_ADD), diff --git a/lib/prefix.c b/lib/prefix.c index 33b6ff1987..de521b2e3e 100644 --- a/lib/prefix.c +++ b/lib/prefix.c @@ -37,390 +37,262 @@ static const u_char maskbit[] = {0x00, 0x80, 0xc0, 0xe0, 0xf0, static const struct in6_addr maskbytes6[] = { /* /0 */ {{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /1 */ - {{{0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /2 */ - {{{0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /3 */ - {{{0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /4 */ - {{{0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /5 */ - {{{0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /6 */ - {{{0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /7 */ - {{{0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /8 */ - {{{0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /9 */ - {{{0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /10 */ - {{{0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /11 */ - {{{0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /12 */ - {{{0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /13 */ - {{{0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /14 */ - {{{0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /15 */ - {{{0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /16 */ - {{{0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /17 */ - {{{0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /18 */ - {{{0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /19 */ - {{{0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /20 */ - {{{0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /21 */ - {{{0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /22 */ - {{{0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /23 */ - {{{0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /24 */ - {{{0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /25 */ - {{{0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /26 */ - {{{0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /27 */ - {{{0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /28 */ - {{{0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /29 */ - {{{0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /30 */ - {{{0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /31 */ - {{{0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /32 */ - {{{0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /33 */ - {{{0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /34 */ - {{{0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /35 */ - {{{0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /36 */ - {{{0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /37 */ - {{{0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /38 */ - {{{0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /39 */ - {{{0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /40 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /41 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /42 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /43 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /44 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /45 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /46 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /47 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /48 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /49 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /50 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /51 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /52 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /53 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /54 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /55 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /56 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /57 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /58 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /59 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /60 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /61 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /62 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /63 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /64 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /65 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /66 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /67 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /68 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /69 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /70 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /71 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /72 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /73 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /74 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /75 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /76 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /77 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /78 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /79 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /80 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /81 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /82 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /83 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /84 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /85 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /86 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /87 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /88 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /89 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0x80, 0x00, 0x00, 0x00, 0x00}}}, - /* /90 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xc0, 0x00, 0x00, 0x00, 0x00}}}, - /* /91 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xe0, 0x00, 0x00, 0x00, 0x00}}}, - /* /92 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xf0, 0x00, 0x00, 0x00, 0x00}}}, - /* /93 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xf8, 0x00, 0x00, 0x00, 0x00}}}, - /* /94 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xfc, 0x00, 0x00, 0x00, 0x00}}}, - /* /95 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xfe, 0x00, 0x00, 0x00, 0x00}}}, - /* /96 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0x00, 0x00, 0x00, 0x00}}}, - /* /97 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0x80, 0x00, 0x00, 0x00}}}, - /* /98 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xc0, 0x00, 0x00, 0x00}}}, - /* /99 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xe0, 0x00, 0x00, 0x00}}}, - /* /100 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xf0, 0x00, 0x00, 0x00}}}, - /* /101 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xf8, 0x00, 0x00, 0x00}}}, - /* /102 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xfc, 0x00, 0x00, 0x00}}}, - /* /103 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xfe, 0x00, 0x00, 0x00}}}, - /* /104 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0x00, 0x00, 0x00}}}, - /* /105 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0x80, 0x00, 0x00}}}, - /* /106 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xc0, 0x00, 0x00}}}, - /* /107 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xe0, 0x00, 0x00}}}, - /* /108 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xf0, 0x00, 0x00}}}, - /* /109 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xf8, 0x00, 0x00}}}, - /* /110 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xfc, 0x00, 0x00}}}, - /* /111 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xfe, 0x00, 0x00}}}, - /* /112 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0x00, 0x00}}}, - /* /113 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0x80, 0x00}}}, - /* /114 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xc0, 0x00}}}, - /* /115 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xe0, 0x00}}}, - /* /116 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xf0, 0x00}}}, - /* /117 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xf8, 0x00}}}, - /* /118 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xfc, 0x00}}}, - /* /119 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xfe, 0x00}}}, - /* /120 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0x00}}}, - /* /121 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0x80}}}, - /* /122 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xc0}}}, - /* /123 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xe0}}}, - /* /124 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xf0}}}, - /* /125 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xf8}}}, - /* /126 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xfc}}}, - /* /127 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xfe}}}, - /* /128 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff}}}}; + /* /1 */ {{{0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /2 */ {{{0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /3 */ {{{0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /4 */ {{{0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /5 */ {{{0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /6 */ {{{0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /7 */ {{{0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /8 */ {{{0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /9 */ {{{0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /10 */ {{{0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /11 */ {{{0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /12 */ {{{0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /13 */ {{{0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /14 */ {{{0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /15 */ {{{0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /16 */ {{{0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /17 */ {{{0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /18 */ {{{0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /19 */ {{{0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /20 */ {{{0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /21 */ {{{0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /22 */ {{{0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /23 */ {{{0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /24 */ {{{0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /25 */ {{{0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /26 */ {{{0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /27 */ {{{0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /28 */ {{{0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /29 */ {{{0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /30 */ {{{0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /31 */ {{{0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /32 */ {{{0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /33 */ {{{0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /34 */ {{{0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /35 */ {{{0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /36 */ {{{0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /37 */ {{{0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /38 */ {{{0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /39 */ {{{0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /40 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /41 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /42 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /43 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /44 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /45 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /46 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /47 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /48 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /49 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /50 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /51 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /52 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /53 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /54 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /55 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /56 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /57 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /58 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /59 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /60 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /61 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /62 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /63 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /64 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /65 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /66 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /67 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /68 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /69 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /70 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /71 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /72 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /73 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /74 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /75 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /76 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /77 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /78 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /79 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /80 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /81 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /82 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /83 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /84 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /85 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /86 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /87 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /88 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* /89 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00}}}, + /* /90 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00}}}, + /* /91 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00}}}, + /* /92 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00}}}, + /* /93 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00}}}, + /* /94 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00}}}, + /* /95 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00}}}, + /* /96 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00}}}, + /* /97 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00}}}, + /* /98 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00}}}, + /* /99 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00}}}, + /* /100 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00}}}, + /* /101 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00}}}, + /* /102 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00}}}, + /* /103 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00}}}, + /* /104 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00}}}, + /* /105 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00}}}, + /* /106 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00}}}, + /* /107 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00}}}, + /* /108 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00}}}, + /* /109 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00}}}, + /* /110 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00}}}, + /* /111 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00}}}, + /* /112 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00}}}, + /* /113 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00}}}, + /* /114 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00}}}, + /* /115 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00}}}, + /* /116 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00}}}, + /* /117 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00}}}, + /* /118 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00}}}, + /* /119 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00}}}, + /* /120 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00}}}, + /* /121 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80}}}, + /* /122 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0}}}, + /* /123 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0}}}, + /* /124 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0}}}, + /* /125 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8}}}, + /* /126 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc}}}, + /* /127 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe}}}, + /* /128 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}}}; /* Number of bits in prefix type. */ #ifndef PNBBY @@ -429,6 +301,18 @@ static const struct in6_addr maskbytes6[] = { #define MASKBIT(offset) ((0xff << (PNBBY - (offset))) & 0xff) +static int is_zero_mac(const struct ethaddr *mac) +{ + int i = 0; + + for (i = 0; i < ETH_ALEN; i++) { + if (mac->octet[i]) + return 0; + } + + return 1; +} + unsigned int prefix_bit(const u_char *prefix, const u_char prefixlen) { unsigned int offset = prefixlen / 8; @@ -450,6 +334,8 @@ int str2family(const char *string) return AF_INET6; else if (!strcmp("ethernet", string)) return AF_ETHERNET; + else if (!strcmp("evpn", string)) + return AF_EVPN; return -1; } @@ -462,6 +348,7 @@ int afi2family(afi_t afi) return AF_INET6; else if (afi == AFI_L2VPN) return AF_ETHERNET; + /* NOTE: EVPN code should NOT use this interface. */ return 0; } @@ -471,7 +358,7 @@ afi_t family2afi(int family) return AFI_IP; else if (family == AF_INET6) return AFI_IP6; - else if (family == AF_ETHERNET) + else if (family == AF_ETHERNET || family == AF_EVPN) return AFI_L2VPN; return 0; } @@ -577,6 +464,9 @@ void prefix_copy(struct prefix *dest, const struct prefix *src) else if (src->family == AF_INET6) dest->u.prefix6 = src->u.prefix6; else if (src->family == AF_ETHERNET) { + memcpy(&dest->u.prefix_eth, &src->u.prefix_eth, + sizeof(struct ethaddr)); + } else if (src->family == AF_EVPN) { memcpy(&dest->u.prefix_evpn, &src->u.prefix_evpn, sizeof(struct evpn_addr)); } else if (src->family == AF_UNSPEC) { @@ -615,6 +505,10 @@ int prefix_same(const struct prefix *p1, const struct prefix *p2) &p2->u.prefix6.s6_addr)) return 1; if (p1->family == AF_ETHERNET) + if (!memcmp(&p1->u.prefix_eth, &p2->u.prefix_eth, + sizeof(struct ethaddr))) + return 1; + if (p1->family == AF_EVPN) if (!memcmp(&p1->u.prefix_evpn, &p2->u.prefix_evpn, sizeof(struct evpn_addr))) return 1; @@ -679,6 +573,8 @@ int prefix_common_bits(const struct prefix *p1, const struct prefix *p2) if (p1->family == AF_INET6) length = IPV6_MAX_BYTELEN; if (p1->family == AF_ETHERNET) + length = ETH_ALEN; + if (p1->family == AF_EVPN) length = 8 * sizeof(struct evpn_addr); if (p1->family != p2->family || !length) @@ -707,6 +603,8 @@ const char *prefix_family_str(const struct prefix *p) return "inet6"; if (p->family == AF_ETHERNET) return "ether"; + if (p->family == AF_EVPN) + return "evpn"; return "unspec"; } @@ -783,6 +681,13 @@ int str2prefix_eth(const char *str, struct prefix_eth *p) const char *str_addr = str; unsigned int a[6]; int i; + bool slash = false; + + if (!strcmp(str, "any")) { + memset(p, 0, sizeof(*p)); + p->family = AF_ETHERNET; + return 1; + } /* Find slash inside string. */ pnt = strchr(str, '/'); @@ -800,6 +705,7 @@ int str2prefix_eth(const char *str, struct prefix_eth *p) *(cp + (pnt - str)) = '\0'; str_addr = cp; + slash = true; } /* Convert string to prefix. */ @@ -814,6 +720,15 @@ int str2prefix_eth(const char *str, struct prefix_eth *p) } p->prefixlen = plen; p->family = AF_ETHERNET; + + /* + * special case to allow old configurations to work + * Since all zero's is implicitly meant to allow + * a comparison to zero, let's assume + */ + if (!slash && is_zero_mac(&(p->eth_addr))) + p->prefixlen = 0; + ret = 1; done: @@ -1063,6 +978,7 @@ int prefix_blen(const struct prefix *p) break; case AF_ETHERNET: return ETH_ALEN; + break; } return 0; } @@ -1090,7 +1006,7 @@ int str2prefix(const char *str, struct prefix *p) return 0; } -static const char *prefixeth2str(const struct prefix *p, char *str, int size) +static const char *prefixevpn2str(const struct prefix *p, char *str, int size) { u_char family; char buf[PREFIX2STR_BUFFER]; @@ -1134,12 +1050,8 @@ static const char *prefixeth2str(const struct prefix *p, char *str, int size) PREFIX2STR_BUFFER), p->prefixlen); } else { - sprintf(str, "UNK AF_ETHER prefix"); - snprintf(str, size, "%02x:%02x:%02x:%02x:%02x:%02x/%d", - p->u.prefix_eth.octet[0], p->u.prefix_eth.octet[1], - p->u.prefix_eth.octet[2], p->u.prefix_eth.octet[3], - p->u.prefix_eth.octet[4], p->u.prefix_eth.octet[5], - p->prefixlen); + sprintf(str, "Unsupported EVPN route type %d", + p->u.prefix_evpn.route_type); } return str; @@ -1159,7 +1071,13 @@ const char *prefix2str(union prefixconstptr pu, char *str, int size) break; case AF_ETHERNET: - prefixeth2str(p, str, size); + snprintf(str, size, "%s/%d", + prefix_mac2str(&p->u.prefix_eth, buf, sizeof(buf)), + p->prefixlen); + break; + + case AF_EVPN: + prefixevpn2str(p, str, size); break; default: diff --git a/lib/prefix.h b/lib/prefix.h index 5f2b57ccce..f0644ea88e 100644 --- a/lib/prefix.h +++ b/lib/prefix.h @@ -116,7 +116,18 @@ struct evpn_addr { #endif #endif -/* IPv4 and IPv6 unified prefix structure. */ +/* The 'family' in the prefix structure is internal to FRR and need not + * map to standard OS AF_ definitions except where needed for interacting + * with the kernel. However, AF_ definitions are currently in use and + * prevalent across the code. Define a new FRR-specific AF for EVPN to + * distinguish between 'ethernet' (MAC-only) and 'evpn' prefixes and + * ensure it does not conflict with any OS AF_ definition. + */ +#if !defined(AF_EVPN) +#define AF_EVPN (AF_MAX + 1) +#endif + +/* FRR generic prefix structure. */ struct prefix { u_char family; u_char prefixlen; @@ -131,7 +142,7 @@ struct prefix { struct ethaddr prefix_eth; /* AF_ETHERNET */ u_char val[8]; uintptr_t ptr; - struct evpn_addr prefix_evpn; + struct evpn_addr prefix_evpn; /* AF_EVPN */ } u __attribute__((aligned(8))); }; @@ -356,6 +367,7 @@ static inline int ipv6_martian(struct in6_addr *addr) } extern int all_digit(const char *); +extern int macstr2prefix_evpn(const char *str, struct prefix_evpn *p); /* NOTE: This routine expects the address argument in network byte order. */ static inline int ipv4_martian(struct in_addr *addr) diff --git a/lib/zclient.h b/lib/zclient.h index 5edb56f517..15d1858d84 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -111,6 +111,7 @@ typedef enum { ZEBRA_FEC_REGISTER, ZEBRA_FEC_UNREGISTER, ZEBRA_FEC_UPDATE, + ZEBRA_ADVERTISE_DEFAULT_GW, ZEBRA_ADVERTISE_ALL_VNI, ZEBRA_VNI_ADD, ZEBRA_VNI_DEL, @@ -305,6 +306,10 @@ struct zapi_pw_status { uint32_t status; }; +/* Zebra MAC types */ +#define ZEBRA_MAC_TYPE_STICKY 0x01 /* Sticky MAC*/ +#define ZEBRA_MAC_TYPE_GW 0x02 /* gateway (SVI) mac*/ + /* Prototypes of zebra client service functions. */ extern struct zclient *zclient_new(struct thread_master *); extern void zclient_init(struct zclient *, int, u_short); diff --git a/tools/frr-reload.py b/tools/frr-reload.py index bfdc08ab4c..43496d4cbf 100755 --- a/tools/frr-reload.py +++ b/tools/frr-reload.py @@ -432,6 +432,8 @@ end ctx_keys.append("address-family ipv6 unicast") elif line == "address-family ipv4": ctx_keys.append("address-family ipv4 unicast") + elif line == "address-family evpn": + ctx_keys.append("address-family l2vpn evpn") else: ctx_keys.append(line) @@ -745,6 +747,37 @@ def ignore_delete_re_add_lines(lines_to_add, lines_to_del): lines_to_del_to_del.append((ctx_keys, None)) lines_to_add_to_del.append(((tmpline,), None)) + if (len(ctx_keys) == 3 and + ctx_keys[0].startswith('router bgp') and + ctx_keys[1] == 'address-family l2vpn evpn' and + ctx_keys[2].startswith('vni')): + + re_route_target = re.search('^route-target import (.*)$', line) if line is not None else False + + if re_route_target: + rt = re_route_target.group(1).strip() + route_target_import_line = line + route_target_export_line = "route-target export %s" % rt + route_target_both_line = "route-target both %s" % rt + + found_route_target_export_line = line_exist(lines_to_del, ctx_keys, route_target_export_line) + found_route_target_both_line = line_exist(lines_to_add, ctx_keys, route_target_both_line) + + ''' + If the running configs has + route-target import 1:1 + route-target export 1:1 + + and the config we are reloading against has + route-target both 1:1 + + then we can ignore deleting the import/export and ignore adding the 'both' + ''' + if found_route_target_export_line and found_route_target_both_line: + lines_to_del_to_del.append((ctx_keys, route_target_import_line)) + lines_to_del_to_del.append((ctx_keys, route_target_export_line)) + lines_to_add_to_del.append((ctx_keys, route_target_both_line)) + if not deleted: found_add_line = line_exist(lines_to_add, ctx_keys, line) @@ -822,6 +855,13 @@ def compare_context_objects(newconf, running): elif "router bgp" in running_ctx_keys[0] and len(running_ctx_keys) > 1 and delete_bgpd: continue + # Delete an entire vni sub-context under "address-family l2vpn evpn" + elif ("router bgp" in running_ctx_keys[0] and + len(running_ctx_keys) > 2 and + running_ctx_keys[1].startswith('address-family l2vpn evpn') and + running_ctx_keys[2].startswith('vni ')): + lines_to_del.append((running_ctx_keys, None)) + elif ("router bgp" in running_ctx_keys[0] and len(running_ctx_keys) > 1 and running_ctx_keys[1].startswith('address-family')): diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index 76c42173da..6255726c47 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -1164,10 +1164,10 @@ DEFUNSH(VTYSH_BGPD, address_family_evpn, address_family_evpn_cmd, } #if defined(HAVE_CUMULUS) -DEFUNSH(VTYSH_BGPD, address_family_evpn2, address_family_evpn2_cmd, - "address-family evpn", - "Enter Address Family command mode\n" - "EVPN Address family\n") +DEFUNSH_HIDDEN(VTYSH_BGPD, address_family_evpn2, address_family_evpn2_cmd, + "address-family evpn", + "Enter Address Family command mode\n" + "EVPN Address family\n") { vty->node = BGP_EVPN_NODE; return CMD_SUCCESS; diff --git a/vtysh/vtysh_config.c b/vtysh/vtysh_config.c index 43aff0e3a5..c561b5222f 100644 --- a/vtysh/vtysh_config.c +++ b/vtysh/vtysh_config.c @@ -224,6 +224,10 @@ void vtysh_config_parse_line(void *arg, const char *line) strlen("ipv6 access-list")) == 0) config = config_get(ACCESS_IPV6_NODE, line); + else if (strncmp(line, "mac access-list", + strlen("mac access-list")) + == 0) + config = config_get(ACCESS_MAC_NODE, line); else if (strncmp(line, "ip prefix-list", strlen("ip prefix-list")) == 0) @@ -302,9 +306,10 @@ void vtysh_config_parse_line(void *arg, const char *line) #define NO_DELIMITER(I) \ ((I) == ACCESS_NODE || (I) == PREFIX_NODE || (I) == IP_NODE \ || (I) == AS_LIST_NODE || (I) == COMMUNITY_LIST_NODE \ - || (I) == ACCESS_IPV6_NODE || (I) == PREFIX_IPV6_NODE \ - || (I) == SERVICE_NODE || (I) == FORWARDING_NODE || (I) == DEBUG_NODE \ - || (I) == AAA_NODE || (I) == VRF_DEBUG_NODE || (I) == MPLS_NODE) + || (I) == ACCESS_IPV6_NODE || (I) == ACCESS_MAC_NODE \ + || (I) == PREFIX_IPV6_NODE || (I) == SERVICE_NODE \ + || (I) == FORWARDING_NODE || (I) == DEBUG_NODE || (I) == AAA_NODE \ + || (I) == VRF_DEBUG_NODE || (I) == MPLS_NODE) /* Display configuration to file pointer. */ void vtysh_config_dump(FILE *fp) diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c index a46657dd2e..0d08155178 100644 --- a/zebra/if_netlink.c +++ b/zebra/if_netlink.c @@ -251,6 +251,8 @@ static void netlink_determine_zebra_iftype(char *kind, zebra_iftype_t *zif_type) *zif_type = ZEBRA_IF_VLAN; else if (strcmp(kind, "vxlan") == 0) *zif_type = ZEBRA_IF_VXLAN; + else if (strcmp(kind, "macvlan") == 0) + *zif_type = ZEBRA_IF_MACVLAN; } // Temporary Assignments to compile on older platforms. @@ -401,16 +403,19 @@ static int get_iflink_speed(const char *ifname) /* use ioctl to get IP address of an interface */ sd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP); if (sd < 0) { - zlog_debug("Failure to read interface %s speed: %d %s", ifname, - errno, safe_strerror(errno)); + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("Failure to read interface %s speed: %d %s", + ifname, errno, safe_strerror(errno)); return 0; } /* Get the current link state for the interface */ rc = ioctl(sd, SIOCETHTOOL, (char *)&ifdata); if (rc < 0) { - zlog_debug("IOCTL failure to read interface %s speed: %d %s", - ifname, errno, safe_strerror(errno)); + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug( + "IOCTL failure to read interface %s speed: %d %s", + ifname, errno, safe_strerror(errno)); ecmd.speed_hi = 0; ecmd.speed = 0; } diff --git a/zebra/interface.h b/zebra/interface.h index ea72264696..970c3c5292 100644 --- a/zebra/interface.h +++ b/zebra/interface.h @@ -186,11 +186,12 @@ struct rtadvconf { /* Zebra interface type - ones of interest. */ typedef enum { - ZEBRA_IF_VXLAN, /* VxLAN interface */ - ZEBRA_IF_VRF, /* VRF device */ - ZEBRA_IF_BRIDGE, /* bridge device */ - ZEBRA_IF_VLAN, /* VLAN sub-interface */ - ZEBRA_IF_OTHER, /* Anything else */ + ZEBRA_IF_VXLAN, /* VxLAN interface */ + ZEBRA_IF_VRF, /* VRF device */ + ZEBRA_IF_BRIDGE, /* bridge device */ + ZEBRA_IF_VLAN, /* VLAN sub-interface */ + ZEBRA_IF_MACVLAN, /* MAC VLAN interface*/ + ZEBRA_IF_OTHER, /* Anything else */ } zebra_iftype_t; /* Zebra "slave" interface type */ @@ -295,6 +296,9 @@ static inline void zebra_if_set_ziftype(struct interface *ifp, #define IS_ZEBRA_IF_VXLAN(ifp) \ (((struct zebra_if *)(ifp->info))->zif_type == ZEBRA_IF_VXLAN) +#define IS_ZEBRA_IF_MACVLAN(ifp) \ + (((struct zebra_if *)(ifp->info))->zif_type == ZEBRA_IF_MACVLAN) + #define IS_ZEBRA_IF_BRIDGE_SLAVE(ifp) \ (((struct zebra_if *)(ifp->info))->zif_slave_type \ == ZEBRA_IF_SLAVE_BRIDGE) diff --git a/zebra/redistribute.c b/zebra/redistribute.c index c3bbf40b3f..92da5fe0ce 100644 --- a/zebra/redistribute.c +++ b/zebra/redistribute.c @@ -41,6 +41,7 @@ #include "zebra/debug.h" #include "zebra/router-id.h" #include "zebra/zebra_memory.h" +#include "zebra/zebra_vxlan.h" #define ZEBRA_PTM_SUPPORT @@ -402,6 +403,8 @@ void zebra_interface_address_add_update(struct interface *ifp, zlog_warn( "WARNING: advertising address to clients that is not yet usable."); + zebra_vxlan_add_del_gw_macip(ifp, ifc->address, 1); + router_id_add_address(ifc); for (ALL_LIST_ELEMENTS(zebrad.client_list, node, nnode, client)) @@ -428,6 +431,8 @@ void zebra_interface_address_delete_update(struct interface *ifp, prefix2str(p, buf, sizeof(buf)), ifc->ifp->name); } + zebra_vxlan_add_del_gw_macip(ifp, ifc->address, 0); + router_id_del_address(ifc); for (ALL_LIST_ELEMENTS(zebrad.client_list, node, nnode, client)) diff --git a/zebra/zebra_mpls.c b/zebra/zebra_mpls.c index d1ab2dbb85..47cf7a3cbf 100644 --- a/zebra/zebra_mpls.c +++ b/zebra/zebra_mpls.c @@ -1321,9 +1321,9 @@ static void nhlfe_print(zebra_nhlfe_t *nhlfe, struct vty *vty) default: break; } - vty_out(vty, "%s", - CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED) ? " (installed)" - : ""); + vty_out(vty, "%s", CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED) + ? " (installed)" + : ""); vty_out(vty, "\n"); } @@ -2807,6 +2807,8 @@ void zebra_mpls_close_tables(struct zebra_vrf *zvrf) hash_free(zvrf->lsp_table); hash_clean(zvrf->slsp_table, NULL); hash_free(zvrf->slsp_table); + route_table_finish(zvrf->fec_table[AFI_IP]); + route_table_finish(zvrf->fec_table[AFI_IP6]); } /* diff --git a/zebra/zebra_vrf.h b/zebra/zebra_vrf.h index dca53bb9f3..6e2dc613df 100644 --- a/zebra/zebra_vrf.h +++ b/zebra/zebra_vrf.h @@ -107,6 +107,11 @@ struct zebra_vrf { */ int advertise_all_vni; + /* + * Whether we are advertising g/w macip in EVPN or not. + */ + int advertise_gw_macip; + /* Route Installs */ uint64_t installs; uint64_t removals; diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index e260338131..f01f037ed5 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -2285,89 +2285,100 @@ DEFUN (show_vrf, DEFUN (show_evpn_vni, show_evpn_vni_cmd, - "show evpn vni", + "show evpn vni [json]", SHOW_STR "EVPN\n" - "VxLAN information\n") + "VxLAN information\n" + JSON_STR) { struct zebra_vrf *zvrf; + u_char uj = use_json(argc, argv); zvrf = vrf_info_lookup(VRF_DEFAULT); - zebra_vxlan_print_vnis(vty, zvrf); + zebra_vxlan_print_vnis(vty, zvrf, uj); return CMD_SUCCESS; } DEFUN (show_evpn_vni_vni, show_evpn_vni_vni_cmd, - "show evpn vni " CMD_VNI_RANGE, + "show evpn vni " CMD_VNI_RANGE "[json]", SHOW_STR "EVPN\n" "VxLAN Network Identifier\n" - "VNI number\n") + "VNI number\n" + JSON_STR) { struct zebra_vrf *zvrf; vni_t vni; + u_char uj = use_json(argc, argv); vni = strtoul(argv[3]->arg, NULL, 10); zvrf = vrf_info_lookup(VRF_DEFAULT); - zebra_vxlan_print_vni(vty, zvrf, vni); + zebra_vxlan_print_vni(vty, zvrf, vni, uj); return CMD_SUCCESS; } DEFUN (show_evpn_mac_vni, show_evpn_mac_vni_cmd, - "show evpn mac vni " CMD_VNI_RANGE, + "show evpn mac vni " CMD_VNI_RANGE "[json]", SHOW_STR "EVPN\n" "MAC addresses\n" "VxLAN Network Identifier\n" - "VNI number\n") + "VNI number\n" + JSON_STR) { struct zebra_vrf *zvrf; vni_t vni; + u_char uj = use_json(argc, argv); vni = strtoul(argv[4]->arg, NULL, 10); zvrf = vrf_info_lookup(VRF_DEFAULT); - zebra_vxlan_print_macs_vni(vty, zvrf, vni); + zebra_vxlan_print_macs_vni(vty, zvrf, vni, uj); return CMD_SUCCESS; } DEFUN (show_evpn_mac_vni_all, show_evpn_mac_vni_all_cmd, - "show evpn mac vni all", + "show evpn mac vni all [json]", SHOW_STR "EVPN\n" "MAC addresses\n" "VxLAN Network Identifier\n" - "All VNIs\n") + "All VNIs\n" + JSON_STR) { struct zebra_vrf *zvrf; + u_char uj = use_json(argc, argv); zvrf = vrf_info_lookup(VRF_DEFAULT); - zebra_vxlan_print_macs_all_vni(vty, zvrf); + zebra_vxlan_print_macs_all_vni(vty, zvrf, uj); return CMD_SUCCESS; } DEFUN (show_evpn_mac_vni_all_vtep, show_evpn_mac_vni_all_vtep_cmd, - "show evpn mac vni all vtep A.B.C.D", + "show evpn mac vni all vtep A.B.C.D [json]", SHOW_STR "EVPN\n" "MAC addresses\n" "VxLAN Network Identifier\n" "All VNIs\n" "Remote VTEP\n" - "Remote VTEP IP address\n") + "Remote VTEP IP address\n" + JSON_STR) { struct zebra_vrf *zvrf; struct in_addr vtep_ip; + u_char uj = use_json(argc, argv); if (!inet_aton(argv[6]->arg, &vtep_ip)) { - vty_out(vty, "%% Malformed VTEP IP address\n"); + if (!uj) + vty_out(vty, "%% Malformed VTEP IP address\n"); return CMD_WARNING; } zvrf = vrf_info_lookup(VRF_DEFAULT); - zebra_vxlan_print_macs_all_vni_vtep(vty, zvrf, vtep_ip); + zebra_vxlan_print_macs_all_vni_vtep(vty, zvrf, vtep_ip, uj); return CMD_SUCCESS; } @@ -2400,112 +2411,125 @@ DEFUN (show_evpn_mac_vni_mac, DEFUN (show_evpn_mac_vni_vtep, show_evpn_mac_vni_vtep_cmd, - "show evpn mac vni " CMD_VNI_RANGE " vtep A.B.C.D", + "show evpn mac vni " CMD_VNI_RANGE " vtep A.B.C.D" "[json]", SHOW_STR "EVPN\n" "MAC addresses\n" "VxLAN Network Identifier\n" "VNI number\n" "Remote VTEP\n" - "Remote VTEP IP address\n") + "Remote VTEP IP address\n" + JSON_STR) { struct zebra_vrf *zvrf; vni_t vni; struct in_addr vtep_ip; + u_char uj = use_json(argc, argv); vni = strtoul(argv[4]->arg, NULL, 10); if (!inet_aton(argv[6]->arg, &vtep_ip)) { - vty_out(vty, "%% Malformed VTEP IP address\n"); + if (!uj) + vty_out(vty, "%% Malformed VTEP IP address\n"); return CMD_WARNING; } zvrf = vrf_info_lookup(VRF_DEFAULT); - zebra_vxlan_print_macs_vni_vtep(vty, zvrf, vni, vtep_ip); + zebra_vxlan_print_macs_vni_vtep(vty, zvrf, vni, vtep_ip, uj); return CMD_SUCCESS; } DEFUN (show_evpn_neigh_vni, show_evpn_neigh_vni_cmd, - "show evpn arp-cache vni " CMD_VNI_RANGE, + "show evpn arp-cache vni " CMD_VNI_RANGE "[json]", SHOW_STR "EVPN\n" "ARP and ND cache\n" "VxLAN Network Identifier\n" - "VNI number\n") + "VNI number\n" + JSON_STR) { struct zebra_vrf *zvrf; vni_t vni; + u_char uj = use_json(argc, argv); vni = strtoul(argv[4]->arg, NULL, 10); zvrf = vrf_info_lookup(VRF_DEFAULT); - zebra_vxlan_print_neigh_vni(vty, zvrf, vni); + zebra_vxlan_print_neigh_vni(vty, zvrf, vni, uj); return CMD_SUCCESS; } DEFUN (show_evpn_neigh_vni_all, show_evpn_neigh_vni_all_cmd, - "show evpn arp-cache vni all", + "show evpn arp-cache vni all [json]", SHOW_STR "EVPN\n" "ARP and ND cache\n" "VxLAN Network Identifier\n" - "All VNIs\n") + "All VNIs\n" + JSON_STR) { struct zebra_vrf *zvrf; + u_char uj = use_json(argc, argv); zvrf = vrf_info_lookup(VRF_DEFAULT); - zebra_vxlan_print_neigh_all_vni(vty, zvrf); + zebra_vxlan_print_neigh_all_vni(vty, zvrf, uj); return CMD_SUCCESS; } DEFUN (show_evpn_neigh_vni_neigh, show_evpn_neigh_vni_neigh_cmd, - "show evpn arp-cache vni " CMD_VNI_RANGE " ip WORD", + "show evpn arp-cache vni " CMD_VNI_RANGE " ip WORD [json]", SHOW_STR "EVPN\n" "ARP and ND cache\n" "VxLAN Network Identifier\n" "VNI number\n" "Neighbor\n" - "Neighbor address (IPv4 or IPv6 address)\n") + "Neighbor address (IPv4 or IPv6 address)\n" + JSON_STR) { struct zebra_vrf *zvrf; vni_t vni; struct ipaddr ip; + u_char uj = use_json(argc, argv); vni = strtoul(argv[4]->arg, NULL, 10); if (str2ipaddr(argv[6]->arg, &ip) != 0) { - vty_out(vty, "%% Malformed Neighbor address\n"); + if (!uj) + vty_out(vty, "%% Malformed Neighbor address\n"); return CMD_WARNING; } zvrf = vrf_info_lookup(VRF_DEFAULT); - zebra_vxlan_print_specific_neigh_vni(vty, zvrf, vni, &ip); + zebra_vxlan_print_specific_neigh_vni(vty, zvrf, vni, &ip, uj); return CMD_SUCCESS; } DEFUN (show_evpn_neigh_vni_vtep, show_evpn_neigh_vni_vtep_cmd, - "show evpn arp-cache vni " CMD_VNI_RANGE " vtep A.B.C.D", + "show evpn arp-cache vni " CMD_VNI_RANGE " vtep A.B.C.D [json]", SHOW_STR "EVPN\n" "ARP and ND cache\n" "VxLAN Network Identifier\n" "VNI number\n" "Remote VTEP\n" - "Remote VTEP IP address\n") + "Remote VTEP IP address\n" + JSON_STR) { struct zebra_vrf *zvrf; vni_t vni; struct in_addr vtep_ip; + u_char uj = use_json(argc, argv); vni = strtoul(argv[4]->arg, NULL, 10); if (!inet_aton(argv[6]->arg, &vtep_ip)) { - vty_out(vty, "%% Malformed VTEP IP address\n"); + if (!uj) + vty_out(vty, "%% Malformed VTEP IP address\n"); return CMD_WARNING; } zvrf = vrf_info_lookup(VRF_DEFAULT); - zebra_vxlan_print_neigh_vni_vtep(vty, zvrf, vni, vtep_ip); + zebra_vxlan_print_neigh_vni_vtep(vty, zvrf, vni, vtep_ip, uj); return CMD_SUCCESS; } diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index 7d265af309..f99c16ae91 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -46,6 +46,7 @@ #include "zebra/zebra_vxlan.h" #include "zebra/zebra_memory.h" #include "zebra/zebra_l2.h" +#include "lib/json.h" DEFINE_MTYPE_STATIC(ZEBRA, ZVNI, "VNI hash"); DEFINE_MTYPE_STATIC(ZEBRA, ZVNI_VTEP, "VNI remote VTEP"); @@ -56,24 +57,25 @@ DEFINE_MTYPE_STATIC(ZEBRA, NEIGH, "VNI Neighbor"); /* static function declarations */ -static void zvni_print_neigh(zebra_neigh_t *n, void *ctxt); +static void zvni_print_neigh(zebra_neigh_t *n, void *ctxt, json_object *json); static void zvni_print_neigh_hash(struct hash_backet *backet, void *ctxt); static void zvni_print_neigh_hash_all_vni(struct hash_backet *backet, - void *ctxt); + void **args); static void zvni_print_mac(zebra_mac_t *mac, void *ctxt); static void zvni_print_mac_hash(struct hash_backet *backet, void *ctxt); static void zvni_print_mac_hash_all_vni(struct hash_backet *backet, void *ctxt); -static void zvni_print(zebra_vni_t *zvni, void *ctxt); -static void zvni_print_hash(struct hash_backet *backet, void *ctxt); +static void zvni_print(zebra_vni_t *zvni, void **ctxt); +static void zvni_print_hash(struct hash_backet *backet, void *ctxt[]); static int zvni_macip_send_msg_to_client(struct zebra_vrf *zvrf, vni_t vni, struct ethaddr *macaddr, - struct ipaddr *ip, u_char sticky, + struct ipaddr *ip, u_char flags, u_int16_t cmd); static unsigned int neigh_hash_keymake(void *p); static int neigh_cmp(const void *p1, const void *p2); static void *zvni_neigh_alloc(void *p); -static zebra_neigh_t *zvni_neigh_add(zebra_vni_t *zvni, struct ipaddr *ip); +static zebra_neigh_t *zvni_neigh_add(zebra_vni_t *zvni, struct ipaddr *ip, + struct ethaddr *mac); static int zvni_neigh_del(zebra_vni_t *zvni, zebra_neigh_t *n); static int zvni_neigh_del_hash_entry(struct hash_backet *backet, void *arg); static void zvni_neigh_del_from_vtep(zebra_vni_t *zvni, int uninstall, @@ -83,10 +85,10 @@ static void zvni_neigh_del_all(struct zebra_vrf *zvrf, zebra_vni_t *zvni, static zebra_neigh_t *zvni_neigh_lookup(zebra_vni_t *zvni, struct ipaddr *ip); static int zvni_neigh_send_add_to_client(struct zebra_vrf *zvrf, vni_t vni, struct ipaddr *ip, - struct ethaddr *macaddr); + struct ethaddr *macaddr, u_char flags); static int zvni_neigh_send_del_to_client(struct zebra_vrf *zvrf, vni_t vni, struct ipaddr *ip, - struct ethaddr *macaddr); + struct ethaddr *macaddr, u_char flags); static int zvni_neigh_install(zebra_vni_t *zvni, zebra_neigh_t *n); static int zvni_neigh_uninstall(zebra_vni_t *zvni, zebra_neigh_t *n); static zebra_vni_t *zvni_map_svi(struct interface *ifp, @@ -106,9 +108,9 @@ static void zvni_mac_del_all(struct zebra_vrf *zvrf, zebra_vni_t *zvni, int uninstall, int upd_client, u_int32_t flags); static zebra_mac_t *zvni_mac_lookup(zebra_vni_t *zvni, struct ethaddr *macaddr); static int zvni_mac_send_add_to_client(struct zebra_vrf *zvrf, vni_t vni, - struct ethaddr *macaddr, u_char sticky); + struct ethaddr *macaddr, u_char flags); static int zvni_mac_send_del_to_client(struct zebra_vrf *zvrf, vni_t vni, - struct ethaddr *macaddr, u_char sticky); + struct ethaddr *macaddr, u_char flags); static zebra_vni_t *zvni_map_vlan(struct interface *ifp, struct interface *br_if, vlanid_t vid); static int zvni_mac_install(zebra_vni_t *zvni, zebra_mac_t *mac); @@ -131,10 +133,31 @@ static int zvni_vtep_del(zebra_vni_t *zvni, zebra_vtep_t *zvtep); static int zvni_vtep_del_all(zebra_vni_t *zvni, int uninstall); static int zvni_vtep_install(zebra_vni_t *zvni, struct in_addr *vtep_ip); static int zvni_vtep_uninstall(zebra_vni_t *zvni, struct in_addr *vtep_ip); - +static int zvni_del_macip_for_intf(struct interface *ifp, zebra_vni_t *zvni); +static int zvni_add_macip_for_intf(struct interface *ifp, zebra_vni_t *zvni); +static int zvni_gw_macip_add(struct interface *ifp, zebra_vni_t *zvni, + struct ethaddr *macaddr, struct ipaddr *ip); +static int zvni_gw_macip_del(struct interface *ifp, zebra_vni_t *zvni, + struct ipaddr *ip); +struct interface *zebra_get_vrr_intf_for_svi(struct interface *ifp); +static int advertise_gw_macip_enabled(struct zebra_vrf *zvrf, + zebra_vni_t *zvni); +static void zvni_deref_ip2mac(zebra_vni_t *zvni, zebra_mac_t *mac, + int uninstall); /* Private functions */ +static int advertise_gw_macip_enabled(struct zebra_vrf *zvrf, zebra_vni_t *zvni) +{ + if (zvrf && zvrf->advertise_gw_macip) + return 1; + + if (zvni && zvni->advertise_gw_macip) + return 1; + + return 0; +} + /* * Helper function to determine maximum width of neighbor IP address for * display - just because we're dealing with IPv6 addresses that can @@ -159,18 +182,37 @@ static void zvni_find_neigh_addr_width(struct hash_backet *backet, void *ctxt) /* * Print a specific neighbor entry. */ -static void zvni_print_neigh(zebra_neigh_t *n, void *ctxt) +static void zvni_print_neigh(zebra_neigh_t *n, void *ctxt, json_object *json) { struct vty *vty; char buf1[ETHER_ADDR_STRLEN]; char buf2[INET6_ADDRSTRLEN]; - ipaddr2str(&n->ip, buf2, sizeof(buf2)), vty = (struct vty *)ctxt; - vty_out(vty, "IP: %s\n", ipaddr2str(&n->ip, buf2, sizeof(buf2))); - vty_out(vty, " MAC: %s", prefix_mac2str(&n->emac, buf1, sizeof(buf1))); - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) - vty_out(vty, " Remote VTEP: %s", inet_ntoa(n->r_vtep_ip)); - vty_out(vty, "\n"); + ipaddr2str(&n->ip, buf2, sizeof(buf2)); + prefix_mac2str(&n->emac, buf1, sizeof(buf1)); + vty = (struct vty *)ctxt; + if (json == NULL) { + vty_out(vty, "IP: %s\n", + ipaddr2str(&n->ip, buf2, sizeof(buf2))); + vty_out(vty, " MAC: %s", + prefix_mac2str(&n->emac, buf1, sizeof(buf1))); + } else { + json_object_string_add(json, "ip", buf2); + json_object_string_add(json, "mac", buf1); + } + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) { + if (json == NULL) { + vty_out(vty, " Remote VTEP: %s", + inet_ntoa(n->r_vtep_ip)); + vty_out(vty, " State: %s", IS_ZEBRA_NEIGH_ACTIVE(n) + ? "Active" + : "Inactive"); + } else + json_object_string_add(json, "remoteVtep", + inet_ntoa(n->r_vtep_ip)); + } + if (json == NULL) + vty_out(vty, "\n"); } /* @@ -179,65 +221,115 @@ static void zvni_print_neigh(zebra_neigh_t *n, void *ctxt) static void zvni_print_neigh_hash(struct hash_backet *backet, void *ctxt) { struct vty *vty; + json_object *json_vni = NULL, *json_row = NULL; zebra_neigh_t *n; char buf1[ETHER_ADDR_STRLEN]; char buf2[INET6_ADDRSTRLEN]; struct neigh_walk_ctx *wctx = ctxt; vty = wctx->vty; + json_vni = wctx->json; n = (zebra_neigh_t *)backet->data; if (!n) return; + if (json_vni) + json_row = json_object_new_object(); + prefix_mac2str(&n->emac, buf1, sizeof(buf1)); ipaddr2str(&n->ip, buf2, sizeof(buf2)); if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL) && !(wctx->flags & SHOW_REMOTE_NEIGH_FROM_VTEP)) { - vty_out(vty, "%*s %-6s %-17s\n", -wctx->addr_width, buf2, - "local", buf1); + if (json_vni == NULL) { + vty_out(vty, "%*s %-6s %-17s\n", -wctx->addr_width, + buf2, "local", buf1); + } else { + json_object_string_add(json_row, "type", "local"); + json_object_string_add(json_row, "mac", buf1); + } wctx->count++; } else { if (wctx->flags & SHOW_REMOTE_NEIGH_FROM_VTEP) { if (IPV4_ADDR_SAME(&n->r_vtep_ip, &wctx->r_vtep_ip)) { - if (wctx->count == 0) + if (json_vni == NULL) { + if (wctx->count == 0) + vty_out(vty, + "%*s %-6s %-17s %-21s\n", + -wctx->addr_width, + "Neighbor", "Type", + "MAC", "Remote VTEP"); vty_out(vty, "%*s %-6s %-17s %-21s\n", - -wctx->addr_width, "Neighbor", - "Type", "MAC", "Remote VTEP"); - vty_out(vty, "%*s %-6s %-17s %-21s\n", - -wctx->addr_width, buf2, "remote", buf1, - inet_ntoa(n->r_vtep_ip)); + -wctx->addr_width, buf2, + "remote", buf1, + inet_ntoa(n->r_vtep_ip)); + } else { + json_object_string_add(json_row, "type", + "remote"); + json_object_string_add(json_row, "mac", + buf1); + json_object_string_add( + json_row, "remoteVtep", + inet_ntoa(n->r_vtep_ip)); + } wctx->count++; } } else { - vty_out(vty, "%*s %-6s %-17s %-21s\n", - -wctx->addr_width, buf2, "remote", buf1, - inet_ntoa(n->r_vtep_ip)); + if (json_vni == NULL) { + vty_out(vty, "%*s %-6s %-17s %-21s\n", + -wctx->addr_width, buf2, "remote", buf1, + inet_ntoa(n->r_vtep_ip)); + } else { + json_object_string_add(json_row, "type", + "remote"); + json_object_string_add(json_row, "mac", buf1); + json_object_string_add(json_row, "remoteVtep", + inet_ntoa(n->r_vtep_ip)); + } wctx->count++; } } + + if (json_vni) + json_object_object_add(json_vni, buf2, json_row); } /* * Print neighbors for all VNI. */ static void zvni_print_neigh_hash_all_vni(struct hash_backet *backet, - void *ctxt) + void **args) { struct vty *vty; + json_object *json = NULL, *json_vni = NULL; zebra_vni_t *zvni; u_int32_t num_neigh; struct neigh_walk_ctx wctx; + char vni_str[VNI_STR_LEN]; + + vty = (struct vty *)args[0]; + json = (json_object *)args[1]; - vty = (struct vty *)ctxt; zvni = (zebra_vni_t *)backet->data; - if (!zvni) + if (!zvni) { + if (json) + vty_out(vty, "{}\n"); return; - + } num_neigh = hashcount(zvni->neigh_table); - vty_out(vty, "\nVNI %u #ARP (IPv4 and IPv6, local and remote) %u\n\n", - zvni->vni, num_neigh); - if (!num_neigh) + if (json == NULL) + vty_out(vty, + "\nVNI %u #ARP (IPv4 and IPv6, local and remote) %u\n\n", + zvni->vni, num_neigh); + else { + json_vni = json_object_new_object(); + json_object_int_add(json_vni, "numArpNd", num_neigh); + snprintf(vni_str, VNI_STR_LEN, "%u", zvni->vni); + } + if (!num_neigh) { + if (json) + json_object_object_add(json, vni_str, json_vni); return; + } /* Since we have IPv6 addresses to deal with which can vary widely in * size, we try to be a bit more elegant in display by first computing @@ -247,11 +339,16 @@ static void zvni_print_neigh_hash_all_vni(struct hash_backet *backet, wctx.zvni = zvni; wctx.vty = vty; wctx.addr_width = 15; + wctx.json = json_vni; hash_iterate(zvni->neigh_table, zvni_find_neigh_addr_width, &wctx); - vty_out(vty, "%*s %-6s %-17s %-21s\n", -wctx.addr_width, "IP", "Type", - "MAC", "Remote VTEP"); + if (json == NULL) + vty_out(vty, "%*s %-6s %-17s %-21s\n", -wctx.addr_width, "IP", + "Type", "MAC", "Remote VTEP"); hash_iterate(zvni->neigh_table, zvni_print_neigh_hash, &wctx); + + if (json) + json_object_object_add(json, vni_str, json_vni); } /* @@ -260,7 +357,10 @@ static void zvni_print_neigh_hash_all_vni(struct hash_backet *backet, static void zvni_print_mac(zebra_mac_t *mac, void *ctxt) { struct vty *vty; + zebra_neigh_t *n = NULL; + struct listnode *node = NULL; char buf1[20]; + char buf2[INET6_ADDRSTRLEN]; vty = (struct vty *)ctxt; vty_out(vty, "MAC: %s", @@ -278,11 +378,30 @@ static void zvni_print_mac(zebra_mac_t *mac, void *ctxt) vty_out(vty, " Intf: %s(%u)", ifp->name, ifindex); if (mac->fwd_info.local.vid) vty_out(vty, " VLAN: %u", mac->fwd_info.local.vid); - } else { + } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) { vty_out(vty, " Remote VTEP: %s", inet_ntoa(mac->fwd_info.r_vtep_ip)); + } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_AUTO)) { + vty_out(vty, " Auto Mac "); } - vty_out(vty, " ARP ref: %u", mac->neigh_refcnt); + vty_out(vty, " ARP ref: %u\n", mac->neigh_refcnt); + + /* print all the associated neigh */ + vty_out(vty, " Neighbors:\n"); + if (!listcount(mac->neigh_list)) + vty_out(vty, " No Neighbors\n"); + else { + for (ALL_LIST_ELEMENTS_RO(mac->neigh_list, node, n)) { + vty_out(vty, " %s %s\n", + ipaddr2str(&n->ip, buf2, sizeof(buf2)), + CHECK_FLAG(n->flags, ZEBRA_MAC_LOCAL) + ? (IS_ZEBRA_NEIGH_ACTIVE(n) + ? "Active" + : "Inactive") + : ""); + } + } + vty_out(vty, "\n"); } @@ -292,16 +411,22 @@ static void zvni_print_mac(zebra_mac_t *mac, void *ctxt) static void zvni_print_mac_hash(struct hash_backet *backet, void *ctxt) { struct vty *vty; + json_object *json_mac_hdr = NULL, *json_mac = NULL; zebra_mac_t *mac; char buf1[20]; struct mac_walk_ctx *wctx = ctxt; vty = wctx->vty; + json_mac_hdr = wctx->json; mac = (zebra_mac_t *)backet->data; if (!mac) return; prefix_mac2str(&mac->macaddr, buf1, sizeof(buf1)); + + if (json_mac_hdr) + json_mac = json_object_new_object(); + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL) && !(wctx->flags & SHOW_REMOTE_MAC_FROM_VTEP)) { struct zebra_ns *zns; @@ -315,29 +440,70 @@ static void zvni_print_mac_hash(struct hash_backet *backet, void *ctxt) if (!ifp) // unexpected return; vid = mac->fwd_info.local.vid; - vty_out(vty, "%-17s %-6s %-21s", buf1, "local", ifp->name); - if (vid) - vty_out(vty, " %-5u", vid); - vty_out(vty, "\n"); + if (json_mac_hdr == NULL) + vty_out(vty, "%-17s %-6s %-21s", buf1, "local", + ifp->name); + else { + json_object_string_add(json_mac, "type", "local"); + json_object_string_add(json_mac, "intf", ifp->name); + } + if (vid) { + if (json_mac_hdr == NULL) + vty_out(vty, " %-5u", vid); + else + json_object_int_add(json_mac, "vlan", vid); + } + if (json_mac_hdr == NULL) + vty_out(vty, "\n"); + else + json_object_object_add(json_mac_hdr, buf1, json_mac); wctx->count++; - } else { + } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) { if (wctx->flags & SHOW_REMOTE_MAC_FROM_VTEP) { if (IPV4_ADDR_SAME(&mac->fwd_info.r_vtep_ip, &wctx->r_vtep_ip)) { if (wctx->count == 0) { - vty_out(vty, "\nVNI %u", - wctx->zvni->vni); - vty_out(vty, "%-17s %-6s %-21s %-5s", - "MAC", "Type", - "Intf/Remote VTEP", "VLAN"); + if (json_mac_hdr == NULL) { + vty_out(vty, "\nVNI %u\n\n", + wctx->zvni->vni); + vty_out(vty, + "%-17s %-6s %-21s %-5s\n", + "MAC", "Type", + "Intf/Remote VTEP", + "VLAN"); + } + } + if (json_mac_hdr == NULL) + vty_out(vty, "%-17s %-6s %-21s\n", buf1, + "remote", + inet_ntoa(mac->fwd_info + .r_vtep_ip)); + else { + json_object_string_add(json_mac, "type", + "remote"); + json_object_string_add( + json_mac, "remoteVtep", + inet_ntoa(mac->fwd_info + .r_vtep_ip)); + json_object_object_add(json_mac_hdr, + buf1, json_mac); } - vty_out(vty, "%-17s %-6s %-21s", buf1, "remote", - inet_ntoa(mac->fwd_info.r_vtep_ip)); wctx->count++; } } else { - vty_out(vty, "%-17s %-6s %-21s", buf1, "remote", - inet_ntoa(mac->fwd_info.r_vtep_ip)); + if (json_mac_hdr == NULL) + vty_out(vty, "%-17s %-6s %-21s\n", buf1, + "remote", + inet_ntoa(mac->fwd_info.r_vtep_ip)); + else { + json_object_string_add(json_mac, "type", + "remote"); + json_object_string_add( + json_mac, "remoteVtep", + inet_ntoa(mac->fwd_info.r_vtep_ip)); + json_object_object_add(json_mac_hdr, buf1, + json_mac); + } wctx->count++; } } @@ -349,15 +515,22 @@ static void zvni_print_mac_hash(struct hash_backet *backet, void *ctxt) static void zvni_print_mac_hash_all_vni(struct hash_backet *backet, void *ctxt) { struct vty *vty; + json_object *json = NULL, *json_vni = NULL; + json_object *json_mac = NULL; zebra_vni_t *zvni; u_int32_t num_macs; struct mac_walk_ctx *wctx = ctxt; + char vni_str[VNI_STR_LEN]; vty = (struct vty *)wctx->vty; + json = (struct json_object *)wctx->json; zvni = (zebra_vni_t *)backet->data; - if (!zvni) + if (!zvni) { + if (json) + vty_out(vty, "{}\n"); return; + } wctx->zvni = zvni; /*We are iterating over a new VNI, set the count to 0*/ @@ -366,59 +539,119 @@ static void zvni_print_mac_hash_all_vni(struct hash_backet *backet, void *ctxt) num_macs = hashcount(zvni->mac_table); if (!num_macs) return; - if (!CHECK_FLAG(wctx->flags, SHOW_REMOTE_MAC_FROM_VTEP)) { - vty_out(vty, "\nVNI %u #MACs (local and remote) %u\n\n", - zvni->vni, num_macs); - vty_out(vty, "%-17s %-6s %-21s %-5s\n", "MAC", "Type", - "Intf/Remote VTEP", "VLAN"); + + if (json) { + json_vni = json_object_new_object(); + json_mac = json_object_new_object(); + snprintf(vni_str, VNI_STR_LEN, "%u", zvni->vni); } + if (!CHECK_FLAG(wctx->flags, SHOW_REMOTE_MAC_FROM_VTEP)) { + if (json == NULL) { + vty_out(vty, "\nVNI %u #MACs (local and remote) %u\n\n", + zvni->vni, num_macs); + vty_out(vty, "%-17s %-6s %-21s %-5s\n", "MAC", "Type", + "Intf/Remote VTEP", "VLAN"); + } else + json_object_int_add(json_vni, "numMacs", num_macs); + } + /* assign per-vni to wctx->json object to fill macs + * under the vni. Re-assign primary json object to fill + * next vni information. + */ + wctx->json = json_mac; hash_iterate(zvni->mac_table, zvni_print_mac_hash, wctx); + wctx->json = json; + if (json) { + if (wctx->count) + json_object_object_add(json_vni, "macs", json_mac); + json_object_object_add(json, vni_str, json_vni); + } } /* * Print a specific VNI entry. */ -static void zvni_print(zebra_vni_t *zvni, void *ctxt) +static void zvni_print(zebra_vni_t *zvni, void **ctxt) { struct vty *vty; zebra_vtep_t *zvtep; u_int32_t num_macs; u_int32_t num_neigh; + json_object *json = NULL; + json_object *json_vtep_list = NULL; + json_object *json_ip_str = NULL; - vty = (struct vty *)ctxt; + vty = ctxt[0]; + json = ctxt[1]; + + if (json == NULL) + vty_out(vty, "VNI: %u\n", zvni->vni); + else + json_object_int_add(json, "vni", zvni->vni); - vty_out(vty, "VNI: %u\n", zvni->vni); if (!zvni->vxlan_if) { // unexpected - vty_out(vty, " VxLAN interface: unknown\n"); + if (json == NULL) + vty_out(vty, " VxLAN interface: unknown\n"); return; } - vty_out(vty, " VxLAN interface: %s ifIndex: %u VTEP IP: %s\n", - zvni->vxlan_if->name, zvni->vxlan_if->ifindex, - inet_ntoa(zvni->local_vtep_ip)); - - if (!zvni->vteps) { - vty_out(vty, " No remote VTEPs known for this VNI\n"); - } else { - vty_out(vty, " Remote VTEPs for this VNI:\n"); - for (zvtep = zvni->vteps; zvtep; zvtep = zvtep->next) - vty_out(vty, " %s\n", inet_ntoa(zvtep->vtep_ip)); - } num_macs = hashcount(zvni->mac_table); - vty_out(vty, - " Number of MACs (local and remote) known for this VNI: %u\n", - num_macs); num_neigh = hashcount(zvni->neigh_table); - vty_out(vty, - " Number of ARPs (IPv4 and IPv6, local and remote) " - "known for this VNI: %u", - num_neigh); + if (json == NULL) + vty_out(vty, " VxLAN interface: %s ifIndex: %u VTEP IP: %s\n", + zvni->vxlan_if->name, zvni->vxlan_if->ifindex, + inet_ntoa(zvni->local_vtep_ip)); + else { + json_object_string_add(json, "vxlanInterface", + zvni->vxlan_if->name); + json_object_int_add(json, "ifindex", zvni->vxlan_if->ifindex); + json_object_string_add(json, "vtepIp", + inet_ntoa(zvni->local_vtep_ip)); + json_object_string_add(json, "advertiseGatewayMacip", + zvni->advertise_gw_macip ? "Yes" : "No"); + json_object_int_add(json, "numMacs", num_macs); + json_object_int_add(json, "numArpNd", num_neigh); + } + if (!zvni->vteps) { + if (json == NULL) + vty_out(vty, " No remote VTEPs known for this VNI\n"); + } else { + if (json == NULL) + vty_out(vty, " Remote VTEPs for this VNI:\n"); + else + json_vtep_list = json_object_new_array(); + for (zvtep = zvni->vteps; zvtep; zvtep = zvtep->next) { + if (json == NULL) + vty_out(vty, " %s\n", + inet_ntoa(zvtep->vtep_ip)); + else { + json_ip_str = json_object_new_string( + inet_ntoa(zvtep->vtep_ip)); + json_object_array_add(json_vtep_list, + json_ip_str); + } + } + if (json) + json_object_object_add(json, "numRemoteVteps", + json_vtep_list); + } + if (json == NULL) { + vty_out(vty, + " Number of MACs (local and remote) known for this VNI: %u\n", + num_macs); + vty_out(vty, + " Number of ARPs (IPv4 and IPv6, local and remote) " + "known for this VNI: %u\n", + num_neigh); + vty_out(vty, " Advertise-gw-macip: %s\n", + zvni->advertise_gw_macip ? "Yes" : "No"); + } } /* * Print a VNI hash entry - called for display of all VNIs. */ -static void zvni_print_hash(struct hash_backet *backet, void *ctxt) +static void zvni_print_hash(struct hash_backet *backet, void *ctxt[]) { struct vty *vty; zebra_vni_t *zvni; @@ -426,8 +659,14 @@ static void zvni_print_hash(struct hash_backet *backet, void *ctxt) u_int32_t num_vteps = 0; u_int32_t num_macs = 0; u_int32_t num_neigh = 0; + json_object *json = NULL; + json_object *json_vni = NULL; + json_object *json_ip_str = NULL; + json_object *json_vtep_list = NULL; + + vty = ctxt[0]; + json = ctxt[1]; - vty = (struct vty *)ctxt; zvni = (zebra_vni_t *)backet->data; if (!zvni) return; @@ -440,9 +679,36 @@ static void zvni_print_hash(struct hash_backet *backet, void *ctxt) num_macs = hashcount(zvni->mac_table); num_neigh = hashcount(zvni->neigh_table); - vty_out(vty, "%-10u %-21s %-15s %-8u %-8u %-15u\n", zvni->vni, - zvni->vxlan_if ? zvni->vxlan_if->name : "unknown", - inet_ntoa(zvni->local_vtep_ip), num_macs, num_neigh, num_vteps); + if (json == NULL) + vty_out(vty, "%-10u %-21s %-15s %-8u %-8u %-15u\n", zvni->vni, + zvni->vxlan_if ? zvni->vxlan_if->name : "unknown", + inet_ntoa(zvni->local_vtep_ip), num_macs, num_neigh, + num_vteps); + else { + char vni_str[VNI_STR_LEN]; + snprintf(vni_str, VNI_STR_LEN, "%u", zvni->vni); + json_vni = json_object_new_object(); + json_object_string_add(json_vni, "vxlanIf", + zvni->vxlan_if ? zvni->vxlan_if->name + : "unknown"); + json_object_string_add(json_vni, "vtepIp", + inet_ntoa(zvni->local_vtep_ip)); + json_object_int_add(json_vni, "numMacs", num_macs); + json_object_int_add(json_vni, "numArpNd", num_neigh); + json_object_int_add(json_vni, "numRemoteVteps", num_vteps); + if (num_vteps) { + json_vtep_list = json_object_new_array(); + for (zvtep = zvni->vteps; zvtep; zvtep = zvtep->next) { + json_ip_str = json_object_new_string( + inet_ntoa(zvtep->vtep_ip)); + json_object_array_add(json_vtep_list, + json_ip_str); + } + json_object_object_add(json_vni, "remoteVteps", + json_vtep_list); + } + json_object_object_add(json, vni_str, json_vni); + } } /* @@ -450,7 +716,7 @@ static void zvni_print_hash(struct hash_backet *backet, void *ctxt) */ static int zvni_macip_send_msg_to_client(struct zebra_vrf *zvrf, vni_t vni, struct ethaddr *macaddr, - struct ipaddr *ip, u_char sticky, + struct ipaddr *ip, u_char flags, u_int16_t cmd) { struct zserv *client; @@ -483,19 +749,18 @@ static int zvni_macip_send_msg_to_client(struct zebra_vrf *zvrf, vni_t vni, } else stream_putl(s, 0); /* Just MAC. */ - stream_putc(s, sticky); /* Sticky MAC? */ + stream_putc(s, flags); /* sticky mac/gateway mac */ /* Write packet size. */ stream_putw_at(s, 0, stream_get_endp(s)); if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("%u:Send MACIP %s %sMAC %s IP %s VNI %u to %s", - zvrf_id(zvrf), - (cmd == ZEBRA_MACIP_ADD) ? "Add" : "Del", - sticky ? "sticky " : "", - prefix_mac2str(macaddr, buf, sizeof(buf)), - ipaddr2str(ip, buf2, sizeof(buf2)), vni, - zebra_route_string(client->proto)); + zlog_debug( + "%u:Send MACIP %s flags 0x%x MAC %s IP %s VNI %u to %s", + zvrf_id(zvrf), (cmd == ZEBRA_MACIP_ADD) ? "Add" : "Del", + flags, prefix_mac2str(macaddr, buf, sizeof(buf)), + ipaddr2str(ip, buf2, sizeof(buf2)), vni, + zebra_route_string(client->proto)); if (cmd == ZEBRA_MACIP_ADD) client->macipadd_cnt++; @@ -554,16 +819,26 @@ static void *zvni_neigh_alloc(void *p) /* * Add neighbor entry. */ -static zebra_neigh_t *zvni_neigh_add(zebra_vni_t *zvni, struct ipaddr *ip) +static zebra_neigh_t *zvni_neigh_add(zebra_vni_t *zvni, struct ipaddr *ip, + struct ethaddr *mac) { zebra_neigh_t tmp_n; zebra_neigh_t *n = NULL; + zebra_mac_t *zmac = NULL; memset(&tmp_n, 0, sizeof(zebra_neigh_t)); memcpy(&tmp_n.ip, ip, sizeof(struct ipaddr)); n = hash_get(zvni->neigh_table, &tmp_n, zvni_neigh_alloc); assert(n); + memcpy(&n->emac, mac, ETH_ALEN); + n->state = ZEBRA_NEIGH_INACTIVE; + + /* Associate the neigh to mac */ + zmac = zvni_mac_lookup(zvni, mac); + if (zmac) + listnode_add_sort(zmac->neigh_list, n); + return n; } @@ -573,6 +848,11 @@ static zebra_neigh_t *zvni_neigh_add(zebra_vni_t *zvni, struct ipaddr *ip) static int zvni_neigh_del(zebra_vni_t *zvni, zebra_neigh_t *n) { zebra_neigh_t *tmp_n; + zebra_mac_t *zmac = NULL; + + zmac = zvni_mac_lookup(zvni, &n->emac); + if (zmac) + listnode_delete(zmac->neigh_list, n); /* Free the VNI hash entry and allocated memory. */ tmp_n = hash_release(zvni->neigh_table, n); @@ -597,8 +877,9 @@ static int zvni_neigh_del_hash_entry(struct hash_backet *backet, void *arg) && (n->flags & ZEBRA_NEIGH_REMOTE) && IPV4_ADDR_SAME(&n->r_vtep_ip, &wctx->r_vtep_ip))) { if (wctx->upd_client && (n->flags & ZEBRA_NEIGH_LOCAL)) - zvni_neigh_send_del_to_client( - wctx->zvrf, wctx->zvni->vni, &n->ip, &n->emac); + zvni_neigh_send_del_to_client(wctx->zvrf, + wctx->zvni->vni, &n->ip, + &n->emac, 0); if (wctx->uninstall) zvni_neigh_uninstall(wctx->zvni, n); @@ -671,14 +952,154 @@ static zebra_neigh_t *zvni_neigh_lookup(zebra_vni_t *zvni, struct ipaddr *ip) return n; } +/* Process all neigh associated to a mac upon local mac add event */ +static void zvni_process_neigh_on_local_mac_add(struct zebra_vrf *zvrf, + zebra_vni_t *zvni, + zebra_mac_t *zmac) +{ + zebra_neigh_t *n = NULL; + struct listnode *node = NULL; + char buf[ETHER_ADDR_STRLEN]; + char buf2[INET6_ADDRSTRLEN]; + + for (ALL_LIST_ELEMENTS_RO(zmac->neigh_list, node, n)) { + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) { + /* MAC is learnt locally, program all inactive neigh + * pointing to this mac */ + if (IS_ZEBRA_NEIGH_INACTIVE(n)) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "%u: neigh %s (MAC %s) on VNI %u is now ACTIVE", + zvrf_id(zvrf), + ipaddr2str(&n->ip, buf2, + sizeof(buf2)), + prefix_mac2str(&n->emac, buf, + sizeof(buf)), + zvni->vni); + + ZEBRA_NEIGH_SET_ACTIVE(n); + zvni_neigh_send_add_to_client( + zvrf, zvni->vni, &n->ip, &n->emac, 0); + } else { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "%u: neigh %s (MAC %s) on VNI %u should NOT be ACTIVE", + zvrf_id(zvrf), + ipaddr2str(&n->ip, buf2, + sizeof(buf2)), + prefix_mac2str(&n->emac, buf, + sizeof(buf)), + zvni->vni); + } + } else if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) { + /* TODO: assume the neigh has moved too ?? */ + } + } +} + +/* Process all neigh associated to a mac upon local mac del event */ +static void zvni_process_neigh_on_local_mac_del(struct zebra_vrf *zvrf, + zebra_vni_t *zvni, + zebra_mac_t *zmac) +{ + zebra_neigh_t *n = NULL; + struct listnode *node = NULL; + char buf[ETHER_ADDR_STRLEN]; + char buf2[INET6_ADDRSTRLEN]; + + for (ALL_LIST_ELEMENTS_RO(zmac->neigh_list, node, n)) { + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) { + if (IS_ZEBRA_NEIGH_ACTIVE(n)) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "%u: neigh %s (MAC %s) on VNI %u is now INACTIVE", + zvrf_id(zvrf), + ipaddr2str(&n->ip, buf2, + sizeof(buf2)), + prefix_mac2str(&n->emac, buf, + sizeof(buf)), + zvni->vni); + + ZEBRA_NEIGH_SET_INACTIVE(n); + zvni_neigh_send_del_to_client( + zvrf, zvni->vni, &n->ip, &n->emac, 0); + } + } else if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_err( + "%u: local MAC %s getting deleted on VNI %u has remote neigh %s", + zvrf_id(zvrf), + prefix_mac2str(&n->emac, buf, + sizeof(buf)), + zvni->vni, + ipaddr2str(&n->ip, buf2, sizeof(buf2))); + } + } +} + +/* process all neigh associated to a mac entry upon remote mac add */ +static void zvni_process_neigh_on_remote_mac_add(struct zebra_vrf *zvrf, + zebra_vni_t *zvni, + zebra_mac_t *zmac) +{ + zebra_neigh_t *n = NULL; + struct listnode *node = NULL; + char buf[ETHER_ADDR_STRLEN]; + char buf2[INET6_ADDRSTRLEN]; + + for (ALL_LIST_ELEMENTS_RO(zmac->neigh_list, node, n)) { + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) { + if (IS_ZEBRA_NEIGH_ACTIVE(n)) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "%u: neigh %s (MAC %s) on VNI %u INACTIVE", + zvrf_id(zvrf), + ipaddr2str(&n->ip, buf2, + sizeof(buf2)), + prefix_mac2str(&n->emac, buf, + sizeof(buf)), + zvni->vni); + + ZEBRA_NEIGH_SET_INACTIVE(n); + zvni_neigh_send_del_to_client( + zvrf, zvni->vni, &n->ip, &n->emac, 0); + } + } + } +} + +/* process all neigh associated to mac entry upon remote mac del */ +static void zvni_process_neigh_on_remote_mac_del(struct zebra_vrf *zvrf, + zebra_vni_t *zvni, + zebra_mac_t *zmac) +{ + zebra_neigh_t *n = NULL; + struct listnode *node = NULL; + char buf[ETHER_ADDR_STRLEN]; + char buf2[INET6_ADDRSTRLEN]; + + for (ALL_LIST_ELEMENTS_RO(zmac->neigh_list, node, n)) { + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_err( + "%u: remote MAC %s getting deleted on VNI %u has local neigh %s", + zvrf_id(zvrf), + prefix_mac2str(&n->emac, buf, + sizeof(buf)), + zvni->vni, + ipaddr2str(&n->ip, buf2, sizeof(buf2))); + } + } +} + /* * Inform BGP about local neighbor addition. */ static int zvni_neigh_send_add_to_client(struct zebra_vrf *zvrf, vni_t vni, struct ipaddr *ip, - struct ethaddr *macaddr) + struct ethaddr *macaddr, u_char flags) { - return zvni_macip_send_msg_to_client(zvrf, vni, macaddr, ip, 0, + return zvni_macip_send_msg_to_client(zvrf, vni, macaddr, ip, flags, ZEBRA_MACIP_ADD); } @@ -687,9 +1108,9 @@ static int zvni_neigh_send_add_to_client(struct zebra_vrf *zvrf, vni_t vni, */ static int zvni_neigh_send_del_to_client(struct zebra_vrf *zvrf, vni_t vni, struct ipaddr *ip, - struct ethaddr *macaddr) + struct ethaddr *macaddr, u_char flags) { - return zvni_macip_send_msg_to_client(zvrf, vni, macaddr, ip, 0, + return zvni_macip_send_msg_to_client(zvrf, vni, macaddr, ip, flags, ZEBRA_MACIP_DEL); } @@ -770,6 +1191,309 @@ static void zvni_install_neigh_hash(struct hash_backet *backet, void *ctxt) zvni_neigh_install(wctx->zvni, n); } +/* Get the VRR interface for SVI if any */ +struct interface *zebra_get_vrr_intf_for_svi(struct interface *ifp) +{ + struct zebra_vrf *zvrf = NULL; + struct interface *tmp_if = NULL; + struct zebra_if *zif = NULL; + struct listnode *node; + + zvrf = vrf_info_lookup(ifp->vrf_id); + assert(zvrf); + + for (ALL_LIST_ELEMENTS_RO(vrf_iflist(zvrf_id(zvrf)), node, tmp_if)) { + zif = tmp_if->info; + if (!zif) + continue; + + if (!IS_ZEBRA_IF_MACVLAN(tmp_if)) + continue; + + if (zif->link == ifp) + return tmp_if; + } + + return NULL; +} + +static int zvni_del_macip_for_intf(struct interface *ifp, zebra_vni_t *zvni) +{ + struct zebra_vrf *zvrf = NULL; + struct listnode *cnode = NULL, *cnnode = NULL; + struct connected *c = NULL; + struct ethaddr macaddr; + + zvrf = vrf_info_lookup(zvni->vxlan_if->vrf_id); + if (!zvrf) + return -1; + + memcpy(&macaddr.octet, ifp->hw_addr, ETH_ALEN); + + for (ALL_LIST_ELEMENTS(ifp->connected, cnode, cnnode, c)) { + struct ipaddr ip; + + memset(&ip, 0, sizeof(struct ipaddr)); + if (!CHECK_FLAG(c->conf, ZEBRA_IFC_REAL)) + continue; + + if (c->address->family == AF_INET) { + ip.ipa_type = IPADDR_V4; + memcpy(&(ip.ipaddr_v4), &(c->address->u.prefix4), + sizeof(struct in_addr)); + } else if (c->address->family == AF_INET6) { + ip.ipa_type = IPADDR_V6; + memcpy(&(ip.ipaddr_v6), &(c->address->u.prefix6), + sizeof(struct in6_addr)); + } else { + continue; + } + + zvni_gw_macip_del(ifp, zvni, &ip); + } + + return 0; +} + +static int zvni_add_macip_for_intf(struct interface *ifp, zebra_vni_t *zvni) +{ + struct zebra_vrf *zvrf = NULL; + struct listnode *cnode = NULL, *cnnode = NULL; + struct connected *c = NULL; + struct ethaddr macaddr; + + zvrf = vrf_info_lookup(zvni->vxlan_if->vrf_id); + if (!zvrf) + return -1; + + memcpy(&macaddr.octet, ifp->hw_addr, ETH_ALEN); + + for (ALL_LIST_ELEMENTS(ifp->connected, cnode, cnnode, c)) { + struct ipaddr ip; + + memset(&ip, 0, sizeof(struct ipaddr)); + if (!CHECK_FLAG(c->conf, ZEBRA_IFC_REAL)) + continue; + + if (c->address->family == AF_INET) { + ip.ipa_type = IPADDR_V4; + memcpy(&(ip.ipaddr_v4), &(c->address->u.prefix4), + sizeof(struct in_addr)); + } else if (c->address->family == AF_INET6) { + ip.ipa_type = IPADDR_V6; + memcpy(&(ip.ipaddr_v6), &(c->address->u.prefix6), + sizeof(struct in6_addr)); + } else { + continue; + } + + zvni_gw_macip_add(ifp, zvni, &macaddr, &ip); + } + + return 0; +} + +/* + * zvni_gw_macip_add_to_client + */ +static int zvni_gw_macip_add(struct interface *ifp, zebra_vni_t *zvni, + struct ethaddr *macaddr, struct ipaddr *ip) +{ + struct zebra_vrf *zvrf = NULL; + struct zebra_if *zif = NULL; + struct zebra_l2info_vxlan *vxl = NULL; + zebra_neigh_t *n = NULL; + zebra_mac_t *mac = NULL; + char buf[ETHER_ADDR_STRLEN]; + char buf2[INET6_ADDRSTRLEN]; + + zvrf = vrf_info_lookup(zvni->vxlan_if->vrf_id); + if (!zvrf) + return -1; + + zif = zvni->vxlan_if->info; + if (!zif) + return -1; + + vxl = &zif->l2info.vxl; + + mac = zvni_mac_lookup(zvni, macaddr); + if (!mac) { + mac = zvni_mac_add(zvni, macaddr); + if (!mac) { + zlog_err("%u:Failed to add MAC %s intf %s(%u) VID %u", + ifp->vrf_id, + prefix_mac2str(macaddr, buf, sizeof(buf)), + ifp->name, ifp->ifindex, vxl->access_vlan); + return -1; + } + } + + /* Set "local" forwarding info. */ + SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL); + SET_FLAG(mac->flags, ZEBRA_MAC_AUTO); + memset(&mac->fwd_info, 0, sizeof(mac->fwd_info)); + mac->fwd_info.local.ifindex = ifp->ifindex; + mac->fwd_info.local.vid = vxl->access_vlan; + + n = zvni_neigh_lookup(zvni, ip); + if (!n) { + n = zvni_neigh_add(zvni, ip, macaddr); + if (!n) { + zlog_err( + "%u:Failed to add neighbor %s MAC %s intf %s(%u) -> VNI %u", + ifp->vrf_id, ipaddr2str(ip, buf2, sizeof(buf2)), + prefix_mac2str(macaddr, NULL, + ETHER_ADDR_STRLEN), + ifp->name, ifp->ifindex, zvni->vni); + return -1; + } + } + + /* Set "local" forwarding info. */ + SET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL); + memcpy(&n->emac, macaddr, ETH_ALEN); + n->ifindex = ifp->ifindex; + + /* We have a neigh associated to mac increment the refcnt*/ + mac->neigh_refcnt++; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "%u:SVI %s(%u) VNI %u, sending GW MAC %s IP %s add to BGP", + ifp->vrf_id, ifp->name, ifp->ifindex, zvni->vni, + prefix_mac2str(macaddr, NULL, ETHER_ADDR_STRLEN), + ipaddr2str(ip, buf2, sizeof(buf2))); + + zvni_neigh_send_add_to_client(zvrf, zvni->vni, ip, macaddr, + ZEBRA_MAC_TYPE_GW); + + return 0; +} + +/* + * zvni_gw_macip_del_from_client + */ +static int zvni_gw_macip_del(struct interface *ifp, zebra_vni_t *zvni, + struct ipaddr *ip) +{ + struct zebra_vrf *zvrf = NULL; + zebra_neigh_t *n = NULL; + zebra_mac_t *mac = NULL; + char buf2[INET6_ADDRSTRLEN]; + + zvrf = vrf_info_lookup(zvni->vxlan_if->vrf_id); + if (!zvrf) + return -1; + + /* If the neigh entry is not present nothing to do*/ + n = zvni_neigh_lookup(zvni, ip); + if (!n) + return 0; + + /* mac entry should be present */ + mac = zvni_mac_lookup(zvni, &n->emac); + if (!mac) + zlog_err("%u: MAC %s doesnt exsists for neigh %s on VNI %u", + ifp->vrf_id, + prefix_mac2str(&n->emac, NULL, ETHER_ADDR_STRLEN), + ipaddr2str(ip, buf2, sizeof(buf2)), zvni->vni); + + /* If the entry is not local nothing to do*/ + if (!CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) + return -1; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "%u:SVI %s(%u) VNI %u, sending GW MAC %s IP %s del to BGP", + ifp->vrf_id, ifp->name, ifp->ifindex, zvni->vni, + prefix_mac2str(&(n->emac), NULL, ETHER_ADDR_STRLEN), + ipaddr2str(ip, buf2, sizeof(buf2))); + + /* Remove neighbor from BGP. */ + zvni_neigh_send_del_to_client(zvrf, zvni->vni, &n->ip, &n->emac, + ZEBRA_MAC_TYPE_GW); + + /* Delete this neighbor entry. */ + zvni_neigh_del(zvni, n); + + /* see if the mac needs to be deleted as well*/ + zvni_deref_ip2mac(zvni, mac, 0); + + return 0; +} + +static void zvni_gw_macip_del_for_vni_hash(struct hash_backet *backet, + void *zvrf) +{ + zebra_vni_t *zvni = NULL; + struct zebra_if *zif = NULL; + struct zebra_l2info_vxlan zl2_info; + struct interface *vlan_if = NULL; + struct interface *vrr_if = NULL; + + /* Add primary SVI MAC*/ + zvni = (zebra_vni_t *)backet->data; + if (!zvni) + return; + + zif = zvni->vxlan_if->info; + zl2_info = zif->l2info.vxl; + + vlan_if = zvni_map_to_svi(zvrf, zl2_info.access_vlan, + zif->brslave_info.br_if); + if (!vlan_if) + return; + + /* Del primary MAC-IP */ + zvni_del_macip_for_intf(vlan_if, zvni); + + /* Del VRR MAC-IP - if any*/ + vrr_if = zebra_get_vrr_intf_for_svi(vlan_if); + if (vrr_if) + zvni_del_macip_for_intf(vrr_if, zvni); + + return; +} + +static void zvni_gw_macip_add_for_vni_hash(struct hash_backet *backet, + void *zvrf) +{ + zebra_vni_t *zvni = NULL; + struct zebra_if *zif = NULL; + struct zebra_l2info_vxlan zl2_info; + struct interface *vlan_if = NULL; + struct interface *vrr_if = NULL; + + zvni = (zebra_vni_t *)backet->data; + if (!zvni) + return; + + if (!advertise_gw_macip_enabled(zvrf, zvni)) + return; + + zif = zvni->vxlan_if->info; + zl2_info = zif->l2info.vxl; + + vlan_if = zvni_map_to_svi(zvrf, zl2_info.access_vlan, + zif->brslave_info.br_if); + if (!vlan_if) + return; + + if (!advertise_gw_macip_enabled(zvrf, zvni)) + return; + + /* Add primary SVI MAC-IP */ + zvni_add_macip_for_intf(vlan_if, zvni); + + /* Add VRR MAC-IP - if any*/ + vrr_if = zebra_get_vrr_intf_for_svi(vlan_if); + if (vrr_if) + zvni_add_macip_for_intf(vrr_if, zvni); + + return; +} + /* * Make hash key for MAC. */ @@ -827,6 +1551,9 @@ static zebra_mac_t *zvni_mac_add(zebra_vni_t *zvni, struct ethaddr *macaddr) mac = hash_get(zvni->mac_table, &tmp_mac, zvni_mac_alloc); assert(mac); + mac->neigh_list = list_new(); + mac->neigh_list->cmp = (int (*)(void *, void *))neigh_cmp; + return mac; } @@ -837,6 +1564,8 @@ static int zvni_mac_del(zebra_vni_t *zvni, zebra_mac_t *mac) { zebra_mac_t *tmp_mac; + list_delete(mac->neigh_list); + /* Free the VNI hash entry and allocated memory. */ tmp_mac = hash_release(zvni->mac_table, mac); if (tmp_mac) @@ -864,8 +1593,9 @@ static int zvni_mac_del_hash_entry(struct hash_backet *backet, void *arg) if (wctx->upd_client && (mac->flags & ZEBRA_MAC_LOCAL)) { sticky = CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY) ? 1 : 0; - zvni_mac_send_del_to_client(wctx->zvrf, wctx->zvni->vni, - &mac->macaddr, sticky); + zvni_mac_send_del_to_client( + wctx->zvrf, wctx->zvni->vni, &mac->macaddr, + (sticky ? ZEBRA_MAC_TYPE_STICKY : 0)); } if (wctx->uninstall) @@ -941,9 +1671,9 @@ static zebra_mac_t *zvni_mac_lookup(zebra_vni_t *zvni, struct ethaddr *mac) * Inform BGP about local MAC addition. */ static int zvni_mac_send_add_to_client(struct zebra_vrf *zvrf, vni_t vni, - struct ethaddr *macaddr, u_char sticky) + struct ethaddr *macaddr, u_char flags) { - return zvni_macip_send_msg_to_client(zvrf, vni, macaddr, NULL, sticky, + return zvni_macip_send_msg_to_client(zvrf, vni, macaddr, NULL, flags, ZEBRA_MACIP_ADD); } @@ -951,9 +1681,9 @@ static int zvni_mac_send_add_to_client(struct zebra_vrf *zvrf, vni_t vni, * Inform BGP about local MAC deletion. */ static int zvni_mac_send_del_to_client(struct zebra_vrf *zvrf, vni_t vni, - struct ethaddr *macaddr, u_char sticky) + struct ethaddr *macaddr, u_char flags) { - return zvni_macip_send_msg_to_client(zvrf, vni, macaddr, NULL, sticky, + return zvni_macip_send_msg_to_client(zvrf, vni, macaddr, NULL, flags, ZEBRA_MACIP_DEL); } @@ -1234,6 +1964,7 @@ static void zvni_read_mac_neigh(struct zebra_vrf *zvrf, zebra_vni_t *zvni, struct zebra_if *zif; struct interface *vlan_if; struct zebra_l2info_vxlan *vxl; + struct interface *vrr_if; zif = ifp->info; vxl = &zif->l2info.vxl; @@ -1247,8 +1978,20 @@ static void zvni_read_mac_neigh(struct zebra_vrf *zvrf, zebra_vni_t *zvni, macfdb_read_for_bridge(zvrf->zns, ifp, zif->brslave_info.br_if); vlan_if = zvni_map_to_svi(zvrf, vxl->access_vlan, zif->brslave_info.br_if); - if (vlan_if) + if (vlan_if) { + + if (advertise_gw_macip_enabled(zvrf, zvni)) { + /* Add SVI MAC-IP */ + zvni_add_macip_for_intf(vlan_if, zvni); + + /* Add VRR MAC-IP - if any*/ + vrr_if = zebra_get_vrr_intf_for_svi(vlan_if); + if (vrr_if) + zvni_add_macip_for_intf(vrr_if, zvni); + } + neigh_read_for_vlan(zvrf->zns, vlan_if); + } } /* @@ -1605,23 +2348,30 @@ static void zvni_cleanup_all(struct hash_backet *backet, void *zvrf) * Display Neighbors for a VNI (VTY command handler). */ void zebra_vxlan_print_neigh_vni(struct vty *vty, struct zebra_vrf *zvrf, - vni_t vni) + vni_t vni, u_char use_json) { zebra_vni_t *zvni; u_int32_t num_neigh; struct neigh_walk_ctx wctx; + json_object *json = NULL; if (!EVPN_ENABLED(zvrf)) return; zvni = zvni_lookup(zvrf, vni); if (!zvni) { - vty_out(vty, "%% VNI %u does not exist\n", vni); + if (use_json) + vty_out(vty, "{}\n"); + else + vty_out(vty, "%% VNI %u does not exist\n", vni); return; } num_neigh = hashcount(zvni->neigh_table); if (!num_neigh) return; + if (use_json) + json = json_object_new_object(); + /* Since we have IPv6 addresses to deal with which can vary widely in * size, we try to be a bit more elegant in display by first computing * the maximum width. @@ -1630,25 +2380,52 @@ void zebra_vxlan_print_neigh_vni(struct vty *vty, struct zebra_vrf *zvrf, wctx.zvni = zvni; wctx.vty = vty; wctx.addr_width = 15; + wctx.json = json; hash_iterate(zvni->neigh_table, zvni_find_neigh_addr_width, &wctx); - vty_out(vty, - "Number of ARPs (local and remote) known for this VNI: %u\n", - num_neigh); - vty_out(vty, "%*s %-6s %-17s %-21s\n", -wctx.addr_width, "IP", "Type", - "MAC", "Remote VTEP"); + if (!use_json) { + vty_out(vty, + "Number of ARPs (local and remote) known for this VNI: %u\n", + num_neigh); + vty_out(vty, "%*s %-6s %-17s %-21s\n", -wctx.addr_width, "IP", + "Type", "MAC", "Remote VTEP"); + } else + json_object_int_add(json, "numArpNd", num_neigh); hash_iterate(zvni->neigh_table, zvni_print_neigh_hash, &wctx); + if (use_json) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } } /* * Display neighbors across all VNIs (VTY command handler). */ -void zebra_vxlan_print_neigh_all_vni(struct vty *vty, struct zebra_vrf *zvrf) +void zebra_vxlan_print_neigh_all_vni(struct vty *vty, struct zebra_vrf *zvrf, + u_char use_json) { + json_object *json = NULL; + void *args[2]; + if (!EVPN_ENABLED(zvrf)) return; - hash_iterate(zvrf->vni_table, zvni_print_neigh_hash_all_vni, vty); + + if (use_json) + json = json_object_new_object(); + + args[0] = vty; + args[1] = json; + hash_iterate(zvrf->vni_table, + (void (*)(struct hash_backet *, + void *))zvni_print_neigh_hash_all_vni, + args); + if (use_json) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } } /* @@ -1656,26 +2433,40 @@ void zebra_vxlan_print_neigh_all_vni(struct vty *vty, struct zebra_vrf *zvrf) */ void zebra_vxlan_print_specific_neigh_vni(struct vty *vty, struct zebra_vrf *zvrf, vni_t vni, - struct ipaddr *ip) + struct ipaddr *ip, u_char use_json) { zebra_vni_t *zvni; zebra_neigh_t *n; + json_object *json = NULL; if (!EVPN_ENABLED(zvrf)) return; zvni = zvni_lookup(zvrf, vni); if (!zvni) { - vty_out(vty, "%% VNI %u does not exist", vni); + if (use_json) + vty_out(vty, "{}\n"); + else + vty_out(vty, "%% VNI %u does not exist\n", vni); return; } n = zvni_neigh_lookup(zvni, ip); if (!n) { - vty_out(vty, "%% Requested neighbor does not exist in VNI %u\n", - vni); + if (!use_json) + vty_out(vty, + "%% Requested neighbor does not exist in VNI %u\n", + vni); return; } + if (use_json) + json = json_object_new_object(); - zvni_print_neigh(n, vty); + zvni_print_neigh(n, vty, json); + + if (use_json) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } } /* @@ -1683,17 +2474,22 @@ void zebra_vxlan_print_specific_neigh_vni(struct vty *vty, * By definition, these are remote neighbors. */ void zebra_vxlan_print_neigh_vni_vtep(struct vty *vty, struct zebra_vrf *zvrf, - vni_t vni, struct in_addr vtep_ip) + vni_t vni, struct in_addr vtep_ip, + u_char use_json) { zebra_vni_t *zvni; u_int32_t num_neigh; struct neigh_walk_ctx wctx; + json_object *json = NULL; if (!EVPN_ENABLED(zvrf)) return; zvni = zvni_lookup(zvrf, vni); if (!zvni) { - vty_out(vty, "%% VNI %u does not exist\n", vni); + if (use_json) + vty_out(vty, "{}\n"); + else + vty_out(vty, "%% VNI %u does not exist\n", vni); return; } num_neigh = hashcount(zvni->neigh_table); @@ -1705,56 +2501,98 @@ void zebra_vxlan_print_neigh_vni_vtep(struct vty *vty, struct zebra_vrf *zvrf, wctx.vty = vty; wctx.flags = SHOW_REMOTE_NEIGH_FROM_VTEP; wctx.r_vtep_ip = vtep_ip; - + wctx.json = json; hash_iterate(zvni->neigh_table, zvni_print_neigh_hash, &wctx); + + if (use_json) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } } /* * Display MACs for a VNI (VTY command handler). */ void zebra_vxlan_print_macs_vni(struct vty *vty, struct zebra_vrf *zvrf, - vni_t vni) + vni_t vni, u_char use_json) { zebra_vni_t *zvni; u_int32_t num_macs; struct mac_walk_ctx wctx; + json_object *json = NULL; + json_object *json_mac = NULL; if (!EVPN_ENABLED(zvrf)) return; zvni = zvni_lookup(zvrf, vni); if (!zvni) { - vty_out(vty, "%% VNI %u does not exist\n", vni); + if (use_json) + vty_out(vty, "{}\n"); + else + vty_out(vty, "%% VNI %u does not exist\n", vni); return; } num_macs = hashcount(zvni->mac_table); if (!num_macs) return; + if (use_json) { + json = json_object_new_object(); + json_mac = json_object_new_object(); + } + memset(&wctx, 0, sizeof(struct mac_walk_ctx)); wctx.zvni = zvni; wctx.vty = vty; + wctx.json = json_mac; - vty_out(vty, - "Number of MACs (local and remote) known for this VNI: %u\n", - num_macs); - vty_out(vty, "%-17s %-6s %-21s %-5s\n", "MAC", "Type", - "Intf/Remote VTEP", "VLAN"); + if (!use_json) { + vty_out(vty, + "Number of MACs (local and remote) known for this VNI: %u\n", + num_macs); + vty_out(vty, "%-17s %-6s %-21s %-5s\n", "MAC", "Type", + "Intf/Remote VTEP", "VLAN"); + } else + json_object_int_add(json, "numMacs", num_macs); hash_iterate(zvni->mac_table, zvni_print_mac_hash, &wctx); + + if (use_json) { + json_object_object_add(json, "macs", json_mac); + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } } /* * Display MACs for all VNIs (VTY command handler). */ -void zebra_vxlan_print_macs_all_vni(struct vty *vty, struct zebra_vrf *zvrf) +void zebra_vxlan_print_macs_all_vni(struct vty *vty, struct zebra_vrf *zvrf, + u_char use_json) { struct mac_walk_ctx wctx; + json_object *json = NULL; - if (!EVPN_ENABLED(zvrf)) + if (!EVPN_ENABLED(zvrf)) { + if (use_json) + vty_out(vty, "{}\n"); return; + } + if (use_json) + json = json_object_new_object(); + memset(&wctx, 0, sizeof(struct mac_walk_ctx)); wctx.vty = vty; + wctx.json = json; hash_iterate(zvrf->vni_table, zvni_print_mac_hash_all_vni, &wctx); + + if (use_json) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } } /* @@ -1762,17 +2600,30 @@ void zebra_vxlan_print_macs_all_vni(struct vty *vty, struct zebra_vrf *zvrf) */ void zebra_vxlan_print_macs_all_vni_vtep(struct vty *vty, struct zebra_vrf *zvrf, - struct in_addr vtep_ip) + struct in_addr vtep_ip, + u_char use_json) { struct mac_walk_ctx wctx; + json_object *json = NULL; if (!EVPN_ENABLED(zvrf)) return; + + if (use_json) + json = json_object_new_object(); + memset(&wctx, 0, sizeof(struct mac_walk_ctx)); wctx.vty = vty; wctx.flags = SHOW_REMOTE_MAC_FROM_VTEP; wctx.r_vtep_ip = vtep_ip; + wctx.json = json; hash_iterate(zvrf->vni_table, zvni_print_mac_hash_all_vni, &wctx); + + if (use_json) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } } /* @@ -1805,64 +2656,128 @@ void zebra_vxlan_print_specific_mac_vni(struct vty *vty, struct zebra_vrf *zvrf, * Display MACs for a VNI from specific VTEP (VTY command handler). */ void zebra_vxlan_print_macs_vni_vtep(struct vty *vty, struct zebra_vrf *zvrf, - vni_t vni, struct in_addr vtep_ip) + vni_t vni, struct in_addr vtep_ip, + u_char use_json) { zebra_vni_t *zvni; u_int32_t num_macs; struct mac_walk_ctx wctx; + json_object *json = NULL; + json_object *json_mac = NULL; if (!EVPN_ENABLED(zvrf)) return; zvni = zvni_lookup(zvrf, vni); if (!zvni) { - vty_out(vty, "%% VNI %u does not exist\n", vni); + if (use_json) + vty_out(vty, "{}\n"); + else + vty_out(vty, "%% VNI %u does not exist\n", vni); return; } num_macs = hashcount(zvni->mac_table); if (!num_macs) return; + + if (use_json) { + json = json_object_new_object(); + json_mac = json_object_new_object(); + } + memset(&wctx, 0, sizeof(struct mac_walk_ctx)); wctx.zvni = zvni; wctx.vty = vty; wctx.flags = SHOW_REMOTE_MAC_FROM_VTEP; wctx.r_vtep_ip = vtep_ip; + wctx.json = json_mac; hash_iterate(zvni->mac_table, zvni_print_mac_hash, &wctx); + + if (use_json) { + json_object_int_add(json, "numMacs", wctx.count); + if (wctx.count) + json_object_object_add(json, "macs", json_mac); + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } } /* * Display VNI information (VTY command handler). */ -void zebra_vxlan_print_vni(struct vty *vty, struct zebra_vrf *zvrf, vni_t vni) +void zebra_vxlan_print_vni(struct vty *vty, struct zebra_vrf *zvrf, vni_t vni, + u_char use_json) { zebra_vni_t *zvni; + json_object *json = NULL; + void *args[2]; if (!EVPN_ENABLED(zvrf)) return; zvni = zvni_lookup(zvrf, vni); if (!zvni) { - vty_out(vty, "%% VNI %u does not exist\n", vni); + if (use_json) + vty_out(vty, "{}\n"); + else + vty_out(vty, "%% VNI %u does not exist\n", vni); return; } - zvni_print(zvni, (void *)vty); + if (use_json) + json = json_object_new_object(); + args[0] = vty; + args[1] = json; + zvni_print(zvni, (void *)args); + if (use_json) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } } /* * Display VNI hash table (VTY command handler). */ -void zebra_vxlan_print_vnis(struct vty *vty, struct zebra_vrf *zvrf) +void zebra_vxlan_print_vnis(struct vty *vty, struct zebra_vrf *zvrf, + u_char use_json) { u_int32_t num_vnis; + json_object *json = NULL; + void *args[2]; if (!EVPN_ENABLED(zvrf)) return; num_vnis = hashcount(zvrf->vni_table); - if (!num_vnis) + if (!num_vnis) { + if (use_json) + vty_out(vty, "{}\n"); return; - vty_out(vty, "Number of VNIs: %u\n", num_vnis); - vty_out(vty, "%-10s %-21s %-15s %-8s %-8s %-15s\n", "VNI", "VxLAN IF", - "VTEP IP", "# MACs", "# ARPs", "# Remote VTEPs"); - hash_iterate(zvrf->vni_table, zvni_print_hash, vty); + } + if (use_json) { + json = json_object_new_object(); + json_object_string_add(json, "advertiseGatewayMacip", + zvrf->advertise_gw_macip ? "Yes" : "No"); + json_object_int_add(json, "numVnis", num_vnis); + } else { + vty_out(vty, "Advertise gateway mac-ip: %s\n", + zvrf->advertise_gw_macip ? "Yes" : "No"); + vty_out(vty, "Number of VNIs: %u\n", num_vnis); + vty_out(vty, "%-10s %-21s %-15s %-8s %-8s %-15s\n", "VNI", + "VxLAN IF", "VTEP IP", "# MACs", "# ARPs", + "# Remote VTEPs"); + } + args[0] = vty; + args[1] = json; + + hash_iterate(zvrf->vni_table, + (void (*)(struct hash_backet *, void *))zvni_print_hash, + args); + + if (use_json) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } } /* @@ -1878,6 +2793,8 @@ int zebra_vxlan_local_neigh_del(struct interface *ifp, zebra_neigh_t *n; struct zebra_vrf *zvrf; char buf[INET6_ADDRSTRLEN]; + char buf2[ETHER_ADDR_STRLEN]; + zebra_mac_t *zmac; /* We are only interested in neighbors on an SVI that resides on top * of a VxLAN bridge. @@ -1902,6 +2819,18 @@ int zebra_vxlan_local_neigh_del(struct interface *ifp, if (!n) return 0; + zmac = zvni_mac_lookup(zvni, &n->emac); + if (!zmac) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_err( + "%u: trying to del a neigh %s without a mac %s on VNI %u", + ifp->vrf_id, ipaddr2str(ip, buf, sizeof(buf)), + prefix_mac2str(&n->emac, buf2, sizeof(buf2)), + zvni->vni); + + return 0; + } + /* If it is a remote entry, the kernel has aged this out or someone has * deleted it, it needs to be re-installed as Quagga is the owner. */ @@ -1915,11 +2844,18 @@ int zebra_vxlan_local_neigh_del(struct interface *ifp, assert(zvrf); /* Remove neighbor from BGP. */ - zvni_neigh_send_del_to_client(zvrf, zvni->vni, &n->ip, &n->emac); + if (IS_ZEBRA_NEIGH_ACTIVE(n)) + zvni_neigh_send_del_to_client(zvrf, zvni->vni, &n->ip, &n->emac, + 0); /* Delete this neighbor entry. */ zvni_neigh_del(zvni, n); + /* see if the AUTO mac needs to be deleted */ + if (CHECK_FLAG(zmac->flags, ZEBRA_MAC_AUTO) + || !listcount(zmac->neigh_list)) + zvni_mac_del(zvni, zmac); + return 0; } @@ -1936,6 +2872,7 @@ int zebra_vxlan_local_neigh_add_update(struct interface *ifp, zebra_vni_t *zvni; zebra_neigh_t *n; struct zebra_vrf *zvrf; + zebra_mac_t *zmac; char buf[ETHER_ADDR_STRLEN]; char buf2[INET6_ADDRSTRLEN]; int send_upd = 1, send_del = 0; @@ -1960,6 +2897,30 @@ int zebra_vxlan_local_neigh_add_update(struct interface *ifp, ifp->ifindex, state, ext_learned ? "ext-learned " : "", zvni->vni); + /* create a dummy MAC if the MAC is not already present */ + zmac = zvni_mac_lookup(zvni, macaddr); + if (!zmac) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "%u: AUTO MAC %s created for neigh %s on VNI %u", + ifp->vrf_id, + prefix_mac2str(macaddr, buf, sizeof(buf)), + ipaddr2str(ip, buf2, sizeof(buf2)), zvni->vni); + + zmac = zvni_mac_add(zvni, macaddr); + if (!zmac) { + zlog_warn("%u:Failed to add MAC %s VNI %u", + zvrf_id(zvrf), + prefix_mac2str(macaddr, buf, sizeof(buf)), + zvni->vni); + return -1; + } + + memset(&zmac->fwd_info, 0, sizeof(zmac->fwd_info)); + memset(&zmac->flags, 0, sizeof(u_int32_t)); + SET_FLAG(zmac->flags, ZEBRA_MAC_AUTO); + } + /* If same entry already exists, it might be a change or it might be a * move from remote to local. */ @@ -1995,7 +2956,7 @@ int zebra_vxlan_local_neigh_add_update(struct interface *ifp, n->r_vtep_ip.s_addr = 0; } } else { - n = zvni_neigh_add(zvni, ip); + n = zvni_neigh_add(zvni, ip, macaddr); if (!n) { zlog_err( "%u:Failed to add neighbor %s MAC %s intf %s(%u) -> VNI %u", @@ -2008,18 +2969,39 @@ int zebra_vxlan_local_neigh_add_update(struct interface *ifp, /* Issue delete for older info, if needed. */ if (send_del) - zvni_neigh_send_del_to_client(zvrf, zvni->vni, &n->ip, - &n->emac); + zvni_neigh_send_del_to_client(zvrf, zvni->vni, &n->ip, &n->emac, + 0); /* Set "local" forwarding info. */ SET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL); - memcpy(&n->emac, macaddr, ETH_ALEN); n->ifindex = ifp->ifindex; + /* Before we program this in BGP, we need to check if MAC is locally + * learnt as well */ + if (!CHECK_FLAG(zmac->flags, ZEBRA_MAC_LOCAL)) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "%u: Skipping neigh %s add to client as MAC %s is not local on VNI %u", + ifp->vrf_id, ipaddr2str(ip, buf2, sizeof(buf2)), + prefix_mac2str(macaddr, buf, sizeof(buf)), + zvni->vni); + + return 0; + } + /* Inform BGP if required. */ - if (send_upd) + if (send_upd) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "%u: neigh %s (MAC %s) is now ACTIVE on VNI %u", + ifp->vrf_id, ipaddr2str(ip, buf2, sizeof(buf2)), + prefix_mac2str(macaddr, buf, sizeof(buf)), + zvni->vni); + + ZEBRA_NEIGH_SET_ACTIVE(n); return zvni_neigh_send_add_to_client(zvrf, zvni->vni, ip, - macaddr); + macaddr, 0); + } return 0; } @@ -2147,6 +3129,9 @@ int zebra_vxlan_remote_macip_del(struct zserv *client, int sock, u_short length, } } else { if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) { + zvni_process_neigh_on_remote_mac_del(zvrf, zvni, + mac); + if (!mac->neigh_refcnt) { zvni_mac_uninstall(zvni, mac, 0); zvni_mac_del(zvni, mac); @@ -2303,6 +3288,8 @@ int zebra_vxlan_remote_macip_add(struct zserv *client, int sock, u_short length, else UNSET_FLAG(mac->flags, ZEBRA_MAC_STICKY); + zvni_process_neigh_on_remote_mac_add(zvrf, zvni, mac); + /* Install the entry. */ zvni_mac_install(zvni, mac); } @@ -2326,7 +3313,7 @@ int zebra_vxlan_remote_macip_add(struct zserv *client, int sock, u_short length, if (update_neigh) { if (!n) { - n = zvni_neigh_add(zvni, &ip); + n = zvni_neigh_add(zvni, &ip, &macaddr); if (!n) { zlog_warn( "%u:Failed to add Neigh %s MAC %s VNI %u Remote VTEP %s", @@ -2349,12 +3336,12 @@ int zebra_vxlan_remote_macip_add(struct zserv *client, int sock, u_short length, if (old_mac) zvni_deref_ip2mac(zvni, old_mac, 1); mac->neigh_refcnt++; + memcpy(&n->emac, &macaddr, ETH_ALEN); } /* Set "remote" forwarding info. */ UNSET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL); /* TODO: Handle MAC change. */ - memcpy(&n->emac, &macaddr, ETH_ALEN); n->r_vtep_ip = vtep_ip; SET_FLAG(n->flags, ZEBRA_NEIGH_REMOTE); @@ -2419,10 +3406,19 @@ int zebra_vxlan_check_del_local_mac(struct interface *ifp, /* Remove MAC from BGP. */ sticky = CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY) ? 1 : 0; - zvni_mac_send_del_to_client(zvrf, zvni->vni, macaddr, sticky); + zvni_mac_send_del_to_client(zvrf, zvni->vni, macaddr, + (sticky ? ZEBRA_MAC_TYPE_STICKY : 0)); - /* Delete this MAC entry. */ - zvni_mac_del(zvni, mac); + /* + * If there are no neigh associated with the mac delete the mac + * else mark it as AUTO for forward reference + */ + if (!listcount(mac->neigh_list)) { + zvni_mac_del(zvni, mac); + } else { + UNSET_FLAG(mac->flags, ZEBRA_MAC_LOCAL); + SET_FLAG(mac->flags, ZEBRA_MAC_AUTO); + } return 0; } @@ -2526,10 +3522,22 @@ int zebra_vxlan_local_mac_del(struct interface *ifp, struct interface *br_if, /* Remove MAC from BGP. */ sticky = CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY) ? 1 : 0; - zvni_mac_send_del_to_client(zvrf, zvni->vni, macaddr, sticky); + zvni_mac_send_del_to_client(zvrf, zvni->vni, macaddr, + (sticky ? ZEBRA_MAC_TYPE_STICKY : 0)); - /* Delete this MAC entry. */ - zvni_mac_del(zvni, mac); + /* Update all the neigh entries associated with this mac */ + zvni_process_neigh_on_local_mac_del(zvrf, zvni, mac); + + /* + * If there are no neigh associated with the mac delete the mac + * else mark it as AUTO for forward reference + */ + if (!listcount(mac->neigh_list)) { + zvni_mac_del(zvni, mac); + } else { + UNSET_FLAG(mac->flags, ZEBRA_MAC_LOCAL); + SET_FLAG(mac->flags, ZEBRA_MAC_AUTO); + } return 0; } @@ -2584,6 +3592,13 @@ int zebra_vxlan_local_mac_add_update(struct interface *ifp, ? 1 : 0; + + /* + * return if nothing has changed. + * inform bgp if sticky flag has changed + * update locally and do not inform bgp if local + * parameters like interface has changed + */ if (mac_sticky == sticky && mac->fwd_info.local.ifindex == ifp->ifindex && mac->fwd_info.local.vid == vid) { @@ -2598,9 +3613,27 @@ int zebra_vxlan_local_mac_add_update(struct interface *ifp, ifp->name, ifp->ifindex, vid, zvni->vni); return 0; + } else if (mac_sticky != sticky) { + add = 1; + } else { + add = 0; /* This is an update of local + interface. */ + } + } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) { + /* + * If we have already learned the MAC as a remote sticky + * MAC, + * this is a operator error and we must log a warning + */ + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY)) { + zlog_warn( + "MAC %s is already learnt as a remote sticky mac behind VTEP %s VNI %d", + prefix_mac2str(macaddr, buf, + sizeof(buf)), + inet_ntoa(mac->fwd_info.r_vtep_ip), + zvni->vni); + return 0; } - - add = 0; /* This is an update of local interface. */ } } @@ -2621,8 +3654,9 @@ int zebra_vxlan_local_mac_add_update(struct interface *ifp, /* Set "local" forwarding info. */ UNSET_FLAG(mac->flags, ZEBRA_MAC_REMOTE); - memset(&mac->fwd_info, 0, sizeof(mac->fwd_info)); + UNSET_FLAG(mac->flags, ZEBRA_MAC_AUTO); SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL); + memset(&mac->fwd_info, 0, sizeof(mac->fwd_info)); mac->fwd_info.local.ifindex = ifp->ifindex; mac->fwd_info.local.vid = vid; @@ -2632,9 +3666,11 @@ int zebra_vxlan_local_mac_add_update(struct interface *ifp, UNSET_FLAG(mac->flags, ZEBRA_MAC_STICKY); /* Inform BGP if required. */ - if (add) + if (add) { + zvni_process_neigh_on_local_mac_add(zvrf, zvni, mac); return zvni_mac_send_add_to_client(zvrf, zvni->vni, macaddr, sticky); + } return 0; } @@ -2765,6 +3801,104 @@ int zebra_vxlan_remote_vtep_add(struct zserv *client, int sock, u_short length, return 0; } +/* + * Add/Del gateway macip to evpn + * g/w can be: + * 1. SVI interface on a vlan aware bridge + * 2. SVI interface on a vlan unaware bridge + * 3. vrr interface (MACVLAN) associated to a SVI + * We advertise macip routes for an interface if it is associated to VxLan vlan + */ +int zebra_vxlan_add_del_gw_macip(struct interface *ifp, struct prefix *p, + int add) +{ + struct ipaddr ip; + struct ethaddr macaddr; + zebra_vni_t *zvni = NULL; + struct zebra_vrf *zvrf = NULL; + + memset(&ip, 0, sizeof(struct ipaddr)); + memset(&macaddr, 0, sizeof(struct ethaddr)); + + zvrf = vrf_info_lookup(ifp->vrf_id); + if (!zvrf) + return -1; + + if (!EVPN_ENABLED(zvrf)) + return 0; + + if (IS_ZEBRA_IF_MACVLAN(ifp)) { + struct interface *svi_if = + NULL; /* SVI corresponding to the MACVLAN */ + struct zebra_if *ifp_zif = + NULL; /* Zebra daemon specific info for MACVLAN */ + struct zebra_if *svi_if_zif = + NULL; /* Zebra daemon specific info for SVI*/ + + ifp_zif = ifp->info; + if (!ifp_zif) + return -1; + + svi_if = ifp_zif->link; + if (!svi_if) { + zlog_err("%u:MACVLAN %s(%u) without link information", + ifp->vrf_id, ifp->name, ifp->ifindex); + return -1; + } + + if (IS_ZEBRA_IF_VLAN(svi_if)) { + svi_if_zif = svi_if->info; + if (svi_if_zif) + zvni = zvni_map_svi(svi_if, svi_if_zif->link); + } else if (IS_ZEBRA_IF_BRIDGE(svi_if)) { + zvni = zvni_map_svi(svi_if, svi_if); + } + } else if (IS_ZEBRA_IF_VLAN(ifp)) { + struct zebra_if *svi_if_zif = + NULL; /* Zebra daemon specific info for SVI*/ + + svi_if_zif = ifp->info; + if (svi_if_zif) + zvni = zvni_map_svi(ifp, svi_if_zif->link); + } else if (IS_ZEBRA_IF_BRIDGE(ifp)) { + zvni = zvni_map_svi(ifp, ifp); + } + + if (!zvni) + return 0; + + if (!zvni->vxlan_if) { + zlog_err("VNI %u hash %p doesn't have intf upon MACVLAN up", + zvni->vni, zvni); + return -1; + } + + + /* check if we are advertising gw macip routes */ + if (!advertise_gw_macip_enabled(zvrf, zvni)) + return 0; + + memcpy(&macaddr.octet, ifp->hw_addr, ETH_ALEN); + + if (p->family == AF_INET) { + ip.ipa_type = IPADDR_V4; + memcpy(&(ip.ipaddr_v4), &(p->u.prefix4), + sizeof(struct in_addr)); + } else if (p->family == AF_INET6) { + ip.ipa_type = IPADDR_V6; + memcpy(&(ip.ipaddr_v6), &(p->u.prefix6), + sizeof(struct in6_addr)); + } + + + if (add) + zvni_gw_macip_add(ifp, zvni, &macaddr, &ip); + else + zvni_gw_macip_del(ifp, zvni, &ip); + + return 0; +} + /* * Handle SVI interface going down. At this point, this is a NOP since * the kernel deletes the neighbor entries on this SVI (if any). @@ -3123,6 +4257,98 @@ int zebra_vxlan_if_add(struct interface *ifp) return 0; } +/* + * Handle message from client to enable/disable advertisement of g/w macip + * routes + */ +int zebra_vxlan_advertise_gw_macip(struct zserv *client, int sock, + u_short length, struct zebra_vrf *zvrf) +{ + struct stream *s; + int advertise; + vni_t vni = 0; + zebra_vni_t *zvni = NULL; + + s = client->ibuf; + advertise = stream_getc(s); + vni = stream_get3(s); + + if (!vni) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("%u:EVPN gateway macip Adv %s, currently %s", + zvrf_id(zvrf), + advertise ? "enabled" : "disabled", + advertise_gw_macip_enabled(zvrf, NULL) + ? "enabled" + : "disabled"); + + if (zvrf->advertise_gw_macip == advertise) + return 0; + + zvrf->advertise_gw_macip = advertise; + + if (advertise_gw_macip_enabled(zvrf, zvni)) + hash_iterate(zvrf->vni_table, + zvni_gw_macip_add_for_vni_hash, zvrf); + else + hash_iterate(zvrf->vni_table, + zvni_gw_macip_del_for_vni_hash, zvrf); + + } else { + struct zebra_if *zif = NULL; + struct zebra_l2info_vxlan zl2_info; + struct interface *vlan_if = NULL; + struct interface *vrr_if = NULL; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "%u:EVPN gateway macip Adv %s on VNI %d , currently %s", + zvrf_id(zvrf), + advertise ? "enabled" : "disabled", vni, + advertise_gw_macip_enabled(zvrf, zvni) + ? "enabled" + : "disabled"); + + zvni = zvni_lookup(zvrf, vni); + if (!zvni) + return 0; + + if (zvni->advertise_gw_macip == advertise) + return 0; + + zvni->advertise_gw_macip = advertise; + + zif = zvni->vxlan_if->info; + zl2_info = zif->l2info.vxl; + + vlan_if = zvni_map_to_svi(zvrf, zl2_info.access_vlan, + zif->brslave_info.br_if); + if (!vlan_if) + return 0; + + if (advertise_gw_macip_enabled(zvrf, zvni)) { + /* Add primary SVI MAC-IP */ + zvni_add_macip_for_intf(vlan_if, zvni); + + /* Add VRR MAC-IP - if any*/ + vrr_if = zebra_get_vrr_intf_for_svi(vlan_if); + if (vrr_if) + zvni_add_macip_for_intf(vrr_if, zvni); + } else { + /* Del primary MAC-IP */ + zvni_del_macip_for_intf(vlan_if, zvni); + + /* Del VRR MAC-IP - if any*/ + vrr_if = zebra_get_vrr_intf_for_svi(vlan_if); + if (vrr_if) + zvni_del_macip_for_intf(vrr_if, zvni); + } + } + + return 0; +} + + /* * Handle message from client to learn (or stop learning) about VNIs and MACs. * When enabled, the VNI hash table will be built and MAC FDB table read; @@ -3151,6 +4377,10 @@ int zebra_vxlan_advertise_all_vni(struct zserv *client, int sock, /* Build VNI hash table and inform BGP. */ zvni_build_hash_table(zvrf); + /* Add all SVI (L3 GW) MACs to BGP*/ + hash_iterate(zvrf->vni_table, zvni_gw_macip_add_for_vni_hash, + zvrf); + /* Read the MAC FDB */ macfdb_read(zvrf->zns); @@ -3182,4 +4412,5 @@ void zebra_vxlan_init_tables(struct zebra_vrf *zvrf) void zebra_vxlan_close_tables(struct zebra_vrf *zvrf) { hash_iterate(zvrf->vni_table, zvni_cleanup_all, zvrf); + hash_free(zvrf->vni_table); } diff --git a/zebra/zebra_vxlan.h b/zebra/zebra_vxlan.h index f9ecd8333d..f7c1afc959 100644 --- a/zebra/zebra_vxlan.h +++ b/zebra/zebra_vxlan.h @@ -41,33 +41,44 @@ #define ZEBRA_VXLIF_MASTER_CHANGE 0x2 #define ZEBRA_VXLIF_VLAN_CHANGE 0x4 +#define VNI_STR_LEN 32 + extern void zebra_vxlan_print_macs_vni(struct vty *vty, struct zebra_vrf *zvrf, - vni_t vni); + vni_t vni, u_char use_json); extern void zebra_vxlan_print_macs_all_vni(struct vty *vty, - struct zebra_vrf *zvrf); + struct zebra_vrf *zvrf, + u_char use_json); extern void zebra_vxlan_print_macs_all_vni_vtep(struct vty *vty, struct zebra_vrf *zvrf, - struct in_addr vtep_ip); + struct in_addr vtep_ip, + u_char use_json); extern void zebra_vxlan_print_specific_mac_vni(struct vty *vty, struct zebra_vrf *zvrf, vni_t vni, struct ethaddr *mac); extern void zebra_vxlan_print_macs_vni_vtep(struct vty *vty, struct zebra_vrf *zvrf, vni_t vni, - struct in_addr vtep_ip); + struct in_addr vtep_ip, + u_char use_json); extern void zebra_vxlan_print_neigh_vni(struct vty *vty, struct zebra_vrf *zvrf, - vni_t vni); + vni_t vni, u_char use_json); extern void zebra_vxlan_print_neigh_all_vni(struct vty *vty, - struct zebra_vrf *zvrf); + struct zebra_vrf *zvrf, + u_char use_json); extern void zebra_vxlan_print_specific_neigh_vni(struct vty *vty, struct zebra_vrf *zvrf, - vni_t vni, struct ipaddr *ip); + vni_t vni, struct ipaddr *ip, + u_char use_json); extern void zebra_vxlan_print_neigh_vni_vtep(struct vty *vty, struct zebra_vrf *zvrf, vni_t vni, - struct in_addr vtep_ip); + struct in_addr vtep_ip, + u_char use_json); extern void zebra_vxlan_print_vni(struct vty *vty, struct zebra_vrf *zvrf, - vni_t vni); -extern void zebra_vxlan_print_vnis(struct vty *vty, struct zebra_vrf *zvrf); + vni_t vni, u_char use_json); +extern void zebra_vxlan_print_vnis(struct vty *vty, struct zebra_vrf *zvrf, + u_char use_json); +extern int zebra_vxlan_add_del_gw_macip(struct interface *ifp, struct prefix *p, + int add); extern int zebra_vxlan_svi_up(struct interface *ifp, struct interface *link_if); extern int zebra_vxlan_svi_down(struct interface *ifp, struct interface *link_if); @@ -104,6 +115,9 @@ extern int zebra_vxlan_remote_vtep_add(struct zserv *client, int sock, u_short length, struct zebra_vrf *zvrf); extern int zebra_vxlan_remote_vtep_del(struct zserv *client, int sock, u_short length, struct zebra_vrf *zvrf); +extern int zebra_vxlan_advertise_gw_macip(struct zserv *client, int sock, + u_short length, + struct zebra_vrf *zvrf); extern int zebra_vxlan_advertise_all_vni(struct zserv *client, int sock, u_short length, struct zebra_vrf *zvrf); diff --git a/zebra/zebra_vxlan_private.h b/zebra/zebra_vxlan_private.h index fbde722927..8539311eab 100644 --- a/zebra/zebra_vxlan_private.h +++ b/zebra/zebra_vxlan_private.h @@ -63,6 +63,9 @@ struct zebra_vni_t_ { /* VNI - key */ vni_t vni; + /* Flag for advertising gw macip */ + u_int8_t advertise_gw_macip; + /* Corresponding VxLAN interface. */ struct interface *vxlan_if; @@ -112,6 +115,9 @@ struct zebra_mac_t_ { } fwd_info; u_int32_t neigh_refcnt; + + /* List of neigh associated with this mac */ + struct list *neigh_list; }; /* @@ -132,10 +138,21 @@ struct mac_walk_ctx { struct in_addr r_vtep_ip; /* To walk MACs from specific VTEP */ - struct vty *vty; /* Used by VTY handlers */ - u_int32_t count; /* Used by VTY handlers */ + struct vty *vty; /* Used by VTY handlers */ + u_int32_t count; /* Used by VTY handlers */ + struct json_object *json; /* Used for JSON Output */ }; +enum zebra_neigh_state { ZEBRA_NEIGH_INACTIVE = 0, ZEBRA_NEIGH_ACTIVE = 1 }; + +#define IS_ZEBRA_NEIGH_ACTIVE(n) n->state == ZEBRA_NEIGH_ACTIVE + +#define IS_ZEBRA_NEIGH_INACTIVE(n) n->state == ZEBRA_NEIGH_INACTIVE + +#define ZEBRA_NEIGH_SET_ACTIVE(n) n->state = ZEBRA_NEIGH_ACTIVE + +#define ZEBRA_NEIGH_SET_INACTIVE(n) n->state = ZEBRA_NEIGH_INACTIVE + /* * Neighbor hash table. * @@ -158,8 +175,10 @@ struct zebra_neigh_t_ { ifindex_t ifindex; u_int32_t flags; -#define ZEBRA_NEIGH_LOCAL 0x01 -#define ZEBRA_NEIGH_REMOTE 0x02 +#define ZEBRA_NEIGH_LOCAL 0x01 +#define ZEBRA_NEIGH_REMOTE 0x02 + + enum zebra_neigh_state state; /* Remote VTEP IP - applicable only for remote neighbors. */ struct in_addr r_vtep_ip; @@ -183,9 +202,10 @@ struct neigh_walk_ctx { struct in_addr r_vtep_ip; /* To walk neighbors from specific VTEP */ - struct vty *vty; /* Used by VTY handlers */ - u_int32_t count; /* Used by VTY handlers */ - u_char addr_width; /* Used by VTY handlers */ + struct vty *vty; /* Used by VTY handlers */ + u_int32_t count; /* Used by VTY handlers */ + u_char addr_width; /* Used by VTY handlers */ + struct json_object *json; /* Used for JSON Output */ }; #endif /* _ZEBRA_VXLAN_PRIVATE_H */ diff --git a/zebra/zserv.c b/zebra/zserv.c index a3a8c11c51..5ee6c6d1f3 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -2533,6 +2533,9 @@ static int zebra_client_read(struct thread *thread) case ZEBRA_FEC_UNREGISTER: zserv_fec_unregister(client, sock, length); break; + case ZEBRA_ADVERTISE_DEFAULT_GW: + zebra_vxlan_advertise_gw_macip(client, sock, length, zvrf); + break; case ZEBRA_ADVERTISE_ALL_VNI: zebra_vxlan_advertise_all_vni(client, sock, length, zvrf); break;