diff options
131 files changed, 4634 insertions, 1417 deletions
diff --git a/Makefile.am b/Makefile.am index 2618029a4..9c6c8663e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -207,7 +207,6 @@ EXTRA_DIST += \ ospfd/Makefile \ pbrd/Makefile \ pimd/Makefile \ - ports/Makefile \ qpb/Makefile \ ripd/Makefile \ ripngd/Makefile \ diff --git a/bfdd/bfd.h b/bfdd/bfd.h index a69ff9a1a..e08a1ff72 100644 --- a/bfdd/bfd.h +++ b/bfdd/bfd.h @@ -166,6 +166,7 @@ enum bfd_session_flags { * expires */ BFD_SESS_FLAG_SHUTDOWN = 1 << 7, /* disable BGP peer function */ + BFD_SESS_FLAG_CONFIG = 1 << 8, /* Session configured with bfd NB API */ }; #define BFD_SET_FLAG(field, flag) (field |= flag) @@ -308,8 +309,8 @@ TAILQ_HEAD(obslist, bfd_session_observer); #define BFD_PKT_INFO_VAL 1 #define BFD_IPV6_PKT_INFO_VAL 1 #define BFD_IPV6_ONLY_VAL 1 -#define BFD_SRCPORTINIT 49142 -#define BFD_SRCPORTMAX 65536 +#define BFD_SRCPORTINIT 49152 +#define BFD_SRCPORTMAX 65535 #define BFD_DEFDESTPORT 3784 #define BFD_DEF_ECHO_PORT 3785 #define BFD_DEF_MHOP_DEST_PORT 4784 diff --git a/bfdd/bfdd_vty.c b/bfdd/bfdd_vty.c index c13949207..4efdd817c 100644 --- a/bfdd/bfdd_vty.c +++ b/bfdd/bfdd_vty.c @@ -158,6 +158,12 @@ DEFUN_NOSH( } } + if (!BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG)) { + if (bs->refcount) + vty_out(vty, "%% session peer is now configurable via bfd daemon.\n"); + BFD_SET_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG); + } + VTY_PUSH_CONTEXT(BFD_PEER_NODE, bs); return CMD_SUCCESS; @@ -984,6 +990,9 @@ static void _bfdd_peer_write_config_iter(struct hash_bucket *hb, void *arg) struct vty *vty = arg; struct bfd_session *bs = hb->data; + if (!BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG)) + return; + _bfdd_peer_write_config(vty, bs); } diff --git a/bfdd/ptm_adapter.c b/bfdd/ptm_adapter.c index 8d80b9468..e92500cd8 100644 --- a/bfdd/ptm_adapter.c +++ b/bfdd/ptm_adapter.c @@ -431,6 +431,10 @@ static void bfdd_dest_deregister(struct stream *msg) /* Unregister client peer notification. */ pcn = pcn_lookup(pc, bs); pcn_free(pcn); + if (bs->refcount || + BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG)) + return; + ptm_bfd_ses_del(&bpc); } /* diff --git a/bgpd/bgp_bfd.c b/bgpd/bgp_bfd.c index 663bc4894..dadf124ee 100644 --- a/bgpd/bgp_bfd.c +++ b/bgpd/bgp_bfd.c @@ -280,6 +280,13 @@ static void bgp_bfd_peer_status_update(struct peer *peer, int status) peer->last_reset = PEER_DOWN_BFD_DOWN; BGP_EVENT_ADD(peer, BGP_Stop); } + if ((status == BFD_STATUS_UP) && (old_status == BFD_STATUS_DOWN) + && peer->status != Established) { + if (!BGP_PEER_START_SUPPRESSED(peer)) { + bgp_fsm_event_update(peer, 1); + BGP_EVENT_ADD(peer, BGP_Start); + } + } } /* diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index 732cc6d2b..52aa92395 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -609,7 +609,8 @@ static int bgp_zebra_send_remote_macip(struct bgp *bgp, struct bgpevpn *vpn, * Add (update) or delete remote VTEP from zebra. */ static int bgp_zebra_send_remote_vtep(struct bgp *bgp, struct bgpevpn *vpn, - struct prefix_evpn *p, int add) + struct prefix_evpn *p, + int flood_control, int add) { struct stream *s; @@ -641,6 +642,7 @@ static int bgp_zebra_send_remote_vtep(struct bgp *bgp, struct bgpevpn *vpn, add ? "ADD" : "DEL", vpn->vni); return -1; } + stream_putl(s, flood_control); stream_putw_at(s, 0, stream_get_endp(s)); @@ -889,6 +891,7 @@ static int evpn_zebra_install(struct bgp *bgp, struct bgpevpn *vpn, { int ret; uint8_t flags; + int flood_control; if (p->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) { flags = 0; @@ -903,7 +906,20 @@ static int evpn_zebra_install(struct bgp *bgp, struct bgpevpn *vpn, bgp, vpn, p, pi->attr->nexthop, 1, flags, mac_mobility_seqnum(pi->attr)); } else { - ret = bgp_zebra_send_remote_vtep(bgp, vpn, p, 1); + switch (pi->attr->pmsi_tnl_type) { + case PMSI_TNLTYPE_INGR_REPL: + flood_control = VXLAN_FLOOD_HEAD_END_REPL; + break; + + case PMSI_TNLTYPE_PIM_SM: + flood_control = VXLAN_FLOOD_PIM_SM; + break; + + default: + flood_control = VXLAN_FLOOD_DISABLED; + break; + } + ret = bgp_zebra_send_remote_vtep(bgp, vpn, p, flood_control, 1); } return ret; @@ -920,7 +936,8 @@ static int evpn_zebra_uninstall(struct bgp *bgp, struct bgpevpn *vpn, ret = bgp_zebra_send_remote_macip(bgp, vpn, p, remote_vtep_ip, 0, 0, 0); else - ret = bgp_zebra_send_remote_vtep(bgp, vpn, p, 0); + ret = bgp_zebra_send_remote_vtep(bgp, vpn, p, + VXLAN_FLOOD_DISABLED, 0); return ret; } @@ -2220,6 +2237,24 @@ static int delete_all_vni_routes(struct bgp *bgp, struct bgpevpn *vpn) return 0; } +/* BUM traffic flood mode per-l2-vni */ +static int bgp_evpn_vni_flood_mode_get(struct bgp *bgp, + struct bgpevpn *vpn) +{ + /* if flooding has been globally disabled per-vni mode is + * not relevant + */ + if (bgp->vxlan_flood_ctrl == VXLAN_FLOOD_DISABLED) + return VXLAN_FLOOD_DISABLED; + + /* if mcast group ip has been specified we use a PIM-SM MDT */ + if (vpn->mcast_grp.s_addr != INADDR_ANY) + return VXLAN_FLOOD_PIM_SM; + + /* default is ingress replication */ + return VXLAN_FLOOD_HEAD_END_REPL; +} + /* * Update (and advertise) local routes for a VNI. Invoked upon the VNI * export RT getting modified or change to tunnel IP. Note that these @@ -2236,7 +2271,8 @@ static int update_routes_for_vni(struct bgp *bgp, struct bgpevpn *vpn) * * RT-3 only if doing head-end replication */ - if (bgp->vxlan_flood_ctrl == VXLAN_FLOOD_HEAD_END_REPL) { + if (bgp_evpn_vni_flood_mode_get(bgp, vpn) + == VXLAN_FLOOD_HEAD_END_REPL) { build_evpn_type3_prefix(&p, vpn->originator_ip); ret = update_evpn_route(bgp, vpn, &p, 0, 0); if (ret) @@ -2297,6 +2333,26 @@ static int delete_routes_for_vni(struct bgp *bgp, struct bgpevpn *vpn) } /* + * There is a flood mcast IP address change. Update the mcast-grp and + * remove the type-3 route if any. A new type-3 route will be generated + * post tunnel_ip update if the new flood mode is head-end-replication. + */ +static int bgp_evpn_mcast_grp_change(struct bgp *bgp, struct bgpevpn *vpn, + struct in_addr mcast_grp) +{ + struct prefix_evpn p; + + vpn->mcast_grp = mcast_grp; + + if (is_vni_live(vpn)) { + build_evpn_type3_prefix(&p, vpn->originator_ip); + delete_evpn_route(bgp, vpn, &p); + } + + return 0; +} + +/* * There is a tunnel endpoint IP address change for this VNI, delete * prior type-3 route (if needed) and update. * Note: Route re-advertisement happens elsewhere after other processing @@ -3538,7 +3594,8 @@ static int update_advertise_vni_routes(struct bgp *bgp, struct bgpevpn *vpn) * * RT-3 only if doing head-end replication */ - if (bgp->vxlan_flood_ctrl == VXLAN_FLOOD_HEAD_END_REPL) { + if (bgp_evpn_vni_flood_mode_get(bgp, vpn) + == VXLAN_FLOOD_HEAD_END_REPL) { build_evpn_type3_prefix(&p, vpn->originator_ip); rn = bgp_node_lookup(vpn->route_table, (struct prefix *)&p); if (!rn) /* unexpected */ @@ -3684,7 +3741,9 @@ static void create_advertise_type3(struct hash_bucket *bucket, void *data) struct bgp *bgp = data; struct prefix_evpn p; - if (!vpn || !is_vni_live(vpn)) + if (!vpn || !is_vni_live(vpn) || + bgp_evpn_vni_flood_mode_get(bgp, vpn) + != VXLAN_FLOOD_HEAD_END_REPL) return; build_evpn_type3_prefix(&p, vpn->originator_ip); @@ -3858,12 +3917,12 @@ static int process_type3_route(struct peer *peer, afi_t afi, safi_t safi, */ if (attr && (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_PMSI_TUNNEL))) { - if (attr->pmsi_tnl_type != PMSI_TNLTYPE_INGR_REPL) { - flog_warn( - EC_BGP_EVPN_PMSI_PRESENT, - "%u:%s - Rx EVPN Type-3 NLRI with unsupported PTA %d", - peer->bgp->vrf_id, peer->host, - attr->pmsi_tnl_type); + if (attr->pmsi_tnl_type != PMSI_TNLTYPE_INGR_REPL && + attr->pmsi_tnl_type != PMSI_TNLTYPE_PIM_SM) { + flog_warn(EC_BGP_EVPN_PMSI_PRESENT, + "%u:%s - Rx EVPN Type-3 NLRI with unsupported PTA %d", + peer->bgp->vrf_id, peer->host, + attr->pmsi_tnl_type); } } @@ -4862,7 +4921,7 @@ int bgp_nlri_parse_evpn(struct peer *peer, struct attr *attr, if (addpath_encoded) { /* When packet overflow occurs return immediately. */ if (pnt + BGP_ADDPATH_ID_LEN > lim) - return -1; + return BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW; addpath_id = ntohl(*((uint32_t *)pnt)); pnt += BGP_ADDPATH_ID_LEN; @@ -4870,14 +4929,14 @@ int bgp_nlri_parse_evpn(struct peer *peer, struct attr *attr, /* All EVPN NLRI types start with type and length. */ if (pnt + 2 > lim) - return -1; + return BGP_NLRI_PARSE_ERROR_EVPN_MISSING_TYPE; rtype = *pnt++; psize = *pnt++; /* When packet overflow occur return immediately. */ if (pnt + psize > lim) - return -1; + return BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW; switch (rtype) { case BGP_EVPN_MAC_IP_ROUTE: @@ -4888,7 +4947,7 @@ int bgp_nlri_parse_evpn(struct peer *peer, struct attr *attr, EC_BGP_EVPN_FAIL, "%u:%s - Error in processing EVPN type-2 NLRI size %d", peer->bgp->vrf_id, peer->host, psize); - return -1; + return BGP_NLRI_PARSE_ERROR_EVPN_TYPE2_SIZE; } break; @@ -4900,7 +4959,7 @@ int bgp_nlri_parse_evpn(struct peer *peer, struct attr *attr, EC_BGP_PKT_PROCESS, "%u:%s - Error in processing EVPN type-3 NLRI size %d", peer->bgp->vrf_id, peer->host, psize); - return -1; + return BGP_NLRI_PARSE_ERROR_EVPN_TYPE3_SIZE; } break; @@ -4912,7 +4971,7 @@ int bgp_nlri_parse_evpn(struct peer *peer, struct attr *attr, EC_BGP_PKT_PROCESS, "%u:%s - Error in processing EVPN type-4 NLRI size %d", peer->bgp->vrf_id, peer->host, psize); - return -1; + return BGP_NLRI_PARSE_ERROR_EVPN_TYPE4_SIZE; } break; @@ -4924,7 +4983,7 @@ int bgp_nlri_parse_evpn(struct peer *peer, struct attr *attr, EC_BGP_PKT_PROCESS, "%u:%s - Error in processing EVPN type-5 NLRI size %d", peer->bgp->vrf_id, peer->host, psize); - return -1; + return BGP_NLRI_PARSE_ERROR_EVPN_TYPE5_SIZE; } break; @@ -4935,9 +4994,9 @@ int bgp_nlri_parse_evpn(struct peer *peer, struct attr *attr, /* Packet length consistency check. */ if (pnt != lim) - return -1; + return BGP_NLRI_PARSE_ERROR_PACKET_LENGTH; - return 0; + return BGP_NLRI_PARSE_OK; } /* @@ -5138,8 +5197,9 @@ struct bgpevpn *bgp_evpn_lookup_vni(struct bgp *bgp, vni_t vni) * Create a new vpn - invoked upon configuration or zebra notification. */ struct bgpevpn *bgp_evpn_new(struct bgp *bgp, vni_t vni, - struct in_addr originator_ip, - vrf_id_t tenant_vrf_id) + struct in_addr originator_ip, + vrf_id_t tenant_vrf_id, + struct in_addr mcast_grp) { struct bgpevpn *vpn; @@ -5152,6 +5212,7 @@ struct bgpevpn *bgp_evpn_new(struct bgp *bgp, vni_t vni, vpn->vni = vni; vpn->originator_ip = originator_ip; vpn->tenant_vrf_id = tenant_vrf_id; + vpn->mcast_grp = mcast_grp; /* Initialize route-target import and export lists */ vpn->import_rtl = list_new(); @@ -5650,7 +5711,10 @@ int bgp_evpn_local_vni_del(struct bgp *bgp, vni_t vni) * about are for the local-tunnel-ip and the (tenant) VRF. */ int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni, - struct in_addr originator_ip, vrf_id_t tenant_vrf_id) + struct in_addr originator_ip, + vrf_id_t tenant_vrf_id, + struct in_addr mcast_grp) + { struct bgpevpn *vpn; struct prefix_evpn p; @@ -5661,11 +5725,14 @@ int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni, if (is_vni_live(vpn) && IPV4_ADDR_SAME(&vpn->originator_ip, &originator_ip) + && IPV4_ADDR_SAME(&vpn->mcast_grp, &mcast_grp) && vpn->tenant_vrf_id == tenant_vrf_id) /* Probably some other param has changed that we don't * care about. */ return 0; + bgp_evpn_mcast_grp_change(bgp, vpn, mcast_grp); + /* Update tenant_vrf_id if it has changed. */ if (vpn->tenant_vrf_id != tenant_vrf_id) { bgpevpn_unlink_from_l3vni(vpn); @@ -5688,7 +5755,8 @@ int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni, /* Create or update as appropriate. */ if (!vpn) { - vpn = bgp_evpn_new(bgp, vni, originator_ip, tenant_vrf_id); + vpn = bgp_evpn_new(bgp, vni, originator_ip, tenant_vrf_id, + mcast_grp); if (!vpn) { flog_err( EC_BGP_VNI, @@ -5716,7 +5784,8 @@ int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni, * * RT-3 only if doing head-end replication */ - if (bgp->vxlan_flood_ctrl == VXLAN_FLOOD_HEAD_END_REPL) { + if (bgp_evpn_vni_flood_mode_get(bgp, vpn) + == VXLAN_FLOOD_HEAD_END_REPL) { build_evpn_type3_prefix(&p, vpn->originator_ip); if (update_evpn_route(bgp, vpn, &p, 0, 0)) { flog_err(EC_BGP_EVPN_ROUTE_CREATE, diff --git a/bgpd/bgp_evpn.h b/bgpd/bgp_evpn.h index f5802fa89..4ad8e95be 100644 --- a/bgpd/bgp_evpn.h +++ b/bgpd/bgp_evpn.h @@ -180,7 +180,8 @@ extern int bgp_evpn_local_l3vni_del(vni_t vni, vrf_id_t vrf_id); 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, - vrf_id_t tenant_vrf_id); + vrf_id_t tenant_vrf_id, + struct in_addr mcast_grp); extern int bgp_evpn_local_es_add(struct bgp *bgp, esi_t *esi, struct ipaddr *originator_ip); extern int bgp_evpn_local_es_del(struct bgp *bgp, esi_t *esi, diff --git a/bgpd/bgp_evpn_private.h b/bgpd/bgp_evpn_private.h index 785139865..a5a091242 100644 --- a/bgpd/bgp_evpn_private.h +++ b/bgpd/bgp_evpn_private.h @@ -87,6 +87,9 @@ struct bgpevpn { /* Route type 3 field */ struct in_addr originator_ip; + /* PIM-SM MDT group for BUM flooding */ + struct in_addr mcast_grp; + /* Import and Export RTs. */ struct list *import_rtl; struct list *export_rtl; @@ -237,6 +240,7 @@ static inline void bgpevpn_unlink_from_l3vni(struct bgpevpn *vpn) listnode_delete(vpn->bgp_vrf->l2vnis, vpn); /* remove the backpointer to the vrf instance */ + bgp_unlock(vpn->bgp_vrf); vpn->bgp_vrf = NULL; } @@ -253,7 +257,7 @@ static inline void bgpevpn_link_to_l3vni(struct bgpevpn *vpn) return; /* associate the vpn to the bgp_vrf instance */ - vpn->bgp_vrf = bgp_vrf; + vpn->bgp_vrf = bgp_lock(bgp_vrf); listnode_add_sort(bgp_vrf->l2vnis, vpn); /* check if we are advertising two labels for this vpn */ @@ -525,8 +529,9 @@ extern void bgp_evpn_derive_auto_rd(struct bgp *bgp, struct bgpevpn *vpn); extern void bgp_evpn_derive_auto_rd_for_vrf(struct bgp *bgp); extern struct bgpevpn *bgp_evpn_lookup_vni(struct bgp *bgp, vni_t vni); extern struct bgpevpn *bgp_evpn_new(struct bgp *bgp, vni_t vni, - struct in_addr originator_ip, - vrf_id_t tenant_vrf_id); + struct in_addr originator_ip, + vrf_id_t tenant_vrf_id, + struct in_addr mcast_grp); extern void bgp_evpn_free(struct bgp *bgp, struct bgpevpn *vpn); extern struct evpnes *bgp_evpn_lookup_es(struct bgp *bgp, esi_t *esi); extern struct evpnes *bgp_evpn_es_new(struct bgp *bgp, esi_t *esi, diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c index 5a5d1e0cd..4dccc89f5 100644 --- a/bgpd/bgp_evpn_vty.c +++ b/bgpd/bgp_evpn_vty.c @@ -473,6 +473,8 @@ static void display_vni(struct vty *vty, struct bgpevpn *vpn, json_object *json) prefix_rd2str(&vpn->prd, buf1, sizeof(buf1))); json_object_string_add(json, "originatorIp", inet_ntoa(vpn->originator_ip)); + json_object_string_add(json, "mcastGroup", + inet_ntoa(vpn->mcast_grp)); json_object_string_add(json, "advertiseGatewayMacip", vpn->advertise_gw_macip ? "Yes" : "No"); } else { @@ -488,6 +490,8 @@ static void display_vni(struct vty *vty, struct bgpevpn *vpn, json_object *json) prefix_rd2str(&vpn->prd, buf1, sizeof(buf1))); vty_out(vty, " Originator IP: %s\n", inet_ntoa(vpn->originator_ip)); + vty_out(vty, " Mcast group: %s\n", + inet_ntoa(vpn->mcast_grp)); vty_out(vty, " Advertise-gw-macip : %s\n", vpn->advertise_gw_macip ? "Yes" : "No"); vty_out(vty, " Advertise-svi-macip : %s\n", @@ -1897,6 +1901,7 @@ static void evpn_unconfigure_rd(struct bgp *bgp, struct bgpevpn *vpn) static struct bgpevpn *evpn_create_update_vni(struct bgp *bgp, vni_t vni) { struct bgpevpn *vpn; + struct in_addr mcast_grp = {INADDR_ANY}; if (!bgp->vnihash) return NULL; @@ -1915,7 +1920,7 @@ static struct bgpevpn *evpn_create_update_vni(struct bgp *bgp, vni_t vni) /* tenant vrf will be updated when we get local_vni_add from * zebra */ - vpn = bgp_evpn_new(bgp, vni, bgp->router_id, 0); + vpn = bgp_evpn_new(bgp, vni, bgp->router_id, 0, mcast_grp); if (!vpn) { flog_err( EC_BGP_VNI, @@ -2396,13 +2401,13 @@ static void evpn_show_route_rd(struct vty *vty, struct bgp *bgp, * If 'type' is non-zero, only routes matching that type are shown. */ static void evpn_show_all_routes(struct vty *vty, struct bgp *bgp, int type, - json_object *json) + json_object *json, int detail) { struct bgp_node *rd_rn; struct bgp_table *table; struct bgp_node *rn; struct bgp_path_info *pi; - int header = 1; + int header = detail ? 0 : 1; int rd_header; afi_t afi; safi_t safi; @@ -2482,6 +2487,13 @@ static void evpn_show_all_routes(struct vty *vty, struct bgp *bgp, int type, rn->p.prefixlen); } + /* Prefix and num paths displayed once per prefix. */ + if (detail) + route_vty_out_detail_header( + vty, bgp, rn, + (struct prefix_rd *)&rd_rn->p, + AFI_L2VPN, SAFI_EVPN, json_prefix); + /* For EVPN, the prefix is displayed for each path (to * fit in * with code that already exists). @@ -2495,8 +2507,13 @@ static void evpn_show_all_routes(struct vty *vty, struct bgp *bgp, int type, if (json) json_path = json_object_new_array(); - route_vty_out(vty, &rn->p, pi, 0, SAFI_EVPN, - json_path); + if (detail) { + route_vty_out_detail( + vty, bgp, &rn->p, pi, AFI_L2VPN, + SAFI_EVPN, json_path); + } else + route_vty_out(vty, &rn->p, pi, 0, + SAFI_EVPN, json_path); if (json) json_object_array_add(json_paths, @@ -3633,12 +3650,13 @@ DEFUN(show_bgp_l2vpn_evpn_summary, */ DEFUN(show_bgp_l2vpn_evpn_route, show_bgp_l2vpn_evpn_route_cmd, - "show bgp l2vpn evpn route [type <macip|multicast|es|prefix>] [json]", + "show bgp l2vpn evpn route [detail] [type <macip|multicast|es|prefix>] [json]", SHOW_STR BGP_STR L2VPN_HELP_STR EVPN_HELP_STR "EVPN route information\n" + "Display Detailed Information\n" "Specify Route type\n" "MAC-IP (Type-2) route\n" "Multicast (Type-3) route\n" @@ -3648,6 +3666,7 @@ DEFUN(show_bgp_l2vpn_evpn_route, { struct bgp *bgp; int type_idx = 0; + int detail = 0; int type = 0; bool uj = false; json_object *json = NULL; @@ -3676,7 +3695,10 @@ DEFUN(show_bgp_l2vpn_evpn_route, return CMD_WARNING; } - evpn_show_all_routes(vty, bgp, type, json); + if (argv_find(argv, argc, "detail", &detail)) + detail = 1; + + evpn_show_all_routes(vty, bgp, type, json, detail); if (uj) { vty_out(vty, "%s\n", json_object_to_json_string_ext( @@ -4298,9 +4320,10 @@ ALIAS_HIDDEN(show_bgp_l2vpn_evpn_summary, show_bgp_evpn_summary_cmd, "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 <macip|multicast>]", + "show bgp evpn route [detail] [type <macip|multicast>]", SHOW_STR BGP_STR EVPN_HELP_STR "EVPN route information\n" + "Display Detailed Information\n" "Specify Route type\n" "MAC-IP (Type-2) route\n" "Multicast (Type-3) route\n") diff --git a/bgpd/bgp_filter.c b/bgpd/bgp_filter.c index 80cfb9743..d4f608d40 100644 --- a/bgpd/bgp_filter.c +++ b/bgpd/bgp_filter.c @@ -389,9 +389,9 @@ static int as_list_dup_check(struct as_list *aslist, struct as_filter *new) return 0; } -static int config_bgp_aspath_validate(const char *regstr) +int config_bgp_aspath_validate(const char *regstr) { - char valid_chars[] = "1234567890_^|[,{}() ]$*+.?-"; + char valid_chars[] = "1234567890_^|[,{}() ]$*+.?-\\"; if (strspn(regstr, valid_chars) == strlen(regstr)) return 1; @@ -407,7 +407,7 @@ DEFUN(as_path, bgp_as_path_cmd, "Regular expression access list name\n" "Specify packets to reject\n" "Specify packets to forward\n" - "A regular-expression (1234567890_(^|[,{}() ]|$)) to match the BGP AS paths\n") + "A regular-expression (1234567890_^|[,{}() ]$*+.?-\\) to match the BGP AS paths\n") { int idx = 0; enum as_filter_type type; @@ -475,7 +475,7 @@ ALIAS(as_path, ip_as_path_cmd, "Regular expression access list name\n" "Specify packets to reject\n" "Specify packets to forward\n" - "A regular-expression (1234567890_(^|[,{}() ]|$)) to match the BGP AS paths\n") + "A regular-expression (1234567890_^|[,{}() ]$*+.?-\\) to match the BGP AS paths\n") DEFUN(no_as_path, no_bgp_as_path_cmd, "no bgp as-path access-list WORD <deny|permit> LINE...", @@ -486,7 +486,7 @@ DEFUN(no_as_path, no_bgp_as_path_cmd, "Regular expression access list name\n" "Specify packets to reject\n" "Specify packets to forward\n" - "A regular-expression (1234567890_(^|[,{}() ]|$)) to match the BGP AS paths\n") + "A regular-expression (1234567890_^|[,{}() ]$*+.?-\\) to match the BGP AS paths\n") { int idx = 0; enum as_filter_type type; @@ -563,7 +563,7 @@ ALIAS(no_as_path, no_ip_as_path_cmd, "Regular expression access list name\n" "Specify packets to reject\n" "Specify packets to forward\n" - "A regular-expression (1234567890_(^|[,{}() ]|$)) to match the BGP AS paths\n") + "A regular-expression (1234567890_^|[,{}() ]$*+.?-\\) to match the BGP AS paths\n") DEFUN (no_as_path_all, no_bgp_as_path_all_cmd, diff --git a/bgpd/bgp_filter.h b/bgpd/bgp_filter.h index e54372f8e..3c49e357f 100644 --- a/bgpd/bgp_filter.h +++ b/bgpd/bgp_filter.h @@ -31,5 +31,6 @@ extern enum as_filter_type as_list_apply(struct as_list *, void *); extern struct as_list *as_list_lookup(const char *); extern void as_list_add_hook(void (*func)(char *)); extern void as_list_delete_hook(void (*func)(const char *)); +extern int config_bgp_aspath_validate(const char *regstr); #endif /* _QUAGGA_BGP_FILTER_H */ diff --git a/bgpd/bgp_flowspec.c b/bgpd/bgp_flowspec.c index ab8bfcb77..955463873 100644 --- a/bgpd/bgp_flowspec.c +++ b/bgpd/bgp_flowspec.c @@ -105,14 +105,14 @@ int bgp_nlri_parse_flowspec(struct peer *peer, struct attr *attr, if (afi == AFI_IP6) { flog_err(EC_LIB_DEVELOPMENT, "BGP flowspec IPv6 not supported"); - return -1; + return BGP_NLRI_PARSE_ERROR_FLOWSPEC_IPV6_NOT_SUPPORTED; } if (packet->length >= FLOWSPEC_NLRI_SIZELIMIT) { flog_err(EC_BGP_FLOWSPEC_PACKET, "BGP flowspec nlri length maximum reached (%u)", packet->length); - return -1; + return BGP_NLRI_PARSE_ERROR_FLOWSPEC_NLRI_SIZELIMIT; } for (; pnt < lim; pnt += psize) { @@ -121,7 +121,7 @@ int bgp_nlri_parse_flowspec(struct peer *peer, struct attr *attr, /* All FlowSpec NLRI begin with length. */ if (pnt + 1 > lim) - return -1; + return BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW; psize = *pnt++; @@ -131,13 +131,13 @@ int bgp_nlri_parse_flowspec(struct peer *peer, struct attr *attr, EC_BGP_FLOWSPEC_PACKET, "Flowspec NLRI length inconsistent ( size %u seen)", psize); - return -1; + return BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW; } if (bgp_fs_nlri_validate(pnt, psize) < 0) { flog_err( EC_BGP_FLOWSPEC_PACKET, "Bad flowspec format or NLRI options not supported"); - return -1; + return BGP_NLRI_PARSE_ERROR_FLOWSPEC_BAD_FORMAT; } p.family = AF_FLOWSPEC; p.prefixlen = 0; @@ -192,8 +192,8 @@ int bgp_nlri_parse_flowspec(struct peer *peer, struct attr *attr, flog_err(EC_BGP_FLOWSPEC_INSTALLATION, "Flowspec NLRI failed to be %s.", attr ? "added" : "withdrawn"); - return -1; + return BGP_NLRI_PARSE_ERROR; } } - return 0; + return BGP_NLRI_PARSE_OK; } diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c index 447d8da61..9e37a6018 100644 --- a/bgpd/bgp_fsm.c +++ b/bgpd/bgp_fsm.c @@ -1754,7 +1754,7 @@ static int bgp_fsm_exeption(struct peer *peer) return (bgp_stop(peer)); } -void bgp_fsm_nht_update(struct peer *peer, int valid) +void bgp_fsm_event_update(struct peer *peer, int valid) { if (!peer) return; @@ -1788,7 +1788,6 @@ void bgp_fsm_nht_update(struct peer *peer, int valid) } } - /* Finite State Machine structure */ static const struct { int (*func)(struct peer *); diff --git a/bgpd/bgp_fsm.h b/bgpd/bgp_fsm.h index d021c9884..3476a3c3a 100644 --- a/bgpd/bgp_fsm.h +++ b/bgpd/bgp_fsm.h @@ -57,7 +57,7 @@ #define FSM_PEER_TRANSITIONED 3 /* Prototypes. */ -extern void bgp_fsm_nht_update(struct peer *, int valid); +extern void bgp_fsm_event_update(struct peer *peer, int valid); extern int bgp_event(struct thread *); extern int bgp_event_update(struct peer *, int event); extern int bgp_stop(struct peer *peer); diff --git a/bgpd/bgp_label.c b/bgpd/bgp_label.c index a219c407d..951165084 100644 --- a/bgpd/bgp_label.c +++ b/bgpd/bgp_label.c @@ -355,7 +355,7 @@ int bgp_nlri_parse_label(struct peer *peer, struct attr *attr, /* When packet overflow occurs return immediately. */ if (pnt + BGP_ADDPATH_ID_LEN > lim) - return -1; + return BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW; addpath_id = ntohl(*((uint32_t *)pnt)); pnt += BGP_ADDPATH_ID_LEN; @@ -372,7 +372,7 @@ int bgp_nlri_parse_label(struct peer *peer, struct attr *attr, EC_BGP_UPDATE_RCV, "%s [Error] Update packet error / L-U (prefix length %d exceeds packet size %u)", peer->host, prefixlen, (uint)(lim - pnt)); - return -1; + return BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW; } /* Fill in the labels */ @@ -387,12 +387,12 @@ int bgp_nlri_parse_label(struct peer *peer, struct attr *attr, peer->host, prefixlen); bgp_notify_send(peer, BGP_NOTIFY_UPDATE_ERR, BGP_NOTIFY_UPDATE_INVAL_NETWORK); - return -1; + return BGP_NLRI_PARSE_ERROR_LABEL_LENGTH; } if ((afi == AFI_IP && p.prefixlen > 32) || (afi == AFI_IP6 && p.prefixlen > 128)) - return -1; + return BGP_NLRI_PARSE_ERROR_PREFIX_LENGTH; /* Fetch prefix from NLRI packet */ memcpy(&p.u.prefix, pnt + llen, psize - llen); @@ -463,8 +463,8 @@ int bgp_nlri_parse_label(struct peer *peer, struct attr *attr, EC_BGP_UPDATE_RCV, "%s [Error] Update packet error / L-U (%zu data remaining after parsing)", peer->host, lim - pnt); - return -1; + return BGP_NLRI_PARSE_ERROR_PACKET_LENGTH; } - return 0; + return BGP_NLRI_PARSE_OK; } diff --git a/bgpd/bgp_main.c b/bgpd/bgp_main.c index e42bc4411..adba73e40 100644 --- a/bgpd/bgp_main.c +++ b/bgpd/bgp_main.c @@ -78,6 +78,7 @@ static const struct option longopts[] = { {"skip_runas", no_argument, NULL, 'S'}, {"ecmp", required_argument, NULL, 'e'}, {"int_num", required_argument, NULL, 'I'}, + {"no_zebra", no_argument, NULL, 'Z'}, {0}}; /* signal definitions */ @@ -387,7 +388,7 @@ int main(int argc, char **argv) frr_preinit(&bgpd_di, argc, argv); frr_opt_add( - "p:l:Sne:I:" DEPRECATED_OPTIONS, longopts, + "p:l:SnZe:I:" DEPRECATED_OPTIONS, longopts, " -p, --bgp_port Set BGP listen port number (0 means do not listen).\n" " -l, --listenon Listen on specified address (implies -n)\n" " -n, --no_kernel Do not install route to kernel.\n" diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c index 4c4659ad5..d7cb84c32 100644 --- a/bgpd/bgp_mplsvpn.c +++ b/bgpd/bgp_mplsvpn.c @@ -140,7 +140,7 @@ int bgp_nlri_parse_vpn(struct peer *peer, struct attr *attr, /* When packet overflow occurs return immediately. */ if (pnt + BGP_ADDPATH_ID_LEN > lim) - return -1; + return BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW; addpath_id = ntohl(*((uint32_t *)pnt)); pnt += BGP_ADDPATH_ID_LEN; @@ -156,7 +156,7 @@ int bgp_nlri_parse_vpn(struct peer *peer, struct attr *attr, EC_BGP_UPDATE_RCV, "%s [Error] Update packet error / VPN (prefix length %d less than VPN min length)", peer->host, prefixlen); - return -1; + return BGP_NLRI_PARSE_ERROR_PREFIX_LENGTH; } /* sanity check against packet data */ @@ -165,7 +165,7 @@ int bgp_nlri_parse_vpn(struct peer *peer, struct attr *attr, EC_BGP_UPDATE_RCV, "%s [Error] Update packet error / VPN (prefix length %d exceeds packet size %u)", peer->host, prefixlen, (uint)(lim - pnt)); - return -1; + return BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW; } /* sanity check against storage for the IP address portion */ @@ -176,7 +176,7 @@ int bgp_nlri_parse_vpn(struct peer *peer, struct attr *attr, peer->host, prefixlen - VPN_PREFIXLEN_MIN_BYTES * 8, sizeof(p.u)); - return -1; + return BGP_NLRI_PARSE_ERROR_PACKET_LENGTH; } /* Sanity check against max bitlen of the address family */ @@ -187,7 +187,7 @@ int bgp_nlri_parse_vpn(struct peer *peer, struct attr *attr, peer->host, prefixlen - VPN_PREFIXLEN_MIN_BYTES * 8, p.family, prefix_blen(&p)); - return -1; + return BGP_NLRI_PARSE_ERROR_PACKET_LENGTH; } /* Copy label to prefix. */ @@ -245,7 +245,7 @@ int bgp_nlri_parse_vpn(struct peer *peer, struct attr *attr, EC_BGP_UPDATE_RCV, "%s [Error] Update packet error / VPN (%zu data remaining after parsing)", peer->host, lim - pnt); - return -1; + return BGP_NLRI_PARSE_ERROR_PACKET_LENGTH; } return 0; diff --git a/bgpd/bgp_nht.c b/bgpd/bgp_nht.c index 6e85abc8d..7e721db49 100644 --- a/bgpd/bgp_nht.c +++ b/bgpd/bgp_nht.c @@ -793,7 +793,7 @@ static void evaluate_paths(struct bgp_nexthop_cache *bnc) if (BGP_DEBUG(nht, NHT)) zlog_debug("%s: Updating peer (%s) status with NHT", __FUNCTION__, peer->host); - bgp_fsm_nht_update(peer, bgp_isvalid_nexthop(bnc)); + bgp_fsm_event_update(peer, bgp_isvalid_nexthop(bnc)); SET_FLAG(bnc->flags, BGP_NEXTHOP_PEER_NOTIFIED); } diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c index 7b76d7e83..130e06a6c 100644 --- a/bgpd/bgp_packet.c +++ b/bgpd/bgp_packet.c @@ -308,7 +308,7 @@ int bgp_nlri_parse(struct peer *peer, struct attr *attr, case SAFI_FLOWSPEC: return bgp_nlri_parse_flowspec(peer, attr, packet, mp_withdraw); } - return -1; + return BGP_NLRI_PARSE_ERROR; } /* @@ -1568,10 +1568,11 @@ static int bgp_update_receive(struct peer *peer, bgp_size_t size) nlri_ret = bgp_nlri_parse(peer, &attr, &nlris[i], 1); break; default: - nlri_ret = -1; + nlri_ret = BGP_NLRI_PARSE_ERROR; } - if (nlri_ret < 0) { + if (nlri_ret < BGP_NLRI_PARSE_OK + && nlri_ret != BGP_NLRI_PARSE_ERROR_PREFIX_OVERFLOW) { flog_err(EC_BGP_UPDATE_RCV, "%s [Error] Error parsing NLRI", peer->host); if (peer->status == Established) diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 7036ededa..fc6798fdf 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -4218,6 +4218,13 @@ static void bgp_cleanup_table(struct bgp *bgp, struct bgp_table *table, for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) for (pi = bgp_node_get_bgp_path_info(rn); pi; pi = next) { next = pi->next; + + /* Unimport EVPN routes from VRFs */ + if (safi == SAFI_EVPN) + bgp_evpn_unimport_route(bgp, AFI_L2VPN, + SAFI_EVPN, + &rn->p, pi); + if (CHECK_FLAG(pi->flags, BGP_PATH_SELECTED) && pi->type == ZEBRA_ROUTE_BGP && (pi->sub_type == BGP_ROUTE_NORMAL @@ -4333,7 +4340,7 @@ int bgp_nlri_parse_ip(struct peer *peer, struct attr *attr, /* When packet overflow occurs return immediately. */ if (pnt + BGP_ADDPATH_ID_LEN > lim) - return -1; + return BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW; addpath_id = ntohl(*((uint32_t *)pnt)); pnt += BGP_ADDPATH_ID_LEN; @@ -4351,7 +4358,7 @@ int bgp_nlri_parse_ip(struct peer *peer, struct attr *attr, EC_BGP_UPDATE_RCV, "%s [Error] Update packet error (wrong prefix length %d for afi %u)", peer->host, p.prefixlen, packet->afi); - return -1; + return BGP_NLRI_PARSE_ERROR_PREFIX_LENGTH; } /* Packet size overflow check. */ @@ -4363,7 +4370,7 @@ int bgp_nlri_parse_ip(struct peer *peer, struct attr *attr, EC_BGP_UPDATE_RCV, "%s [Error] Update packet error (prefix length %d overflows packet)", peer->host, p.prefixlen); - return -1; + return BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW; } /* Defensive coding, double-check the psize fits in a struct @@ -4373,7 +4380,7 @@ int bgp_nlri_parse_ip(struct peer *peer, struct attr *attr, EC_BGP_UPDATE_RCV, "%s [Error] Update packet error (prefix length %d too large for prefix storage %zu)", peer->host, p.prefixlen, sizeof(p.u)); - return -1; + return BGP_NLRI_PARSE_ERROR_PACKET_LENGTH; } /* Fetch prefix from NLRI packet. */ @@ -4438,10 +4445,14 @@ int bgp_nlri_parse_ip(struct peer *peer, struct attr *attr, BGP_ROUTE_NORMAL, NULL, NULL, 0, NULL); - /* Address family configuration mismatch or maximum-prefix count - overflow. */ + /* Do not send BGP notification twice when maximum-prefix count + * overflow. */ + if (CHECK_FLAG(peer->sflags, PEER_STATUS_PREFIX_OVERFLOW)) + return BGP_NLRI_PARSE_ERROR_PREFIX_OVERFLOW; + + /* Address family configuration mismatch. */ if (ret < 0) - return -1; + return BGP_NLRI_PARSE_ERROR_ADDRESS_FAMILY; } /* Packet length consistency check. */ @@ -4450,10 +4461,10 @@ int bgp_nlri_parse_ip(struct peer *peer, struct attr *attr, EC_BGP_UPDATE_RCV, "%s [Error] Update packet error (prefix length mismatch with total length)", peer->host); - return -1; + return BGP_NLRI_PARSE_ERROR_PACKET_LENGTH; } - return 0; + return BGP_NLRI_PARSE_OK; } static struct bgp_static *bgp_static_new(void) @@ -9001,6 +9012,7 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi, unsigned long i; for (i = 0; i < *json_header_depth; ++i) vty_out(vty, " } "); + vty_out(vty, "\n"); } } else { if (is_last) { @@ -9927,7 +9939,7 @@ DEFUN (show_ip_bgp_regexp, BGP_AFI_HELP_STR BGP_SAFI_WITH_LABEL_HELP_STR "Display routes matching the AS path regular expression\n" - "A regular-expression to match the BGP AS paths\n") + "A regular-expression (1234567890_^|[,{}() ]$*+.?-\\) to match the BGP AS paths\n") { afi_t afi = AFI_IP6; safi_t safi = SAFI_UNICAST; @@ -9985,6 +9997,12 @@ static int bgp_show_regexp(struct vty *vty, struct bgp *bgp, const char *regstr, regex_t *regex; int rc; + if (!config_bgp_aspath_validate(regstr)) { + vty_out(vty, "Invalid character in as-path access-list %s\n", + regstr); + return CMD_WARNING_CONFIG_FAILED; + } + regex = bgp_regcomp(regstr); if (!regex) { vty_out(vty, "Can't compile regexp %s\n", regstr); diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h index a559a6b58..7bbc14b46 100644 --- a/bgpd/bgp_route.h +++ b/bgpd/bgp_route.h @@ -73,6 +73,24 @@ enum bgp_show_adj_route_type { */ #define BGP_MAX_LABELS 2 +/* Error codes for handling NLRI */ +#define BGP_NLRI_PARSE_OK 0 +#define BGP_NLRI_PARSE_ERROR_PREFIX_OVERFLOW -1 +#define BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW -2 +#define BGP_NLRI_PARSE_ERROR_PREFIX_LENGTH -3 +#define BGP_NLRI_PARSE_ERROR_PACKET_LENGTH -4 +#define BGP_NLRI_PARSE_ERROR_LABEL_LENGTH -5 +#define BGP_NLRI_PARSE_ERROR_EVPN_MISSING_TYPE -6 +#define BGP_NLRI_PARSE_ERROR_EVPN_TYPE2_SIZE -7 +#define BGP_NLRI_PARSE_ERROR_EVPN_TYPE3_SIZE -8 +#define BGP_NLRI_PARSE_ERROR_EVPN_TYPE4_SIZE -9 +#define BGP_NLRI_PARSE_ERROR_EVPN_TYPE5_SIZE -10 +#define BGP_NLRI_PARSE_ERROR_FLOWSPEC_IPV6_NOT_SUPPORTED -11 +#define BGP_NLRI_PARSE_ERROR_FLOWSPEC_NLRI_SIZELIMIT -12 +#define BGP_NLRI_PARSE_ERROR_FLOWSPEC_BAD_FORMAT -13 +#define BGP_NLRI_PARSE_ERROR_ADDRESS_FAMILY -14 +#define BGP_NLRI_PARSE_ERROR -32 + /* Ancillary information to struct bgp_path_info, * used for uncommonly used data (aggregation, MPLS, etc.) * and lazily allocated to save memory. diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 9004926de..01144f5c7 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -3943,6 +3943,13 @@ ALIAS_HIDDEN(neighbor_nexthop_self_force, "Disable the next hop calculation for this neighbor\n" "Set the next hop to self for reflected routes\n") +ALIAS_HIDDEN(neighbor_nexthop_self_force, + neighbor_nexthop_self_all_hidden_cmd, + "neighbor <A.B.C.D|X:X::X:X|WORD> next-hop-self all", + NEIGHBOR_STR NEIGHBOR_ADDR_STR2 + "Disable the next hop calculation for this neighbor\n" + "Set the next hop to self for reflected routes\n") + DEFUN (no_neighbor_nexthop_self, no_neighbor_nexthop_self_cmd, "no neighbor <A.B.C.D|X:X::X:X|WORD> next-hop-self", @@ -3984,6 +3991,13 @@ ALIAS_HIDDEN(no_neighbor_nexthop_self_force, "Disable the next hop calculation for this neighbor\n" "Set the next hop to self for reflected routes\n") +ALIAS_HIDDEN(no_neighbor_nexthop_self_force, + no_neighbor_nexthop_self_all_hidden_cmd, + "no neighbor <A.B.C.D|X:X::X:X|WORD> next-hop-self all", + NO_STR NEIGHBOR_STR NEIGHBOR_ADDR_STR2 + "Disable the next hop calculation for this neighbor\n" + "Set the next hop to self for reflected routes\n") + /* neighbor as-override */ DEFUN (neighbor_as_override, neighbor_as_override_cmd, @@ -13234,6 +13248,8 @@ void bgp_vty_init(void) /* "neighbor next-hop-self force" commands. */ install_element(BGP_NODE, &neighbor_nexthop_self_force_hidden_cmd); install_element(BGP_NODE, &no_neighbor_nexthop_self_force_hidden_cmd); + install_element(BGP_NODE, &neighbor_nexthop_self_all_hidden_cmd); + install_element(BGP_NODE, &no_neighbor_nexthop_self_all_hidden_cmd); install_element(BGP_IPV4_NODE, &neighbor_nexthop_self_force_cmd); install_element(BGP_IPV4_NODE, &no_neighbor_nexthop_self_force_cmd); install_element(BGP_IPV4M_NODE, &neighbor_nexthop_self_force_cmd); diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index 55ecc5f16..e42d6ee26 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -2553,12 +2553,14 @@ static int bgp_zebra_process_local_vni(int command, struct zclient *zclient, struct bgp *bgp; struct in_addr vtep_ip = {INADDR_ANY}; vrf_id_t tenant_vrf_id = VRF_DEFAULT; + struct in_addr mcast_grp = {INADDR_ANY}; s = zclient->ibuf; vni = stream_getl(s); if (command == ZEBRA_VNI_ADD) { vtep_ip.s_addr = stream_get_ipv4(s); stream_get(&tenant_vrf_id, s, sizeof(vrf_id_t)); + mcast_grp.s_addr = stream_get_ipv4(s); } bgp = bgp_lookup_by_vrf_id(vrf_id); @@ -2574,7 +2576,7 @@ static int bgp_zebra_process_local_vni(int command, struct zclient *zclient, if (command == ZEBRA_VNI_ADD) return bgp_evpn_local_vni_add( bgp, vni, vtep_ip.s_addr ? vtep_ip : bgp->router_id, - tenant_vrf_id); + tenant_vrf_id, mcast_grp); else return bgp_evpn_local_vni_del(bgp, vni); } diff --git a/doc/developer/ospf-api.rst b/doc/developer/ospf-api.rst index f4f38a63e..41c31b29c 100644 --- a/doc/developer/ospf-api.rst +++ b/doc/developer/ospf-api.rst @@ -4,76 +4,76 @@ OSPF API Documentation Disclaimer ---------- -The OSPF daemon contains an API for application access to the LSA -database. This API was created by Ralph Keller, originally as patch for -Zebra. Unfortunately, the page containing documentation of the API is no -longer online. This page is an attempt to recreate documentation for the -API (with lots of help of the WayBackMachine) +The OSPF daemon contains an API for application access to the LSA database. +This API and documentation was created by Ralph Keller, originally as patch for +Zebra. Unfortunately, the page containing documentation for the API is no +longer online. This page is an attempt to recreate documentation for the API +(with lots of help from the WayBackMachine). + +Ralph has kindly licensed this documentation under GPLv2+. Please preserve the +acknowledgements at the bottom of this document. Introduction ------------ -This page describes an API that allows external applications to access -the link-state database (LSDB) of the OSPF daemon. The implementation is -based on the OSPF code from FRRouting (forked from Quagga and formerly -Zebra) routing protocol suite and is subject to the GNU General Public -License. The OSPF API provides you with the following functionality: - -- Retrieval of the full or partial link-state database of the OSPF - daemon. This allows applications to obtain an exact copy of the LSDB - including router LSAs, network LSAs and so on. Whenever a new LSA - arrives at the OSPF daemon, the API module immediately informs the - application by sending a message. This way, the application is always - synchronized with the LSDB of the OSPF daemon. -- Origination of own opaque LSAs (of type 9, 10, or 11) which are then - distributed transparently to other routers within the flooding scope - and received by other applications through the OSPF API. - -Opaque LSAs, which are described in RFC 2370 , allow you to distribute -application-specific information within a network using the OSPF -protocol. The information contained in opaque LSAs is transparent for -the routing process but it can be processed by other modules such as -traffic engineering (e.g., MPLS-TE). +This page describes an API that allows external applications to access the +link-state database (LSDB) of the OSPF daemon. The implementation is based on +the OSPF code from FRRouting (forked from Quagga and formerly Zebra) routing +protocol suite and is subject to the GNU General Public License. The OSPF API +provides you with the following functionality: + +- Retrieval of the full or partial link-state database of the OSPF daemon. + This allows applications to obtain an exact copy of the LSDB including router + LSAs, network LSAs and so on. Whenever a new LSA arrives at the OSPF daemon, + the API module immediately informs the application by sending a message. This + way, the application is always synchronized with the LSDB of the OSPF daemon. +- Origination of own opaque LSAs (of type 9, 10, or 11) which are then + distributed transparently to other routers within the flooding scope and + received by other applications through the OSPF API. + +Opaque LSAs, which are described in :rfc:`2370`, allow you to distribute +application-specific information within a network using the OSPF protocol. The +information contained in opaque LSAs is transparent for the routing process but +it can be processed by other modules such as traffic engineering (e.g., +MPLS-TE). Architecture ------------ -The following picture depicts the architecture of the Quagga/Zebra -protocol suite. The OSPF daemon is extended with opaque LSA capabilities -and an API for external applications. The OSPF core module executes the -OSPF protocol by discovering neighbors and exchanging neighbor state. -The opaque module, implemented by Masahiko Endo, provides functions to -exchange opaque LSAs between routers. Opaque LSAs can be generated by -several modules such as the MPLS-TE module or the API server module. -These modules then invoke the opaque module to flood their data to -neighbors within the flooding scope. - -The client, which is an application potentially running on a different -node than the OSPF daemon, links against the OSPF API client library. -This client library establishes a socket connection with the API server -module of the OSPF daemon and uses this connection to retrieve LSAs and -originate opaque LSAs. +The following picture depicts the architecture of the Quagga/Zebra protocol +suite. The OSPF daemon is extended with opaque LSA capabilities and an API for +external applications. The OSPF core module executes the OSPF protocol by +discovering neighbors and exchanging neighbor state. The opaque module, +implemented by Masahiko Endo, provides functions to exchange opaque LSAs +between routers. Opaque LSAs can be generated by several modules such as the +MPLS-TE module or the API server module. These modules then invoke the opaque +module to flood their data to neighbors within the flooding scope. + +The client, which is an application potentially running on a different node +than the OSPF daemon, links against the OSPF API client library. This client +library establishes a socket connection with the API server module of the OSPF +daemon and uses this connection to retrieve LSAs and originate opaque LSAs. .. figure:: ../figures/ospf_api_architecture.png :alt: image image -The OSPF API server module works like any other internal opaque module -(such as the MPLS-TE module), but listens to connections from external -applications that want to communicate with the OSPF daemon. The API -server module can handle multiple clients concurrently. +The OSPF API server module works like any other internal opaque module (such as +the MPLS-TE module), but listens to connections from external applications that +want to communicate with the OSPF daemon. The API server module can handle +multiple clients concurrently. -One of the main objectives of the implementation is to make as little -changes to the existing Zebra code as possible. +One of the main objectives of the implementation is to make as little changes +to the existing Zebra code as possible. Installation & Configuration ---------------------------- -Download FRRouting and unpack +Download FRRouting and unpack it. -Configure your frr version (note that --enable-opaque-lsa also enables -the ospfapi server and ospfclient). +Configure and build FRR (note that ``--enable-opaque-lsa`` also enables the +ospfapi server and ospfclient). :: @@ -83,8 +83,8 @@ the ospfapi server and ospfclient). This should also compile the client library and sample application in ospfclient. -Make sure that you have enabled opaque LSAs in your configuration. Add -the ospf opaque-lsa statement to your ospfd.conf: +Make sure that you have enabled opaque LSAs in your configuration. Add the +``ospf opaque-lsa`` statement to your :file:`ospfd.conf`: :: @@ -108,171 +108,165 @@ Usage ----- In the following we describe how you can use the sample application to -originate opaque LSAs. The sample application first registers with the -OSPF daemon the opaque type it wants to inject and then waits until the -OSPF daemon is ready to accept opaque LSAs of that type. Then the client -application originates an opaque LSA, waits 10 seconds and then updates -the opaque LSA with new opaque data. After another 20 seconds, the -client application deletes the opaque LSA from the LSDB. If the clients -terminates unexpectedly, the OSPF API module will remove all the opaque -LSAs that the application registered. Since the opaque LSAs are flooded -to other routers, we will see the opaque LSAs in all routers according -to the flooding scope of the opaque LSA. +originate opaque LSAs. The sample application first registers with the OSPF +daemon the opaque type it wants to inject and then waits until the OSPF daemon +is ready to accept opaque LSAs of that type. Then the client application +originates an opaque LSA, waits 10 seconds and then updates the opaque LSA with +new opaque data. After another 20 seconds, the client application deletes the +opaque LSA from the LSDB. If the clients terminates unexpectedly, the OSPF API +module will remove all the opaque LSAs that the application registered. Since +the opaque LSAs are flooded to other routers, we will see the opaque LSAs in +all routers according to the flooding scope of the opaque LSA. We have a very simple demo setup, just two routers connected with an ATM -point-to-point link. Start the modified OSPF daemons on two adjacent -routers. First run on msr2: +point-to-point link. Start the modified OSPF daemons on two adjacent routers. +First run on msr2: -:: +.. code-block:: console - > msr2:/home/keller/ospfapi/zebra/ospfd# ./ospfd -f /usr/local/etc/ospfd.conf + # ./ospfd --apiserver -f /usr/local/etc/ospfd.conf And on the neighboring router msr3: -:: +.. code-block:: console - > msr3:/home/keller/ospfapi/zebra/ospfd# ./ospfd -f /usr/local/etc/ospfd.conf + # ./ospfd --apiserver -f /usr/local/etc/ospfd.conf Now the two routers form adjacency and start exchanging their databases. Looking at the OSPF daemon of msr2 (or msr3), you see this: -:: +.. code-block:: console - ospfd> show ip ospf database + ospfd> show ip ospf database - OSPF Router with ID (10.0.0.1) + OSPF Router with ID (10.0.0.1) - Router Link States (Area 0.0.0.1) + Router Link States (Area 0.0.0.1) - Link ID ADV Router Age Seq# CkSum Link count - 10.0.0.1 10.0.0.1 55 0x80000003 0xc62f 2 - 10.0.0.2 10.0.0.2 55 0x80000003 0xe3e4 3 + Link ID ADV Router Age Seq# CkSum Link count + 10.0.0.1 10.0.0.1 55 0x80000003 0xc62f 2 + 10.0.0.2 10.0.0.2 55 0x80000003 0xe3e4 3 - Net Link States (Area 0.0.0.1) + Net Link States (Area 0.0.0.1) - Link ID ADV Router Age Seq# CkSum - 10.0.0.2 10.0.0.2 60 0x80000001 0x5fcb + Link ID ADV Router Age Seq# CkSum + 10.0.0.2 10.0.0.2 60 0x80000001 0x5fcb Now we start the sample main application that originates an opaque LSA. -:: +.. code-block:: console - > cd ospfapi/apiclient - > ./main msr2 10 250 20 0.0.0.0 0.0.0.1 + # cd ospfapi/apiclient + # ./main msr2 10 250 20 0.0.0.0 0.0.0.1 -This originates an opaque LSA of type 10 (area local), with opaque type -250 (experimental), opaque id of 20 (chosen arbitrarily), interface -address 0.0.0.0 (which is used only for opaque LSAs type 9), and area -0.0.0.1 +This originates an opaque LSA of type 10 (area local), with opaque type 250 +(experimental), opaque id of 20 (chosen arbitrarily), interface address 0.0.0.0 +(which is used only for opaque LSAs type 9), and area 0.0.0.1 Again looking at the OSPF database you see: -:: +.. code-block:: console - ospfd> show ip ospf database + ospfd> show ip ospf database - OSPF Router with ID (10.0.0.1) + OSPF Router with ID (10.0.0.1) - Router Link States (Area 0.0.0.1) + Router Link States (Area 0.0.0.1) - Link ID ADV Router Age Seq# CkSum Link count - 10.0.0.1 10.0.0.1 437 0x80000003 0xc62f 2 - 10.0.0.2 10.0.0.2 437 0x80000003 0xe3e4 3 + Link ID ADV Router Age Seq# CkSum Link count + 10.0.0.1 10.0.0.1 437 0x80000003 0xc62f 2 + 10.0.0.2 10.0.0.2 437 0x80000003 0xe3e4 3 - Net Link States (Area 0.0.0.1) + Net Link States (Area 0.0.0.1) - Link ID ADV Router Age Seq# CkSum - 10.0.0.2 10.0.0.2 442 0x80000001 0x5fcb + Link ID ADV Router Age Seq# CkSum + 10.0.0.2 10.0.0.2 442 0x80000001 0x5fcb - Area-Local Opaque-LSA (Area 0.0.0.1) + Area-Local Opaque-LSA (Area 0.0.0.1) - Opaque-Type/Id ADV Router Age Seq# CkSum - 250.0.0.20 10.0.0.1 0 0x80000001 0x58a6 <=== opaque LSA + Opaque-Type/Id ADV Router Age Seq# CkSum + 250.0.0.20 10.0.0.1 0 0x80000001 0x58a6 <=== opaque LSA You can take a closer look at this opaque LSA: -:: +.. code-block:: console - ospfd> show ip ospf database opaque-area + ospfd> show ip ospf database opaque-area - OSPF Router with ID (10.0.0.1) + OSPF Router with ID (10.0.0.1) - Area-Local Opaque-LSA (Area 0.0.0.1) + Area-Local Opaque-LSA (Area 0.0.0.1) - LS age: 4 - Options: 66 - LS Type: Area-Local Opaque-LSA - Link State ID: 250.0.0.20 (Area-Local Opaque-Type/ID) - Advertising Router: 10.0.0.1 - LS Seq Number: 80000001 - Checksum: 0x58a6 - Length: 24 - Opaque-Type 250 (Private/Experimental) - Opaque-ID 0x14 - Opaque-Info: 4 octets of data - Added using OSPF API: 4 octets of opaque data - Opaque data: 1 0 0 0 <==== counter is 1 + LS age: 4 + Options: 66 + LS Type: Area-Local Opaque-LSA + Link State ID: 250.0.0.20 (Area-Local Opaque-Type/ID) + Advertising Router: 10.0.0.1 + LS Seq Number: 80000001 + Checksum: 0x58a6 + Length: 24 + Opaque-Type 250 (Private/Experimental) + Opaque-ID 0x14 + Opaque-Info: 4 octets of data + Added using OSPF API: 4 octets of opaque data + Opaque data: 1 0 0 0 <==== counter is 1 -Note that the main application updates the opaque LSA after 10 seconds, -then it looks as follows: +Note that the main application updates the opaque LSA after 10 seconds, then it +looks as follows: -:: +.. code-block:: console - ospfd> show ip ospf database opaque-area + ospfd> show ip ospf database opaque-area - OSPF Router with ID (10.0.0.1) + OSPF Router with ID (10.0.0.1) - Area-Local Opaque-LSA (Area 0.0.0.1) + Area-Local Opaque-LSA (Area 0.0.0.1) - LS age: 1 - Options: 66 - LS Type: Area-Local Opaque-LSA - Link State ID: 250.0.0.20 (Area-Local Opaque-Type/ID) - Advertising Router: 10.0.0.1 - LS Seq Number: 80000002 - Checksum: 0x59a3 - Length: 24 - Opaque-Type 250 (Private/Experimental) - Opaque-ID 0x14 - Opaque-Info: 4 octets of data - Added using OSPF API: 4 octets of opaque data - Opaque data: 2 0 0 0 <==== counter is now 2 + LS age: 1 + Options: 66 + LS Type: Area-Local Opaque-LSA + Link State ID: 250.0.0.20 (Area-Local Opaque-Type/ID) + Advertising Router: 10.0.0.1 + LS Seq Number: 80000002 + Checksum: 0x59a3 + Length: 24 + Opaque-Type 250 (Private/Experimental) + Opaque-ID 0x14 + Opaque-Info: 4 octets of data + Added using OSPF API: 4 octets of opaque data + Opaque data: 2 0 0 0 <==== counter is now 2 -Note that the payload of the opaque LSA has changed as you can see -above. +Note that the payload of the opaque LSA has changed as you can see above. -Then, again after another 20 seconds, the opaque LSA is flushed from the -LSDB. +Then, again after another 20 seconds, the opaque LSA is flushed from the LSDB. Important note: ^^^^^^^^^^^^^^^ In order to originate an opaque LSA, there must be at least one active -opaque-capable neighbor. Thus, you cannot originate opaque LSAs of no -neighbors are present. If you try to originate even so no neighbor is -ready, you will receive a not ready error message. The reason for this -restriction is that it might be possible that some routers have an -identical opaque LSA from a previous origination in their LSDB that -unfortunately could not be flushed due to a crash, and now if the router -comes up again and starts originating a new opaque LSA, the new opaque -LSA is considered older since it has a lower sequence number and is -ignored by other routers (that consider the stalled opaque LSA as more -recent). However, if the originating router first synchronizes the -database before originating opaque LSAs, it will detect the older opaque -LSA and can flush it first. +opaque-capable neighbor. Thus, you cannot originate opaque LSAs if no neighbors +are present. If you try to originate when no neighbors are ready, you will +receive a not ready error message. The reason for this restriction is that it +might be possible that some routers have an identical opaque LSA from a +previous origination in their LSDB that unfortunately could not be flushed due +to a crash, and now if the router comes up again and starts originating a new +opaque LSA, the new opaque LSA is considered older since it has a lower +sequence number and is ignored by other routers (that consider the stalled +opaque LSA as more recent). However, if the originating router first +synchronizes the database before originating opaque LSAs, it will detect the +older opaque LSA and can flush it first. Protocol and Message Formats ---------------------------- -If you are developing your own client application and you don't want to -make use of the client library (due to the GNU license restriction or -whatever reason), you can implement your own client-side message -handling. The OSPF API uses two connections between the client and the -OSPF API server: One connection is used for a synchronous request /reply -protocol and another connection is used for asynchronous notifications -(e.g., LSA update, neighbor status change). +If you are developing your own client application and you don't want to make +use of the client library (due to the GNU license restriction or whatever +reason), you can implement your own client-side message handling. The OSPF API +uses two connections between the client and the OSPF API server: One connection +is used for a synchronous request /reply protocol and another connection is +used for asynchronous notifications (e.g., LSA update, neighbor status change). Each message begins with the following header: @@ -326,8 +320,8 @@ The synchronous requests and replies have the following message formats: image -The origin field allows to select according to the following types of -origins: +The origin field allows origin-based filtering using the following origin +types: +-------------------------+---------+ | Origin | Value | @@ -339,7 +333,7 @@ origins: | ANY\_ORIGIN | 2 | +-------------------------+---------+ -The reply message has on of the following error codes: +The reply message has one of the following error codes: +--------------------------+---------+ | Error code | Value | @@ -372,16 +366,18 @@ The asynchronous notifications have the following message formats: image + +.. Do not delete these acknowledgements! + Original Acknowledgments from Ralph Keller ------------------------------------------ -I would like to thank Masahiko Endo, the author of the opaque LSA -extension module, for his great support. His wonderful ASCII graphs -explaining the internal workings of this code, and his invaluable input -proved to be crucial in designing a useful API for accessing the link -state database of the OSPF daemon. Once, he even decided to take the -plane from Tokyo to Zurich so that we could actually meet and have -face-to-face discussions, which was a lot of fun. Clearly, without -Masahiko no API would ever be completed. I also would like to thank -Daniel Bauer who wrote an opaque LSA implementation too and was willing +I would like to thank Masahiko Endo, the author of the opaque LSA extension +module, for his great support. His wonderful ASCII graphs explaining the +internal workings of this code, and his invaluable input proved to be crucial +in designing a useful API for accessing the link state database of the OSPF +daemon. Once, he even decided to take the plane from Tokyo to Zurich so that we +could actually meet and have face-to-face discussions, which was a lot of fun. +Clearly, without Masahiko no API would ever be completed. I also would like to +thank Daniel Bauer who wrote an opaque LSA implementation too and was willing to test the OSPF API code in one of his projects. diff --git a/doc/manpages/ospfd.rst b/doc/manpages/ospfd.rst index 6e4d093f6..951a0229a 100644 --- a/doc/manpages/ospfd.rst +++ b/doc/manpages/ospfd.rst @@ -22,6 +22,10 @@ OPTIONS available for the |DAEMON| command: .. include:: common-options.rst +.. option:: -a, --apiserver + + Enable the OSPF API server. + FILES ===== diff --git a/doc/user/ospfd.rst b/doc/user/ospfd.rst index ad0a8639a..83e14d474 100644 --- a/doc/user/ospfd.rst +++ b/doc/user/ospfd.rst @@ -26,7 +26,7 @@ Configuring OSPF .. option:: -a, --apiserver - Enable the OSPF API server + Enable the OSPF API server. This is required to use ``ospfclient``. *ospfd* must acquire interface information from *zebra* in order to function. Therefore *zebra* must be running before invoking *ospfd*. Also, if *zebra* is diff --git a/doc/user/overview.rst b/doc/user/overview.rst index 6aa946229..7f8ea392d 100644 --- a/doc/user/overview.rst +++ b/doc/user/overview.rst @@ -228,6 +228,8 @@ features with system dependencies are included here. +-----------------------------------+----------------+--------------+------------+------------+------------+ | ASM (Any Source) | :mark:`Y` | :mark:`N` | :mark:`N` | :mark:`N` | :mark:`N` | +-----------------------------------+----------------+--------------+------------+------------+------------+ +| EVPN BUM Forwarding | :mark:`≥5.0` | :mark:`N` | :mark:`N` | :mark:`N` | :mark:`N` | ++-----------------------------------+----------------+--------------+------------+------------+------------+ The indicators have the following semantics: diff --git a/doc/user/pim.rst b/doc/user/pim.rst index f4611c520..5148d3baf 100644 --- a/doc/user/pim.rst +++ b/doc/user/pim.rst @@ -8,7 +8,8 @@ PIM -- Protocol Independent Multicast *pimd* supports pim-sm as well as igmp v2 and v3. pim is vrf aware and can work within the context of vrf's in order to -do S,G mrouting. +do S,G mrouting. Additionally PIM can be used in the EVPN underlay +network for optimizing forwarding of overlay BUM traffic. .. _starting-and-stopping-pimd: @@ -292,10 +293,14 @@ cause great confusion. Display various information about the interfaces used in this pim instance. -.. index:: show ip mroute -.. clicmd:: show ip mroute +.. index:: show ip mroute [vrf NAME] [A.B.C.D [A.B.C.D]] [fill] [json] +.. clicmd:: show ip mroute [vrf NAME] [A.B.C.D [A.B.C.D]] [fill] [json] - Display information about installed into the kernel S,G mroutes. + Display information about installed into the kernel S,G mroutes. If + one address is specified we assume it is the Group we are interested + in displaying data on. If the second address is specified then it is + Source Group. The keyword `fill` says to fill in all assumed data + for test/data gathering purposes. .. index:: show ip mroute count .. clicmd:: show ip mroute count @@ -333,10 +338,12 @@ cause great confusion. Display information about interfaces PIM is using. -.. index:: show ip pim join +.. index:: show ip pim [vrf NAME] join [A.B.C.D [A.B.C.D]] [json] .. clicmd:: show ip pim join - Display information about PIM joins received. + Display information about PIM joins received. If one address is specified + then we assume it is the Group we are interested in displaying data on. + If the second address is specified then it is Source Group. .. index:: show ip pim local-membership .. clicmd:: show ip pim local-membership @@ -383,10 +390,11 @@ cause great confusion. Display information about known S,G's and incoming interface as well as the OIL and how they were chosen. -.. index:: show ip pim upstream +.. index:: show ip pim [vrf NAME] upstream [A.B.C.D [A.B.C.D]] [json] .. clicmd:: show ip pim upstream - Display upstream information about a S,G mroute. + Display upstream information about a S,G mroute. Allow the user to + specify sub Source and Groups that we are only interested in. .. index:: show ip pim upstream-join-desired .. clicmd:: show ip pim upstream-join-desired @@ -495,3 +503,28 @@ Clear commands reset various variables. .. clicmd:: clear ip pim oil Rescan PIM OIL (output interface list). + +PIM EVPN configuration +====================== +To use PIM in the underlay for overlay BUM forwarding associate a multicast +group with the L2 VNI. The actual configuration is based on your distribution. +Here is an ifupdown2 example:: + + auto vx-10100 + iface vx-10100 + vxlan-id 10100 + bridge-access 100 + vxlan-local-tunnelip 27.0.0.11 + vxlan-mcastgrp 239.1.1.100 + +.. note:: + + PIM will see the ``vxlan-mcastgrp`` configuration and auto configure state + to properly forward BUM traffic. + +PIM also needs to be configured in the underlay to allow the BUM MDT to be +setup. This is existing PIM configuration: + +- Enable pim on the underlay L3 interface via the "ip pim" command. +- Configure RPs for the BUM multicast group range. +- Ensure the PIM is enabled on the lo of the VTEPs and the RP. diff --git a/isisd/isis_circuit.h b/isisd/isis_circuit.h index e0ea4f78b..2371c0b73 100644 --- a/isisd/isis_circuit.h +++ b/isisd/isis_circuit.h @@ -121,8 +121,7 @@ struct isis_circuit { uint16_t psnp_interval[2]; /* psnp-interval in seconds */ uint8_t metric[2]; uint32_t te_metric[2]; - struct mpls_te_circuit - *mtc; /* Support for MPLS-TE parameters - see isis_te.[c,h] */ + struct mpls_te_circuit *mtc; /* MPLS-TE parameters */ int ip_router; /* Route IP ? */ int is_passive; /* Is Passive ? */ struct list *mt_settings; /* IS-IS MT Settings */ diff --git a/isisd/isis_cli.c b/isisd/isis_cli.c index 52f68eff5..009485282 100644 --- a/isisd/isis_cli.c +++ b/isisd/isis_cli.c @@ -136,8 +136,6 @@ DEFPY(ip_router_isis, ip_router_isis_cmd, "ip router isis WORD$tag", const char *circ_type; struct isis_area *area; struct interface *ifp; - const struct lyd_node *dnode = - yang_dnode_get(running_config->dnode, VTY_CURR_XPATH); /* area will be created if it is not present. make sure the yang model * is synced with FRR and call the appropriate NB cb. @@ -190,7 +188,7 @@ DEFPY(ip_router_isis, ip_router_isis_cmd, "ip router isis WORD$tag", } /* check if the interface is a loopback and if so set it as passive */ - ifp = yang_dnode_get_entry(dnode, false); + ifp = nb_running_get_entry(NULL, VTY_CURR_XPATH, false); if (ifp && if_is_loopback(ifp)) nb_cli_enqueue_change(vty, "./frr-isisd:isis/passive", NB_OP_MODIFY, "true"); @@ -208,8 +206,6 @@ DEFPY(ip6_router_isis, ip6_router_isis_cmd, "ipv6 router isis WORD$tag", const char *circ_type; struct isis_area *area; struct interface *ifp; - const struct lyd_node *dnode = - yang_dnode_get(running_config->dnode, VTY_CURR_XPATH); /* area will be created if it is not present. make sure the yang model * is synced with FRR and call the appropriate NB cb. @@ -262,7 +258,7 @@ DEFPY(ip6_router_isis, ip6_router_isis_cmd, "ipv6 router isis WORD$tag", } /* check if the interface is a loopback and if so set it as passive */ - ifp = yang_dnode_get_entry(dnode, false); + ifp = nb_running_get_entry(NULL, VTY_CURR_XPATH, false); if (ifp && if_is_loopback(ifp)) nb_cli_enqueue_change(vty, "./frr-isisd:isis/passive", NB_OP_MODIFY, "true"); @@ -373,9 +369,9 @@ DEFPY(no_is_type, no_is_type_cmd, "Act as an area router only\n") { const char *value = NULL; - const struct lyd_node *dnode = - yang_dnode_get(running_config->dnode, VTY_CURR_XPATH); - struct isis_area *area = yang_dnode_get_entry(dnode, false); + struct isis_area *area; + + area = nb_running_get_entry(NULL, VTY_CURR_XPATH, false); /* * Put the is-type back to defaults: @@ -932,12 +928,12 @@ void cli_show_isis_purge_origin(struct vty *vty, struct lyd_node *dnode, } /* - * XPath: /frr-isisd:isis/mpls-te + * XPath: /frr-isisd:isis/instance/mpls-te */ DEFPY(isis_mpls_te_on, isis_mpls_te_on_cmd, "mpls-te on", MPLS_TE_STR "Enable the MPLS-TE functionality\n") { - nb_cli_enqueue_change(vty, "/frr-isisd:isis/mpls-te", NB_OP_CREATE, + nb_cli_enqueue_change(vty, "./mpls-te", NB_OP_CREATE, NULL); return nb_cli_apply_changes(vty, NULL); @@ -946,9 +942,9 @@ DEFPY(isis_mpls_te_on, isis_mpls_te_on_cmd, "mpls-te on", DEFPY(no_isis_mpls_te_on, no_isis_mpls_te_on_cmd, "no mpls-te [on]", NO_STR "Disable the MPLS-TE functionality\n" - "Enable the MPLS-TE functionality\n") + "Disable the MPLS-TE functionality\n") { - nb_cli_enqueue_change(vty, "/frr-isisd:isis/mpls-te", NB_OP_DESTROY, + nb_cli_enqueue_change(vty, "./mpls-te", NB_OP_DESTROY, NULL); return nb_cli_apply_changes(vty, NULL); @@ -961,7 +957,7 @@ void cli_show_isis_mpls_te(struct vty *vty, struct lyd_node *dnode, } /* - * XPath: /frr-isisd:isis/mpls-te/router-address + * XPath: /frr-isisd:isis/instance/mpls-te/router-address */ DEFPY(isis_mpls_te_router_addr, isis_mpls_te_router_addr_cmd, "mpls-te router-address A.B.C.D", @@ -969,12 +965,24 @@ DEFPY(isis_mpls_te_router_addr, isis_mpls_te_router_addr_cmd, "Stable IP address of the advertising router\n" "MPLS-TE router address in IPv4 address format\n") { - nb_cli_enqueue_change(vty, "/frr-isisd:isis/mpls-te/router-address", + nb_cli_enqueue_change(vty, "./mpls-te/router-address", NB_OP_MODIFY, router_address_str); return nb_cli_apply_changes(vty, NULL); } +DEFPY(no_isis_mpls_te_router_addr, no_isis_mpls_te_router_addr_cmd, + "no mpls-te router-address [A.B.C.D]", + NO_STR MPLS_TE_STR + "Delete IP address of the advertising router\n" + "MPLS-TE router address in IPv4 address format\n") +{ + nb_cli_enqueue_change(vty, "./mpls-te/router-address", + NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + void cli_show_isis_mpls_te_router_addr(struct vty *vty, struct lyd_node *dnode, bool show_defaults) { @@ -990,7 +998,7 @@ DEFPY(isis_mpls_te_inter_as, isis_mpls_te_inter_as_cmd, "AREA native mode self originate INTER-AS LSP with L1 and L2 flooding scope\n" "AS native mode self originate INTER-AS LSP with L2 only flooding scope\n") { - vty_out(vty, "MPLS-TE Inter-AS is not yet supported."); + vty_out(vty, "MPLS-TE Inter-AS is not yet supported\n"); return CMD_SUCCESS; } @@ -1761,7 +1769,6 @@ DEFPY(no_isis_circuit_type, no_isis_circuit_type_cmd, "Level-1-2 adjacencies are formed\n" "Level-2 only adjacencies are formed\n") { - const struct lyd_node *dnode; struct interface *ifp; struct isis_circuit *circuit; int is_type; @@ -1772,8 +1779,7 @@ DEFPY(no_isis_circuit_type, no_isis_circuit_type_cmd, * and the is-type of the area if there is one. So we need to do this * here. */ - dnode = yang_dnode_get(running_config->dnode, VTY_CURR_XPATH); - ifp = yang_dnode_get_entry(dnode, false); + ifp = nb_running_get_entry(NULL, VTY_CURR_XPATH, false); if (!ifp) goto def_val; @@ -1973,6 +1979,7 @@ void isis_cli_init(void) install_element(ISIS_NODE, &isis_mpls_te_on_cmd); install_element(ISIS_NODE, &no_isis_mpls_te_on_cmd); install_element(ISIS_NODE, &isis_mpls_te_router_addr_cmd); + install_element(ISIS_NODE, &no_isis_mpls_te_router_addr_cmd); install_element(ISIS_NODE, &isis_mpls_te_inter_as_cmd); install_element(ISIS_NODE, &isis_default_originate_cmd); diff --git a/isisd/isis_lsp.c b/isisd/isis_lsp.c index a0be2d82f..199166695 100644 --- a/isisd/isis_lsp.c +++ b/isisd/isis_lsp.c @@ -1040,7 +1040,7 @@ static void lsp_build(struct isis_lsp *lsp, struct isis_area *area) uint8_t subtlvs[256]; uint8_t subtlv_len; - if (IS_MPLS_TE(isisMplsTE) + if (IS_MPLS_TE(area->mta) && circuit->interface && HAS_LINK_PARAMS( circuit->interface)) @@ -1082,7 +1082,7 @@ static void lsp_build(struct isis_lsp *lsp, struct isis_area *area) uint8_t subtlvs[256]; uint8_t subtlv_len; - if (IS_MPLS_TE(isisMplsTE) + if (IS_MPLS_TE(area->mta) && circuit->interface != NULL && HAS_LINK_PARAMS( circuit->interface)) diff --git a/isisd/isis_misc.c b/isisd/isis_misc.c index ef0d77d6c..0a42adea3 100644 --- a/isisd/isis_misc.c +++ b/isisd/isis_misc.c @@ -28,7 +28,6 @@ #include "hash.h" #include "if.h" #include "command.h" -#include "log_int.h" #include "isisd/isis_constants.h" #include "isisd/isis_common.h" diff --git a/isisd/isis_northbound.c b/isisd/isis_northbound.c index cbe5ba786..d5cdec154 100644 --- a/isisd/isis_northbound.c +++ b/isisd/isis_northbound.c @@ -66,7 +66,7 @@ static int isis_instance_create(enum nb_event event, area = isis_area_create(area_tag); /* save area in dnode to avoid looking it up all the time */ - yang_dnode_set_entry(dnode, area); + nb_running_set_entry(dnode, area); return NB_OK; } @@ -74,13 +74,13 @@ static int isis_instance_create(enum nb_event event, static int isis_instance_destroy(enum nb_event event, const struct lyd_node *dnode) { - const char *area_tag; + struct isis_area *area; if (event != NB_EV_APPLY) return NB_OK; - area_tag = yang_dnode_get_string(dnode, "./area-tag"); - isis_area_destroy(area_tag); + area = nb_running_unset_entry(dnode); + isis_area_destroy(area->area_tag); return NB_OK; } @@ -98,7 +98,7 @@ static int isis_instance_is_type_modify(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - area = yang_dnode_get_entry(dnode, true); + area = nb_running_get_entry(dnode, NULL, true); type = yang_dnode_get_enum(dnode, NULL); isis_area_is_type_set(area, type); @@ -149,7 +149,7 @@ static int isis_instance_area_address_create(enum nb_event event, XFREE(MTYPE_ISIS_AREA_ADDR, resource->ptr); break; case NB_EV_APPLY: - area = yang_dnode_get_entry(dnode, true); + area = nb_running_get_entry(dnode, NULL, true); addrr = resource->ptr; if (isis->sysid_set == 0) { @@ -207,7 +207,7 @@ static int isis_instance_area_address_destroy(enum nb_event event, net_title = yang_dnode_get_string(dnode, NULL); addr.addr_len = dotformat2buff(buff, net_title); memcpy(addr.area_addr, buff, (int)addr.addr_len); - area = yang_dnode_get_entry(dnode, true); + area = nb_running_get_entry(dnode, NULL, true); for (ALL_LIST_ELEMENTS_RO(area->area_addrs, node, addrp)) { if ((addrp->addr_len + ISIS_SYS_ID_LEN + 1) == addr.addr_len && !memcmp(addrp->area_addr, addr.area_addr, addr.addr_len)) @@ -243,7 +243,7 @@ static int isis_instance_dynamic_hostname_modify(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - area = yang_dnode_get_entry(dnode, true); + area = nb_running_get_entry(dnode, NULL, true); isis_area_dynhostname_set(area, yang_dnode_get_bool(dnode, NULL)); return NB_OK; @@ -262,7 +262,7 @@ static int isis_instance_attached_modify(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - area = yang_dnode_get_entry(dnode, true); + area = nb_running_get_entry(dnode, NULL, true); attached = yang_dnode_get_bool(dnode, NULL); isis_area_attached_bit_set(area, attached); @@ -282,7 +282,7 @@ static int isis_instance_overload_modify(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - area = yang_dnode_get_entry(dnode, true); + area = nb_running_get_entry(dnode, NULL, true); overload = yang_dnode_get_bool(dnode, NULL); isis_area_overload_bit_set(area, overload); @@ -303,7 +303,7 @@ static int isis_instance_metric_style_modify(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - area = yang_dnode_get_entry(dnode, true); + area = nb_running_get_entry(dnode, NULL, true); old_metric = (metric_style == ISIS_WIDE_METRIC) ? false : true; new_metric = (metric_style == ISIS_NARROW_METRIC) ? false : true; isis_area_metricstyle_set(area, old_metric, new_metric); @@ -323,7 +323,7 @@ static int isis_instance_purge_originator_modify(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - area = yang_dnode_get_entry(dnode, true); + area = nb_running_get_entry(dnode, NULL, true); area->purge_originator = yang_dnode_get_bool(dnode, NULL); return NB_OK; @@ -343,7 +343,7 @@ static int isis_instance_lsp_mtu_modify(enum nb_event event, switch (event) { case NB_EV_VALIDATE: - area = yang_dnode_get_entry(dnode, false); + area = nb_running_get_entry(dnode, NULL, false); if (!area) break; for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit)) { @@ -364,7 +364,7 @@ static int isis_instance_lsp_mtu_modify(enum nb_event event, case NB_EV_ABORT: break; case NB_EV_APPLY: - area = yang_dnode_get_entry(dnode, true); + area = nb_running_get_entry(dnode, NULL, true); isis_area_lsp_mtu_set(area, lsp_mtu); break; } @@ -387,7 +387,7 @@ isis_instance_lsp_refresh_interval_level_1_modify(enum nb_event event, return NB_OK; refr_int = yang_dnode_get_uint16(dnode, NULL); - area = yang_dnode_get_entry(dnode, true); + area = nb_running_get_entry(dnode, NULL, true); isis_area_lsp_refresh_set(area, IS_LEVEL_1, refr_int); return NB_OK; @@ -408,7 +408,7 @@ isis_instance_lsp_refresh_interval_level_2_modify(enum nb_event event, return NB_OK; refr_int = yang_dnode_get_uint16(dnode, NULL); - area = yang_dnode_get_entry(dnode, true); + area = nb_running_get_entry(dnode, NULL, true); isis_area_lsp_refresh_set(area, IS_LEVEL_2, refr_int); return NB_OK; @@ -429,7 +429,7 @@ isis_instance_lsp_maximum_lifetime_level_1_modify(enum nb_event event, return NB_OK; max_lt = yang_dnode_get_uint16(dnode, NULL); - area = yang_dnode_get_entry(dnode, true); + area = nb_running_get_entry(dnode, NULL, true); isis_area_max_lsp_lifetime_set(area, IS_LEVEL_1, max_lt); return NB_OK; @@ -450,7 +450,7 @@ isis_instance_lsp_maximum_lifetime_level_2_modify(enum nb_event event, return NB_OK; max_lt = yang_dnode_get_uint16(dnode, NULL); - area = yang_dnode_get_entry(dnode, true); + area = nb_running_get_entry(dnode, NULL, true); isis_area_max_lsp_lifetime_set(area, IS_LEVEL_2, max_lt); return NB_OK; @@ -470,7 +470,7 @@ static int isis_instance_lsp_generation_interval_level_1_modify( return NB_OK; gen_int = yang_dnode_get_uint16(dnode, NULL); - area = yang_dnode_get_entry(dnode, true); + area = nb_running_get_entry(dnode, NULL, true); area->lsp_gen_interval[0] = gen_int; return NB_OK; @@ -490,7 +490,7 @@ static int isis_instance_lsp_generation_interval_level_2_modify( return NB_OK; gen_int = yang_dnode_get_uint16(dnode, NULL); - area = yang_dnode_get_entry(dnode, true); + area = nb_running_get_entry(dnode, NULL, true); area->lsp_gen_interval[1] = gen_int; return NB_OK; @@ -506,7 +506,7 @@ static void ietf_backoff_delay_apply_finish(const struct lyd_node *dnode) long long_delay = yang_dnode_get_uint16(dnode, "./long-delay"); long holddown = yang_dnode_get_uint16(dnode, "./hold-down"); long timetolearn = yang_dnode_get_uint16(dnode, "./time-to-learn"); - struct isis_area *area = yang_dnode_get_entry(dnode, true); + struct isis_area *area = nb_running_get_entry(dnode, NULL, true); size_t bufsiz = strlen(area->area_tag) + sizeof("IS-IS Lx"); char *buf = XCALLOC(MTYPE_TMP, bufsiz); @@ -543,7 +543,7 @@ isis_instance_spf_ietf_backoff_delay_destroy(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - area = yang_dnode_get_entry(dnode, true); + area = nb_running_get_entry(dnode, NULL, true); spf_backoff_free(area->spf_delay_ietf[0]); spf_backoff_free(area->spf_delay_ietf[1]); area->spf_delay_ietf[0] = NULL; @@ -620,7 +620,7 @@ isis_instance_spf_minimum_interval_level_1_modify(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - area = yang_dnode_get_entry(dnode, true); + area = nb_running_get_entry(dnode, NULL, true); area->min_spf_interval[0] = yang_dnode_get_uint16(dnode, NULL); return NB_OK; @@ -639,7 +639,7 @@ isis_instance_spf_minimum_interval_level_2_modify(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - area = yang_dnode_get_entry(dnode, true); + area = nb_running_get_entry(dnode, NULL, true); area->min_spf_interval[1] = yang_dnode_get_uint16(dnode, NULL); return NB_OK; @@ -651,7 +651,7 @@ isis_instance_spf_minimum_interval_level_2_modify(enum nb_event event, static void area_password_apply_finish(const struct lyd_node *dnode) { const char *password = yang_dnode_get_string(dnode, "./password"); - struct isis_area *area = yang_dnode_get_entry(dnode, true); + struct isis_area *area = nb_running_get_entry(dnode, NULL, true); int pass_type = yang_dnode_get_enum(dnode, "./password-type"); uint8_t snp_auth = yang_dnode_get_enum(dnode, "./authenticate-snp"); @@ -683,7 +683,7 @@ static int isis_instance_area_password_destroy(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - area = yang_dnode_get_entry(dnode, true); + area = nb_running_get_entry(dnode, NULL, true); isis_area_passwd_unset(area, IS_LEVEL_1); return NB_OK; @@ -730,7 +730,7 @@ static int isis_instance_area_password_authenticate_snp_modify( static void domain_password_apply_finish(const struct lyd_node *dnode) { const char *password = yang_dnode_get_string(dnode, "./password"); - struct isis_area *area = yang_dnode_get_entry(dnode, true); + struct isis_area *area = nb_running_get_entry(dnode, NULL, true); int pass_type = yang_dnode_get_enum(dnode, "./password-type"); uint8_t snp_auth = yang_dnode_get_enum(dnode, "./authenticate-snp"); @@ -762,7 +762,7 @@ static int isis_instance_domain_password_destroy(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - area = yang_dnode_get_entry(dnode, true); + area = nb_running_get_entry(dnode, NULL, true); isis_area_passwd_unset(area, IS_LEVEL_2); return NB_OK; @@ -812,7 +812,7 @@ static void default_info_origin_apply_finish(const struct lyd_node *dnode, int originate_type = DEFAULT_ORIGINATE; unsigned long metric = 0; const char *routemap = NULL; - struct isis_area *area = yang_dnode_get_entry(dnode, true); + struct isis_area *area = nb_running_get_entry(dnode, NULL, true); int level = yang_dnode_get_enum(dnode, "./level"); if (yang_dnode_get_bool(dnode, "./always")) { @@ -859,7 +859,7 @@ static int isis_instance_default_information_originate_ipv4_destroy( if (event != NB_EV_APPLY) return NB_OK; - area = yang_dnode_get_entry(dnode, true); + area = nb_running_get_entry(dnode, NULL, true); level = yang_dnode_get_enum(dnode, "./level"); isis_redist_unset(area, level, AF_INET, DEFAULT_ROUTE); @@ -926,7 +926,7 @@ static int isis_instance_default_information_originate_ipv6_destroy( if (event != NB_EV_APPLY) return NB_OK; - area = yang_dnode_get_entry(dnode, true); + area = nb_running_get_entry(dnode, NULL, true); level = yang_dnode_get_enum(dnode, "./level"); isis_redist_unset(area, level, AF_INET6, DEFAULT_ROUTE); @@ -986,7 +986,7 @@ static void redistribute_apply_finish(const struct lyd_node *dnode, int family) type = yang_dnode_get_enum(dnode, "./protocol"); level = yang_dnode_get_enum(dnode, "./level"); - area = yang_dnode_get_entry(dnode, true); + area = nb_running_get_entry(dnode, NULL, true); if (yang_dnode_exists(dnode, "./metric")) metric = yang_dnode_get_uint32(dnode, "./metric"); @@ -1023,7 +1023,7 @@ static int isis_instance_redistribute_ipv4_destroy(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - area = yang_dnode_get_entry(dnode, true); + area = nb_running_get_entry(dnode, NULL, true); level = yang_dnode_get_enum(dnode, "./level"); type = yang_dnode_get_enum(dnode, "./protocol"); isis_redist_unset(area, level, AF_INET, type); @@ -1083,7 +1083,7 @@ static int isis_instance_redistribute_ipv6_destroy(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - area = yang_dnode_get_entry(dnode, true); + area = nb_running_get_entry(dnode, NULL, true); level = yang_dnode_get_enum(dnode, "./level"); type = yang_dnode_get_enum(dnode, "./protocol"); isis_redist_unset(area, level, AF_INET6, type); @@ -1146,7 +1146,7 @@ static int isis_multi_topology_common(enum nb_event event, case NB_EV_ABORT: break; case NB_EV_APPLY: - area = yang_dnode_get_entry(dnode, true); + area = nb_running_get_entry(dnode, NULL, true); setting = area_get_mt_setting(area, mtid); setting->enabled = create; lsp_regenerate_schedule(area, IS_LEVEL_1 | IS_LEVEL_2, 0); @@ -1168,7 +1168,7 @@ static int isis_multi_topology_overload_common(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - area = yang_dnode_get_entry(dnode, true); + area = nb_running_get_entry(dnode, NULL, true); setting = area_get_mt_setting(area, mtid); setting->overload = yang_dnode_get_bool(dnode, NULL); if (setting->enabled) @@ -1357,26 +1357,47 @@ isis_instance_log_adjacency_changes_modify(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - area = yang_dnode_get_entry(dnode, true); + area = nb_running_get_entry(dnode, NULL, true); area->log_adj_changes = log ? 1 : 0; return NB_OK; } /* - * XPath: /frr-isisd:isis/mpls-te + * XPath: /frr-isisd:isis/instance/mpls-te */ -static int isis_mpls_te_create(enum nb_event event, +static int isis_instance_mpls_te_create(enum nb_event event, const struct lyd_node *dnode, union nb_resource *resource) { struct listnode *node; + struct isis_area *area; struct isis_circuit *circuit; if (event != NB_EV_APPLY) return NB_OK; - isisMplsTE.status = enable; + area = nb_running_get_entry(dnode, NULL, true); + if (area->mta == NULL) { + + struct mpls_te_area *new; + + zlog_debug("ISIS MPLS-TE: Initialize area %s", + area->area_tag); + + new = XCALLOC(MTYPE_ISIS_MPLS_TE, sizeof(struct mpls_te_area)); + + /* Initialize MPLS_TE structure */ + new->status = enable; + new->level = 0; + new->inter_as = off; + new->interas_areaid.s_addr = 0; + new->router_id.s_addr = 0; + + area->mta = new; + } else { + area->mta->status = enable; + } /* * Following code is intended to handle two cases; @@ -1386,11 +1407,11 @@ static int isis_mpls_te_create(enum nb_event event, * MPLS_TE flag * 2) MPLS-TE was once enabled then disabled, and now enabled again. */ - for (ALL_LIST_ELEMENTS_RO(isisMplsTE.cir_list, node, circuit)) { + for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit)) { if (circuit->mtc == NULL || IS_FLOOD_AS(circuit->mtc->type)) continue; - if ((circuit->mtc->status == disable) + if (!IS_MPLS_TE(circuit->mtc) && HAS_LINK_PARAMS(circuit->interface)) circuit->mtc->status = enable; else @@ -1405,19 +1426,24 @@ static int isis_mpls_te_create(enum nb_event event, return NB_OK; } -static int isis_mpls_te_destroy(enum nb_event event, +static int isis_instance_mpls_te_destroy(enum nb_event event, const struct lyd_node *dnode) { struct listnode *node; + struct isis_area *area; struct isis_circuit *circuit; if (event != NB_EV_APPLY) return NB_OK; - isisMplsTE.status = disable; + area = nb_running_get_entry(dnode, NULL, true); + if (IS_MPLS_TE(area->mta)) + area->mta->status = disable; + else + return NB_OK; /* Flush LSP if circuit engage */ - for (ALL_LIST_ELEMENTS_RO(isisMplsTE.cir_list, node, circuit)) { + for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit)) { if (circuit->mtc == NULL || (circuit->mtc->status == disable)) continue; @@ -1434,55 +1460,53 @@ static int isis_mpls_te_destroy(enum nb_event event, } /* - * XPath: /frr-isisd:isis/mpls-te/router-address + * XPath: /frr-isisd:isis/instance/mpls-te/router-address */ -static int isis_mpls_te_router_address_modify(enum nb_event event, +static int isis_instance_mpls_te_router_address_modify(enum nb_event event, const struct lyd_node *dnode, union nb_resource *resource) { struct in_addr value; - struct listnode *node; struct isis_area *area; if (event != NB_EV_APPLY) return NB_OK; - yang_dnode_get_ipv4(&value, dnode, NULL); - isisMplsTE.router_id.s_addr = value.s_addr; + area = nb_running_get_entry(dnode, NULL, true); /* only proceed if MPLS-TE is enabled */ - if (isisMplsTE.status == disable) + if (!IS_MPLS_TE(area->mta)) return NB_OK; - /* Update main Router ID in isis global structure */ - isis->router_id = value.s_addr; + /* Update Area Router ID */ + yang_dnode_get_ipv4(&value, dnode, NULL); + area->mta->router_id.s_addr = value.s_addr; + /* And re-schedule LSP update */ - for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) - if (listcount(area->area_addrs) > 0) - lsp_regenerate_schedule(area, area->is_type, 0); + if (listcount(area->area_addrs) > 0) + lsp_regenerate_schedule(area, area->is_type, 0); return NB_OK; } -static int isis_mpls_te_router_address_destroy(enum nb_event event, +static int isis_instance_mpls_te_router_address_destroy(enum nb_event event, const struct lyd_node *dnode) { - struct listnode *node; struct isis_area *area; if (event != NB_EV_APPLY) return NB_OK; - isisMplsTE.router_id.s_addr = INADDR_ANY; + area = nb_running_get_entry(dnode, NULL, true); /* only proceed if MPLS-TE is enabled */ - if (isisMplsTE.status == disable) + if (!IS_MPLS_TE(area->mta)) return NB_OK; - /* Update main Router ID in isis global structure */ - isis->router_id = 0; + /* Reset Area Router ID */ + area->mta->router_id.s_addr = INADDR_ANY; + /* And re-schedule LSP update */ - for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) - if (listcount(area->area_addrs) > 0) - lsp_regenerate_schedule(area, area->is_type, 0); + if (listcount(area->area_addrs) > 0) + lsp_regenerate_schedule(area, area->is_type, 0); return NB_OK; } @@ -1516,10 +1540,10 @@ static int lib_interface_isis_create(enum nb_event event, abort(); } - ifp = yang_dnode_get_entry(dnode, true); + ifp = nb_running_get_entry(dnode, NULL, true); circuit = isis_circuit_create(area, ifp); assert(circuit->state == C_STATE_CONF || circuit->state == C_STATE_UP); - yang_dnode_set_entry(dnode, circuit); + nb_running_set_entry(dnode, circuit); return NB_OK; } @@ -1532,7 +1556,7 @@ static int lib_interface_isis_destroy(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - circuit = yang_dnode_get_entry(dnode, true); + circuit = nb_running_unset_entry(dnode); if (!circuit) return NB_ERR_INCONSISTENCY; /* delete circuit through csm changes */ @@ -1628,7 +1652,7 @@ static int lib_interface_isis_circuit_type_modify(enum nb_event event, case NB_EV_ABORT: break; case NB_EV_APPLY: - circuit = yang_dnode_get_entry(dnode, true); + circuit = nb_running_get_entry(dnode, NULL, true); isis_circuit_is_type_set(circuit, circ_type); break; } @@ -1649,7 +1673,7 @@ static int lib_interface_isis_ipv4_routing_modify(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - circuit = yang_dnode_get_entry(dnode, true); + circuit = nb_running_get_entry(dnode, NULL, true); ipv4 = yang_dnode_get_bool(dnode, NULL); ipv6 = yang_dnode_get_bool(dnode, "../ipv6-routing"); isis_circuit_af_set(circuit, ipv4, ipv6); @@ -1670,7 +1694,7 @@ static int lib_interface_isis_ipv6_routing_modify(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - circuit = yang_dnode_get_entry(dnode, true); + circuit = nb_running_get_entry(dnode, NULL, true); ipv4 = yang_dnode_exists(dnode, "../ipv4-routing"); ipv6 = yang_dnode_get_bool(dnode, NULL); isis_circuit_af_set(circuit, ipv4, ipv6); @@ -1691,7 +1715,7 @@ lib_interface_isis_csnp_interval_level_1_modify(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - circuit = yang_dnode_get_entry(dnode, true); + circuit = nb_running_get_entry(dnode, NULL, true); circuit->csnp_interval[0] = yang_dnode_get_uint16(dnode, NULL); return NB_OK; @@ -1710,7 +1734,7 @@ lib_interface_isis_csnp_interval_level_2_modify(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - circuit = yang_dnode_get_entry(dnode, true); + circuit = nb_running_get_entry(dnode, NULL, true); circuit->csnp_interval[1] = yang_dnode_get_uint16(dnode, NULL); return NB_OK; @@ -1729,7 +1753,7 @@ lib_interface_isis_psnp_interval_level_1_modify(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - circuit = yang_dnode_get_entry(dnode, true); + circuit = nb_running_get_entry(dnode, NULL, true); circuit->psnp_interval[0] = yang_dnode_get_uint16(dnode, NULL); return NB_OK; @@ -1748,7 +1772,7 @@ lib_interface_isis_psnp_interval_level_2_modify(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - circuit = yang_dnode_get_entry(dnode, true); + circuit = nb_running_get_entry(dnode, NULL, true); circuit->psnp_interval[1] = yang_dnode_get_uint16(dnode, NULL); return NB_OK; @@ -1766,7 +1790,7 @@ static int lib_interface_isis_hello_padding_modify(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - circuit = yang_dnode_get_entry(dnode, true); + circuit = nb_running_get_entry(dnode, NULL, true); circuit->pad_hellos = yang_dnode_get_bool(dnode, NULL); return NB_OK; @@ -1786,7 +1810,7 @@ lib_interface_isis_hello_interval_level_1_modify(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - circuit = yang_dnode_get_entry(dnode, true); + circuit = nb_running_get_entry(dnode, NULL, true); interval = yang_dnode_get_uint32(dnode, NULL); circuit->hello_interval[0] = interval; @@ -1807,7 +1831,7 @@ lib_interface_isis_hello_interval_level_2_modify(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - circuit = yang_dnode_get_entry(dnode, true); + circuit = nb_running_get_entry(dnode, NULL, true); interval = yang_dnode_get_uint32(dnode, NULL); circuit->hello_interval[1] = interval; @@ -1828,7 +1852,7 @@ lib_interface_isis_hello_multiplier_level_1_modify(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - circuit = yang_dnode_get_entry(dnode, true); + circuit = nb_running_get_entry(dnode, NULL, true); multi = yang_dnode_get_uint16(dnode, NULL); circuit->hello_multiplier[0] = multi; @@ -1849,7 +1873,7 @@ lib_interface_isis_hello_multiplier_level_2_modify(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - circuit = yang_dnode_get_entry(dnode, true); + circuit = nb_running_get_entry(dnode, NULL, true); multi = yang_dnode_get_uint16(dnode, NULL); circuit->hello_multiplier[1] = multi; @@ -1870,7 +1894,7 @@ lib_interface_isis_metric_level_1_modify(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - circuit = yang_dnode_get_entry(dnode, true); + circuit = nb_running_get_entry(dnode, NULL, true); met = yang_dnode_get_uint32(dnode, NULL); isis_circuit_metric_set(circuit, IS_LEVEL_1, met); @@ -1891,7 +1915,7 @@ lib_interface_isis_metric_level_2_modify(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - circuit = yang_dnode_get_entry(dnode, true); + circuit = nb_running_get_entry(dnode, NULL, true); met = yang_dnode_get_uint32(dnode, NULL); isis_circuit_metric_set(circuit, IS_LEVEL_2, met); @@ -1911,7 +1935,7 @@ lib_interface_isis_priority_level_1_modify(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - circuit = yang_dnode_get_entry(dnode, true); + circuit = nb_running_get_entry(dnode, NULL, true); circuit->priority[0] = yang_dnode_get_uint8(dnode, NULL); return NB_OK; @@ -1930,7 +1954,7 @@ lib_interface_isis_priority_level_2_modify(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - circuit = yang_dnode_get_entry(dnode, true); + circuit = nb_running_get_entry(dnode, NULL, true); circuit->priority[1] = yang_dnode_get_uint8(dnode, NULL); return NB_OK; @@ -1948,7 +1972,7 @@ static int lib_interface_isis_network_type_modify(enum nb_event event, switch (event) { case NB_EV_VALIDATE: - circuit = yang_dnode_get_entry(dnode, false); + circuit = nb_running_get_entry(dnode, NULL, false); if (!circuit) break; if (circuit->circ_type == CIRCUIT_T_LOOPBACK) { @@ -1970,7 +1994,7 @@ static int lib_interface_isis_network_type_modify(enum nb_event event, case NB_EV_ABORT: break; case NB_EV_APPLY: - circuit = yang_dnode_get_entry(dnode, true); + circuit = nb_running_get_entry(dnode, NULL, true); isis_circuit_circ_type_set(circuit, net_type); break; } @@ -1992,7 +2016,7 @@ static int lib_interface_isis_passive_modify(enum nb_event event, /* validation only applies if we are setting passive to false */ if (!passive && event == NB_EV_VALIDATE) { - circuit = yang_dnode_get_entry(dnode, false); + circuit = nb_running_get_entry(dnode, NULL, false); if (!circuit) return NB_OK; ifp = circuit->interface; @@ -2008,7 +2032,7 @@ static int lib_interface_isis_passive_modify(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - circuit = yang_dnode_get_entry(dnode, true); + circuit = nb_running_get_entry(dnode, NULL, true); if (circuit->state != C_STATE_UP) { circuit->is_passive = passive; } else { @@ -2039,7 +2063,7 @@ static int lib_interface_isis_password_destroy(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - circuit = yang_dnode_get_entry(dnode, true); + circuit = nb_running_get_entry(dnode, NULL, true); isis_circuit_passwd_unset(circuit); return NB_OK; @@ -2060,7 +2084,7 @@ lib_interface_isis_password_password_modify(enum nb_event event, return NB_OK; password = yang_dnode_get_string(dnode, NULL); - circuit = yang_dnode_get_entry(dnode, true); + circuit = nb_running_get_entry(dnode, NULL, true); isis_circuit_passwd_set(circuit, circuit->passwd.type, password); @@ -2082,7 +2106,7 @@ lib_interface_isis_password_password_type_modify(enum nb_event event, return NB_OK; pass_type = yang_dnode_get_enum(dnode, NULL); - circuit = yang_dnode_get_entry(dnode, true); + circuit = nb_running_get_entry(dnode, NULL, true); circuit->passwd.type = pass_type; return NB_OK; @@ -2101,7 +2125,7 @@ static int lib_interface_isis_disable_three_way_handshake_modify( if (event != NB_EV_APPLY) return NB_OK; - circuit = yang_dnode_get_entry(dnode, true); + circuit = nb_running_get_entry(dnode, NULL, true); circuit->disable_threeway_adj = yang_dnode_get_bool(dnode, NULL); return NB_OK; @@ -2119,7 +2143,7 @@ static int lib_interface_isis_multi_topology_common( switch (event) { case NB_EV_VALIDATE: - circuit = yang_dnode_get_entry(dnode, false); + circuit = nb_running_get_entry(dnode, NULL, false); if (circuit && circuit->area && circuit->area->oldmetric) { flog_warn( EC_LIB_NB_CB_CONFIG_VALIDATE, @@ -2131,7 +2155,7 @@ static int lib_interface_isis_multi_topology_common( case NB_EV_ABORT: break; case NB_EV_APPLY: - circuit = yang_dnode_get_entry(dnode, true); + circuit = nb_running_get_entry(dnode, NULL, true); value = yang_dnode_get_bool(dnode, NULL); isis_circuit_mt_enabled_set(circuit, mtid, value); break; @@ -3014,15 +3038,15 @@ const struct frr_yang_module_info frr_isisd_info = { .cbs.cli_show = cli_show_isis_log_adjacency, }, { - .xpath = "/frr-isisd:isis/mpls-te", - .cbs.create = isis_mpls_te_create, - .cbs.destroy = isis_mpls_te_destroy, + .xpath = "/frr-isisd:isis/instance/mpls-te", + .cbs.create = isis_instance_mpls_te_create, + .cbs.destroy = isis_instance_mpls_te_destroy, .cbs.cli_show = cli_show_isis_mpls_te, }, { - .xpath = "/frr-isisd:isis/mpls-te/router-address", - .cbs.modify = isis_mpls_te_router_address_modify, - .cbs.destroy = isis_mpls_te_router_address_destroy, + .xpath = "/frr-isisd:isis/instance/mpls-te/router-address", + .cbs.modify = isis_instance_mpls_te_router_address_modify, + .cbs.destroy = isis_instance_mpls_te_router_address_destroy, .cbs.cli_show = cli_show_isis_mpls_te_router_addr, }, { diff --git a/isisd/isis_pdu.c b/isisd/isis_pdu.c index afd5e2bf8..9c633117b 100644 --- a/isisd/isis_pdu.c +++ b/isisd/isis_pdu.c @@ -200,8 +200,9 @@ static int process_p2p_hello(struct iih_info *iih) adj); /* Update MPLS TE Remote IP address parameter if possible */ - if (IS_MPLS_TE(isisMplsTE) && iih->circuit->mtc - && IS_CIRCUIT_TE(iih->circuit->mtc) && adj->ipv4_address_count) + if (IS_MPLS_TE(iih->circuit->area->mta) + && IS_MPLS_TE(iih->circuit->mtc) + && adj->ipv4_address_count) set_circuitparams_rmt_ipaddr(iih->circuit->mtc, adj->ipv4_addresses[0]); diff --git a/isisd/isis_te.c b/isisd/isis_te.c index a9937b955..4ea6c2c60 100644 --- a/isisd/isis_te.c +++ b/isisd/isis_te.c @@ -57,9 +57,6 @@ #include "isisd/isis_spf.h" #include "isisd/isis_te.h" -/* Global varial for MPLS TE management */ -struct isis_mpls_te isisMplsTE; - const char *mode2text[] = {"Disable", "Area", "AS", "Emulate"}; /*------------------------------------------------------------------------* @@ -623,7 +620,7 @@ void isis_link_params_update(struct isis_circuit *circuit, /* Finally Update LSP */ #if 0 - if (IS_MPLS_TE(isisMplsTE) && circuit->area) + if (circuit->area && IS_MPLS_TE(circuit->area->mta)) lsp_regenerate_schedule (circuit->area, circuit->is_type, 0); #endif return; @@ -645,7 +642,7 @@ void isis_mpls_te_update(struct interface *ifp) isis_link_params_update(circuit, ifp); /* ... and LSP */ - if (IS_MPLS_TE(isisMplsTE) && circuit->area) + if (circuit->area && IS_MPLS_TE(circuit->area->mta)) lsp_regenerate_schedule(circuit->area, circuit->is_type, 0); return; @@ -1057,35 +1054,11 @@ void mpls_te_print_detail(struct sbuf *buf, int indent, return; } -/* Specific MPLS TE router parameters write function */ -void isis_mpls_te_config_write_router(struct vty *vty) -{ - if (IS_MPLS_TE(isisMplsTE)) { - vty_out(vty, " mpls-te on\n"); - vty_out(vty, " mpls-te router-address %s\n", - inet_ntoa(isisMplsTE.router_id)); - } - - return; -} - - /*------------------------------------------------------------------------* * Followings are vty command functions. *------------------------------------------------------------------------*/ #ifndef FABRICD -/* Search MPLS TE Circuit context from Interface */ -static struct mpls_te_circuit *lookup_mpls_params_by_ifp(struct interface *ifp) -{ - struct isis_circuit *circuit; - - if ((circuit = circuit_scan_by_ifp(ifp)) == NULL) - return NULL; - - return circuit->mtc; -} - DEFUN (show_isis_mpls_te_router, show_isis_mpls_te_router_cmd, "show " PROTO_NAME " mpls-te router", @@ -1094,84 +1067,73 @@ DEFUN (show_isis_mpls_te_router, MPLS_TE_STR "Router information\n") { - if (IS_MPLS_TE(isisMplsTE)) { - vty_out(vty, "--- MPLS-TE router parameters ---\n"); - if (ntohs(isisMplsTE.router_id.s_addr) != 0) - vty_out(vty, " Router-Address: %s\n", - inet_ntoa(isisMplsTE.router_id)); + struct listnode *anode; + struct isis_area *area; + + if (!isis) { + vty_out(vty, "IS-IS Routing Process not enabled\n"); + return CMD_SUCCESS; + } + + for (ALL_LIST_ELEMENTS_RO(isis->area_list, anode, area)) { + + if (!IS_MPLS_TE(area->mta)) + continue; + + vty_out(vty, "Area %s:\n", area->area_tag); + if (ntohs(area->mta->router_id.s_addr) != 0) + vty_out(vty, " MPLS-TE Router-Address: %s\n", + inet_ntoa(area->mta->router_id)); else vty_out(vty, " N/A\n"); - } else - vty_out(vty, " MPLS-TE is disable on this router\n"); + } return CMD_SUCCESS; } -static void show_mpls_te_sub(struct vty *vty, struct interface *ifp) +static void show_mpls_te_sub(struct vty *vty, char *name, + struct mpls_te_circuit *mtc) { - struct mpls_te_circuit *mtc; struct sbuf buf; sbuf_init(&buf, NULL, 0); - if ((IS_MPLS_TE(isisMplsTE)) - && ((mtc = lookup_mpls_params_by_ifp(ifp)) != NULL)) { - /* Continue only if interface is not passive or support Inter-AS - * TEv2 */ - if (mtc->status != enable) { - if (IS_INTER_AS(mtc->type)) { - vty_out(vty, - "-- Inter-AS TEv2 link parameters for %s --\n", - ifp->name); - } else { - /* MPLS-TE is not activate on this interface */ - /* or this interface is passive and Inter-AS - * TEv2 is not activate */ - vty_out(vty, - " %s: MPLS-TE is disabled on this interface\n", - ifp->name); - return; - } - } else { - vty_out(vty, "-- MPLS-TE link parameters for %s --\n", - ifp->name); - } - - sbuf_reset(&buf); - print_subtlv_admin_grp(&buf, 4, &mtc->admin_grp); + if (mtc->status != enable) + return; - if (SUBTLV_TYPE(mtc->local_ipaddr) != 0) - print_subtlv_local_ipaddr(&buf, 4, &mtc->local_ipaddr); - if (SUBTLV_TYPE(mtc->rmt_ipaddr) != 0) - print_subtlv_rmt_ipaddr(&buf, 4, &mtc->rmt_ipaddr); + vty_out(vty, "-- MPLS-TE link parameters for %s --\n", name); - print_subtlv_max_bw(&buf, 4, &mtc->max_bw); - print_subtlv_max_rsv_bw(&buf, 4, &mtc->max_rsv_bw); - print_subtlv_unrsv_bw(&buf, 4, &mtc->unrsv_bw); - print_subtlv_te_metric(&buf, 4, &mtc->te_metric); + sbuf_reset(&buf); + print_subtlv_admin_grp(&buf, 4, &mtc->admin_grp); - if (IS_INTER_AS(mtc->type)) { - if (SUBTLV_TYPE(mtc->ras) != 0) - print_subtlv_ras(&buf, 4, &mtc->ras); - if (SUBTLV_TYPE(mtc->rip) != 0) - print_subtlv_rip(&buf, 4, &mtc->rip); - } + if (SUBTLV_TYPE(mtc->local_ipaddr) != 0) + print_subtlv_local_ipaddr(&buf, 4, &mtc->local_ipaddr); + if (SUBTLV_TYPE(mtc->rmt_ipaddr) != 0) + print_subtlv_rmt_ipaddr(&buf, 4, &mtc->rmt_ipaddr); + + print_subtlv_max_bw(&buf, 4, &mtc->max_bw); + print_subtlv_max_rsv_bw(&buf, 4, &mtc->max_rsv_bw); + print_subtlv_unrsv_bw(&buf, 4, &mtc->unrsv_bw); + print_subtlv_te_metric(&buf, 4, &mtc->te_metric); + + if (IS_INTER_AS(mtc->type)) { + if (SUBTLV_TYPE(mtc->ras) != 0) + print_subtlv_ras(&buf, 4, &mtc->ras); + if (SUBTLV_TYPE(mtc->rip) != 0) + print_subtlv_rip(&buf, 4, &mtc->rip); + } - print_subtlv_av_delay(&buf, 4, &mtc->av_delay); - print_subtlv_mm_delay(&buf, 4, &mtc->mm_delay); - print_subtlv_delay_var(&buf, 4, &mtc->delay_var); - print_subtlv_pkt_loss(&buf, 4, &mtc->pkt_loss); - print_subtlv_res_bw(&buf, 4, &mtc->res_bw); - print_subtlv_ava_bw(&buf, 4, &mtc->ava_bw); - print_subtlv_use_bw(&buf, 4, &mtc->use_bw); + print_subtlv_av_delay(&buf, 4, &mtc->av_delay); + print_subtlv_mm_delay(&buf, 4, &mtc->mm_delay); + print_subtlv_delay_var(&buf, 4, &mtc->delay_var); + print_subtlv_pkt_loss(&buf, 4, &mtc->pkt_loss); + print_subtlv_res_bw(&buf, 4, &mtc->res_bw); + print_subtlv_ava_bw(&buf, 4, &mtc->ava_bw); + print_subtlv_use_bw(&buf, 4, &mtc->use_bw); - vty_multiline(vty, "", "%s", sbuf_buf(&buf)); - vty_out(vty, "---------------\n\n"); - } else { - vty_out(vty, " %s: MPLS-TE is disabled on this interface\n", - ifp->name); - } + vty_multiline(vty, "", "%s", sbuf_buf(&buf)); + vty_out(vty, "---------------\n\n"); sbuf_free(&buf); return; @@ -1186,23 +1148,45 @@ DEFUN (show_isis_mpls_te_interface, "Interface information\n" "Interface name\n") { - struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT); - int idx_interface = 4; + struct listnode *anode, *cnode; + struct isis_area *area; + struct isis_circuit *circuit; struct interface *ifp; + int idx_interface = 4; - /* Show All Interfaces. */ - if (argc == 4) { - FOR_ALL_INTERFACES (vrf, ifp) - show_mpls_te_sub(vty, ifp); + if (!isis) { + vty_out(vty, "IS-IS Routing Process not enabled\n"); + return CMD_SUCCESS; } - /* Interface name is specified. */ - else { - if ((ifp = if_lookup_by_name(argv[idx_interface]->arg, - VRF_DEFAULT)) - == NULL) + + if (argc == idx_interface) { + /* Show All Interfaces. */ + for (ALL_LIST_ELEMENTS_RO(isis->area_list, anode, area)) { + + if (!IS_MPLS_TE(area->mta)) + continue; + + vty_out(vty, "Area %s:\n", area->area_tag); + + for (ALL_LIST_ELEMENTS_RO(area->circuit_list, cnode, + circuit)) + show_mpls_te_sub(vty, circuit->interface->name, + circuit->mtc); + } + } else { + /* Interface name is specified. */ + ifp = if_lookup_by_name(argv[idx_interface]->arg, VRF_DEFAULT); + if (ifp == NULL) vty_out(vty, "No such interface name\n"); - else - show_mpls_te_sub(vty, ifp); + else { + circuit = circuit_scan_by_ifp(ifp); + if (!circuit) + vty_out(vty, + "ISIS is not enabled on circuit %s\n", + ifp->name); + else + show_mpls_te_sub(vty, ifp->name, circuit->mtc); + } } return CMD_SUCCESS; @@ -1213,16 +1197,6 @@ DEFUN (show_isis_mpls_te_interface, void isis_mpls_te_init(void) { - zlog_debug("ISIS MPLS-TE: Initialize"); - - /* Initialize MPLS_TE structure */ - isisMplsTE.status = disable; - isisMplsTE.level = 0; - isisMplsTE.inter_as = off; - isisMplsTE.interas_areaid.s_addr = 0; - isisMplsTE.cir_list = list_new(); - isisMplsTE.router_id.s_addr = 0; - #ifndef FABRICD /* Register new VTY commands */ install_element(VIEW_NODE, &show_isis_mpls_te_router_cmd); diff --git a/isisd/isis_te.h b/isisd/isis_te.h index e9eff08cd..beb0c1836 100644 --- a/isisd/isis_te.h +++ b/isisd/isis_te.h @@ -244,11 +244,10 @@ typedef enum _status_t { disable, enable, learn } status_t; /* Mode for Inter-AS LSP */ /* TODO: Check how if LSP is flooded in RFC5316 */ typedef enum _interas_mode_t { off, region, as, emulate } interas_mode_t; -#define IS_MPLS_TE(m) (m.status == enable) -#define IS_CIRCUIT_TE(c) (c->status == enable) +#define IS_MPLS_TE(m) (m && m->status == enable) -/* Following structure are internal use only. */ -struct isis_mpls_te { +/* Per area MPLS-TE parameters */ +struct mpls_te_area { /* Status of MPLS-TE: enable or disable */ status_t status; @@ -259,15 +258,11 @@ struct isis_mpls_te { interas_mode_t inter_as; struct in_addr interas_areaid; - /* Circuit list on which TE are enable */ - struct list *cir_list; - /* MPLS_TE router ID */ struct in_addr router_id; }; -extern struct isis_mpls_te isisMplsTE; - +/* Per Circuit MPLS-TE parameters */ struct mpls_te_circuit { /* Status of MPLS-TE on this interface */ @@ -318,6 +313,5 @@ uint8_t add_te_subtlvs(uint8_t *, struct mpls_te_circuit *); uint8_t build_te_subtlvs(uint8_t *, struct isis_circuit *); void isis_link_params_update(struct isis_circuit *, struct interface *); void isis_mpls_te_update(struct interface *); -void isis_mpls_te_config_write_router(struct vty *); #endif /* _ZEBRA_ISIS_MPLS_TE_H */ diff --git a/isisd/isis_zebra.c b/isisd/isis_zebra.c index ad2cfe82d..904ab99c7 100644 --- a/isisd/isis_zebra.c +++ b/isisd/isis_zebra.c @@ -60,13 +60,6 @@ static int isis_router_id_update_zebra(int command, struct zclient *zclient, struct listnode *node; struct prefix router_id; - /* - * If ISIS TE is enable, TE Router ID is set through specific command. - * See mpls_te_router_addr() command in isis_te.c - */ - if (IS_MPLS_TE(isisMplsTE)) - return 0; - zebra_router_id_update_read(zclient->ibuf, &router_id); if (isis->router_id == router_id.u.prefix4.s_addr) return 0; diff --git a/isisd/isisd.c b/isisd/isisd.c index 172c00e26..07be68d9a 100644 --- a/isisd/isisd.c +++ b/isisd/isisd.c @@ -94,7 +94,6 @@ void isis_new(unsigned long process_id) * uncomment the next line for full debugs */ /* isis->debugs = 0xFFFF; */ - isisMplsTE.status = disable; /* Only support TE metric */ QOBJ_REG(isis, isis); } @@ -255,6 +254,10 @@ int isis_area_destroy(const char *area_tag) if (fabricd) fabricd_finish(area->fabricd); + /* Disable MPLS if necessary before flooding LSP */ + if (IS_MPLS_TE(area->mta)) + area->mta->status = disable; + if (area->circuit_list) { for (ALL_LIST_ELEMENTS(area->circuit_list, node, nnode, circuit)) { @@ -2122,7 +2125,6 @@ int isis_config_write(struct vty *vty) write += area_write_mt_settings(area, vty); write += fabricd_write_settings(area, vty); } - isis_mpls_te_config_write_router(vty); } return write; diff --git a/isisd/isisd.h b/isisd/isisd.h index 023738413..f8486ae0d 100644 --- a/isisd/isisd.h +++ b/isisd/isisd.h @@ -165,6 +165,8 @@ struct isis_area { uint8_t log_adj_changes; /* multi topology settings */ struct list *mt_settings; + /* MPLS-TE settings */ + struct mpls_te_area *mta; int ipv6_circuits; bool purge_originator; /* Counters */ @@ -436,13 +436,13 @@ void if_set_index(struct interface *ifp, ifindex_t ifindex) } /* Does interface up ? */ -int if_is_up(struct interface *ifp) +int if_is_up(const struct interface *ifp) { return ifp->flags & IFF_UP; } /* Is interface running? */ -int if_is_running(struct interface *ifp) +int if_is_running(const struct interface *ifp) { return ifp->flags & IFF_RUNNING; } @@ -450,7 +450,7 @@ int if_is_running(struct interface *ifp) /* Is the interface operative, eg. either UP & RUNNING or UP & !ZEBRA_INTERFACE_LINK_DETECTION and if ptm checking is enabled, then ptm check has passed */ -int if_is_operative(struct interface *ifp) +int if_is_operative(const struct interface *ifp) { return ((ifp->flags & IFF_UP) && (((ifp->flags & IFF_RUNNING) @@ -461,7 +461,7 @@ int if_is_operative(struct interface *ifp) /* Is the interface operative, eg. either UP & RUNNING or UP & !ZEBRA_INTERFACE_LINK_DETECTION, without PTM check */ -int if_is_no_ptm_operative(struct interface *ifp) +int if_is_no_ptm_operative(const struct interface *ifp) { return ((ifp->flags & IFF_UP) && ((ifp->flags & IFF_RUNNING) @@ -470,7 +470,7 @@ int if_is_no_ptm_operative(struct interface *ifp) } /* Is this loopback interface ? */ -int if_is_loopback(struct interface *ifp) +int if_is_loopback(const struct interface *ifp) { /* XXX: Do this better, eg what if IFF_WHATEVER means X on platform M * but Y on platform N? @@ -479,12 +479,12 @@ int if_is_loopback(struct interface *ifp) } /* Check interface is VRF */ -int if_is_vrf(struct interface *ifp) +int if_is_vrf(const struct interface *ifp) { return CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_VRF_LOOPBACK); } -bool if_is_loopback_or_vrf(struct interface *ifp) +bool if_is_loopback_or_vrf(const struct interface *ifp) { if (if_is_loopback(ifp) || if_is_vrf(ifp)) return true; @@ -493,19 +493,19 @@ bool if_is_loopback_or_vrf(struct interface *ifp) } /* Does this interface support broadcast ? */ -int if_is_broadcast(struct interface *ifp) +int if_is_broadcast(const struct interface *ifp) { return ifp->flags & IFF_BROADCAST; } /* Does this interface support broadcast ? */ -int if_is_pointopoint(struct interface *ifp) +int if_is_pointopoint(const struct interface *ifp) { return ifp->flags & IFF_POINTOPOINT; } /* Does this interface support multicast ? */ -int if_is_multicast(struct interface *ifp) +int if_is_multicast(const struct interface *ifp) { return ifp->flags & IFF_MULTICAST; } @@ -1303,7 +1303,7 @@ static int lib_interface_create(enum nb_event event, #else ifp = if_get_by_name(ifname, vrf->vrf_id); #endif /* SUNOS_5 */ - yang_dnode_set_entry(dnode, ifp); + nb_running_set_entry(dnode, ifp); break; } @@ -1315,10 +1315,10 @@ static int lib_interface_destroy(enum nb_event event, { struct interface *ifp; - ifp = yang_dnode_get_entry(dnode, true); switch (event) { case NB_EV_VALIDATE: + ifp = nb_running_get_entry(dnode, NULL, true); if (CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE)) { zlog_warn("%s: only inactive interfaces can be deleted", __func__); @@ -1329,6 +1329,7 @@ static int lib_interface_destroy(enum nb_event event, case NB_EV_ABORT: break; case NB_EV_APPLY: + ifp = nb_running_unset_entry(dnode); if_delete(ifp); break; } @@ -1349,7 +1350,7 @@ static int lib_interface_description_modify(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - ifp = yang_dnode_get_entry(dnode, true); + ifp = nb_running_get_entry(dnode, NULL, true); XFREE(MTYPE_TMP, ifp->desc); description = yang_dnode_get_string(dnode, NULL); ifp->desc = XSTRDUP(MTYPE_TMP, description); @@ -1365,7 +1366,7 @@ static int lib_interface_description_destroy(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - ifp = yang_dnode_get_entry(dnode, true); + ifp = nb_running_get_entry(dnode, NULL, true); XFREE(MTYPE_TMP, ifp->desc); return NB_OK; @@ -499,16 +499,16 @@ extern void if_delete_retain(struct interface *); deletes it from the interface list and frees the structure. */ extern void if_delete(struct interface *); -extern int if_is_up(struct interface *); -extern int if_is_running(struct interface *); -extern int if_is_operative(struct interface *); -extern int if_is_no_ptm_operative(struct interface *); -extern int if_is_loopback(struct interface *); -extern int if_is_vrf(struct interface *ifp); -extern bool if_is_loopback_or_vrf(struct interface *ifp); -extern int if_is_broadcast(struct interface *); -extern int if_is_pointopoint(struct interface *); -extern int if_is_multicast(struct interface *); +extern int if_is_up(const struct interface *ifp); +extern int if_is_running(const struct interface *ifp); +extern int if_is_operative(const struct interface *ifp); +extern int if_is_no_ptm_operative(const struct interface *ifp); +extern int if_is_loopback(const struct interface *ifp); +extern int if_is_vrf(const struct interface *ifp); +extern bool if_is_loopback_or_vrf(const struct interface *ifp); +extern int if_is_broadcast(const struct interface *ifp); +extern int if_is_pointopoint(const struct interface *ifp); +extern int if_is_multicast(const struct interface *ifp); struct vrf; extern void if_terminate(struct vrf *vrf); extern void if_dump_all(void); diff --git a/lib/if_rmap.c b/lib/if_rmap.c index d8236b6b2..b0802da96 100644 --- a/lib/if_rmap.c +++ b/lib/if_rmap.c @@ -291,6 +291,7 @@ int config_write_if_rmap(struct vty *vty, void if_rmap_ctx_delete(struct if_rmap_ctx *ctx) { + listnode_delete(if_rmap_ctx_list, ctx); hash_clean(ctx->ifrmaphash, (void (*)(void *))if_rmap_free); if (ctx->name) XFREE(MTYPE_IF_RMAP_CTX_NAME, ctx); diff --git a/lib/libfrr.c b/lib/libfrr.c index 1afe30d61..0d4c8d6c0 100644 --- a/lib/libfrr.c +++ b/lib/libfrr.c @@ -38,6 +38,7 @@ #include "lib_errors.h" #include "db.h" #include "northbound_cli.h" +#include "northbound_db.h" DEFINE_HOOK(frr_late_init, (struct thread_master * tm), (tm)) DEFINE_KOOH(frr_early_fini, (), ()) @@ -654,6 +655,10 @@ struct thread_master *frr_init(void) yang_init(); nb_init(master, di->yang_modules, di->n_yang_modules); + if (nb_db_init() != NB_OK) + flog_warn(EC_LIB_NB_DATABASE, + "%s: failed to initialize northbound database", + __func__); return master; } diff --git a/lib/linklist.c b/lib/linklist.c index f0d0c2924..40c4b2716 100644 --- a/lib/linklist.c +++ b/lib/linklist.c @@ -50,7 +50,7 @@ static void listnode_free(struct listnode *node) XFREE(MTYPE_LINK_NODE, node); } -void listnode_add(struct list *list, void *val) +struct listnode *listnode_add(struct list *list, void *val) { struct listnode *node; @@ -68,6 +68,8 @@ void listnode_add(struct list *list, void *val) list->tail = node; list->count++; + + return node; } void listnode_add_head(struct list *list, void *val) @@ -326,7 +328,7 @@ void list_sort(struct list *list, int (*cmp)(const void **, const void **)) XFREE(MTYPE_TMP, items); } -void listnode_add_force(struct list **list, void *val) +struct listnode *listnode_add_force(struct list **list, void *val) { if (*list == NULL) *list = list_new(); diff --git a/lib/linklist.h b/lib/linklist.h index e75d86343..c30d8d314 100644 --- a/lib/linklist.h +++ b/lib/linklist.h @@ -84,7 +84,7 @@ extern struct list *list_new(void); * data * element to add */ -extern void listnode_add(struct list *list, void *data); +extern struct listnode *listnode_add(struct list *list, void *data); /* * Add a new element to the beginning of a list. @@ -343,7 +343,13 @@ extern void list_add_list(struct list *list, struct list *add); extern struct listnode *listnode_lookup_nocheck(struct list *list, void *data); -extern void listnode_add_force(struct list **list, void *val); +/* + * Add a node to *list, if non-NULL. Otherwise, allocate a new list, mail + * it back in *list, and add a new node. + * + * Return: the new node. + */ +extern struct listnode *listnode_add_force(struct list **list, void *val); #ifdef __cplusplus } @@ -1068,6 +1068,8 @@ static const struct zebra_desc_table command_types[] = { DESC_ENTRY(ZEBRA_IPTABLE_DELETE), DESC_ENTRY(ZEBRA_IPTABLE_NOTIFY_OWNER), DESC_ENTRY(ZEBRA_VXLAN_FLOOD_CONTROL), + DESC_ENTRY(ZEBRA_VXLAN_SG_ADD), + DESC_ENTRY(ZEBRA_VXLAN_SG_DEL), }; #undef DESC_ENTRY @@ -94,6 +94,7 @@ extern void zlog_warn(const char *format, ...) PRINTF_ATTRIBUTE(1, 2); extern void zlog_info(const char *format, ...) PRINTF_ATTRIBUTE(1, 2); extern void zlog_notice(const char *format, ...) PRINTF_ATTRIBUTE(1, 2); extern void zlog_debug(const char *format, ...) PRINTF_ATTRIBUTE(1, 2); +extern void zlog(int priority, const char *format, ...) PRINTF_ATTRIBUTE(2, 3); /* For logs which have error codes associated with them */ #define flog_err(ferr_id, format, ...) \ @@ -102,7 +103,8 @@ extern void zlog_debug(const char *format, ...) PRINTF_ATTRIBUTE(1, 2); flog_err(ferr_id, format, ##__VA_ARGS__) #define flog_warn(ferr_id, format, ...) \ zlog_warn("[EC %" PRIu32 "] " format, ferr_id, ##__VA_ARGS__) - +#define flog(priority, ferr_id, format, ...) \ + zlog(priority, "[EC %" PRIu32 "] " format, ferr_id, ##__VA_ARGS__) extern void zlog_thread_info(int log_level); diff --git a/lib/log_int.h b/lib/log_int.h index 58ae031e1..287e626ea 100644 --- a/lib/log_int.h +++ b/lib/log_int.h @@ -51,7 +51,6 @@ extern const char *zlog_priority[]; /* Generic function for zlog. */ extern void vzlog(int priority, const char *format, va_list args); -extern void zlog(int priority, const char *format, ...) PRINTF_ATTRIBUTE(2, 3); #ifdef __cplusplus } diff --git a/lib/nexthop.h b/lib/nexthop.h index 24b095319..663acaeb6 100644 --- a/lib/nexthop.h +++ b/lib/nexthop.h @@ -81,8 +81,8 @@ struct nexthop { #define NEXTHOP_FLAG_RECURSIVE (1 << 2) /* Recursive nexthop. */ #define NEXTHOP_FLAG_ONLINK (1 << 3) /* Nexthop should be installed onlink. */ #define NEXTHOP_FLAG_MATCHED (1 << 4) /* Already matched vs a nexthop */ -#define NEXTHOP_FLAG_FILTERED (1 << 5) /* rmap filtered, used by static only */ -#define NEXTHOP_FLAG_DUPLICATE (1 << 6) /* nexthop duplicates another active one */ +#define NEXTHOP_FLAG_DUPLICATE (1 << 5) /* nexthop duplicates another active one */ +#define NEXTHOP_FLAG_RNH_FILTERED (1 << 6) /* rmap filtered, used by rnh */ #define NEXTHOP_IS_ACTIVE(flags) \ (CHECK_FLAG(flags, NEXTHOP_FLAG_ACTIVE) \ && !CHECK_FLAG(flags, NEXTHOP_FLAG_DUPLICATE)) diff --git a/lib/nexthop_group.c b/lib/nexthop_group.c index fa89b7708..ed22f6449 100644 --- a/lib/nexthop_group.c +++ b/lib/nexthop_group.c @@ -59,6 +59,30 @@ nexthop_group_cmd_compare(const struct nexthop_group_cmd *nhgc1, return strcmp(nhgc1->name, nhgc2->name); } +uint8_t nexthop_group_nexthop_num(const struct nexthop_group *nhg) +{ + struct nexthop *nhop; + uint8_t num = 0; + + for (ALL_NEXTHOPS_PTR(nhg, nhop)) + num++; + + return num; +} + +uint8_t nexthop_group_active_nexthop_num(const struct nexthop_group *nhg) +{ + struct nexthop *nhop; + uint8_t num = 0; + + for (ALL_NEXTHOPS_PTR(nhg, nhop)) { + if (CHECK_FLAG(nhop->flags, NEXTHOP_FLAG_ACTIVE)) + num++; + } + + return num; +} + struct nexthop *nexthop_exists(struct nexthop_group *nhg, struct nexthop *nh) { struct nexthop *nexthop; diff --git a/lib/nexthop_group.h b/lib/nexthop_group.h index f68033c20..5adf2db93 100644 --- a/lib/nexthop_group.h +++ b/lib/nexthop_group.h @@ -117,6 +117,11 @@ extern struct nexthop_group_cmd *nhgc_find(const char *name); extern void nexthop_group_write_nexthop(struct vty *vty, struct nexthop *nh); +/* Return the number of nexthops in this nhg */ +extern uint8_t nexthop_group_nexthop_num(const struct nexthop_group *nhg); +extern uint8_t +nexthop_group_active_nexthop_num(const struct nexthop_group *nhg); + #ifdef __cplusplus } #endif diff --git a/lib/northbound.c b/lib/northbound.c index 9deb9c6cc..5e031ac2c 100644 --- a/lib/northbound.c +++ b/lib/northbound.c @@ -22,6 +22,7 @@ #include "libfrr.h" #include "log.h" #include "lib_errors.h" +#include "hash.h" #include "command.h" #include "debug.h" #include "db.h" @@ -31,10 +32,14 @@ DEFINE_MTYPE_STATIC(LIB, NB_NODE, "Northbound Node") DEFINE_MTYPE_STATIC(LIB, NB_CONFIG, "Northbound Configuration") +DEFINE_MTYPE_STATIC(LIB, NB_CONFIG_ENTRY, "Northbound Configuration Entry") /* Running configuration - shouldn't be modified directly. */ struct nb_config *running_config; +/* Hash table of user pointers associated with configuration entries. */ +static struct hash *running_config_entries; + /* * Global lock used to prevent multiple configuration transactions from * happening concurrently. @@ -454,13 +459,6 @@ int nb_candidate_edit(struct nb_config *candidate, struct lyd_node *dnode; char xpath_edit[XPATH_MAXLEN]; - if (!nb_operation_is_valid(operation, nb_node->snode)) { - flog_warn(EC_LIB_NB_CANDIDATE_EDIT_ERROR, - "%s: %s operation not valid for %s", __func__, - nb_operation_name(operation), xpath); - return NB_ERR; - } - /* Use special notation for leaf-lists (RFC 6020, section 9.13.5). */ if (nb_node->snode->nodetype == LYS_LEAFLIST) snprintf(xpath_edit, sizeof(xpath_edit), "%s[.='%s']", xpath, @@ -535,38 +533,6 @@ int nb_candidate_update(struct nb_config *candidate) } /* - * The northbound configuration callbacks use the 'priv' pointer present in the - * libyang lyd_node structure to store pointers to FRR internal variables - * associated to YANG lists and presence containers. Before commiting a - * candidate configuration, we must restore the 'priv' pointers stored in the - * running configuration since they might be lost while editing the candidate. - */ -static void nb_candidate_restore_priv_pointers(struct nb_config *candidate) -{ - struct lyd_node *root, *next, *dnode_iter; - - LY_TREE_FOR (running_config->dnode, root) { - LY_TREE_DFS_BEGIN (root, next, dnode_iter) { - struct lyd_node *dnode_candidate; - char xpath[XPATH_MAXLEN]; - - if (!dnode_iter->priv) - goto next; - - yang_dnode_get_path(dnode_iter, xpath, sizeof(xpath)); - dnode_candidate = - yang_dnode_get(candidate->dnode, xpath); - if (dnode_candidate) - yang_dnode_set_entry(dnode_candidate, - dnode_iter->priv); - - next: - LY_TREE_DFS_END(root, next, dnode_iter); - } - } -} - -/* * Perform YANG syntactic and semantic validation. * * WARNING: lyd_validate() can change the configuration as part of the @@ -588,7 +554,6 @@ static int nb_candidate_validate_changes(struct nb_config *candidate, { struct nb_config_cb *cb; - nb_candidate_restore_priv_pointers(candidate); RB_FOREACH (cb, nb_config_cbs, changes) { struct nb_config_change *change = (struct nb_config_change *)cb; int ret; @@ -753,40 +718,44 @@ static int nb_callback_configuration(const enum nb_event event, ret = (*nb_node->cbs.move)(event, dnode); break; default: - break; + flog_err(EC_LIB_DEVELOPMENT, + "%s: unknown operation (%u) [xpath %s]", __func__, + operation, xpath); + exit(1); } if (ret != NB_OK) { - enum lib_log_refs ref = 0; + int priority; + enum lib_log_refs ref; switch (event) { case NB_EV_VALIDATE: + priority = LOG_WARNING; ref = EC_LIB_NB_CB_CONFIG_VALIDATE; break; case NB_EV_PREPARE: + priority = LOG_WARNING; ref = EC_LIB_NB_CB_CONFIG_PREPARE; break; case NB_EV_ABORT: + priority = LOG_WARNING; ref = EC_LIB_NB_CB_CONFIG_ABORT; break; case NB_EV_APPLY: + priority = LOG_ERR; ref = EC_LIB_NB_CB_CONFIG_APPLY; break; + default: + flog_err(EC_LIB_DEVELOPMENT, + "%s: unknown event (%u) [xpath %s]", + __func__, event, xpath); + exit(1); } - if (event == NB_EV_VALIDATE || event == NB_EV_PREPARE) - flog_warn( - ref, - "%s: error processing configuration change: error [%s] event [%s] operation [%s] xpath [%s]", - __func__, nb_err_name(ret), - nb_event_name(event), - nb_operation_name(operation), xpath); - else - flog_err( - ref, - "%s: error processing configuration change: error [%s] event [%s] operation [%s] xpath [%s]", - __func__, nb_err_name(ret), - nb_event_name(event), - nb_operation_name(operation), xpath); + + flog(priority, ref, + "%s: error processing configuration change: error [%s] event [%s] operation [%s] xpath [%s]", + __func__, nb_err_name(ret), nb_event_name(event), + nb_operation_name(operation), xpath); } return ret; @@ -1548,6 +1517,116 @@ int nb_notification_send(const char *xpath, struct list *arguments) return ret; } +/* Running configuration user pointers management. */ +struct nb_config_entry { + char xpath[XPATH_MAXLEN]; + void *entry; +}; + +static bool running_config_entry_cmp(const void *value1, const void *value2) +{ + const struct nb_config_entry *c1 = value1; + const struct nb_config_entry *c2 = value2; + + return strmatch(c1->xpath, c2->xpath); +} + +static unsigned int running_config_entry_key_make(void *value) +{ + return string_hash_make(value); +} + +static void *running_config_entry_alloc(void *p) +{ + struct nb_config_entry *new, *key = p; + + new = XCALLOC(MTYPE_NB_CONFIG_ENTRY, sizeof(*new)); + strlcpy(new->xpath, key->xpath, sizeof(new->xpath)); + + return new; +} + +static void running_config_entry_free(void *arg) +{ + XFREE(MTYPE_NB_CONFIG_ENTRY, arg); +} + +void nb_running_set_entry(const struct lyd_node *dnode, void *entry) +{ + struct nb_config_entry *config, s; + + yang_dnode_get_path(dnode, s.xpath, sizeof(s.xpath)); + config = hash_get(running_config_entries, &s, + running_config_entry_alloc); + config->entry = entry; +} + +static void *nb_running_unset_entry_helper(const struct lyd_node *dnode) +{ + struct nb_config_entry *config, s; + struct lyd_node *child; + void *entry = NULL; + + yang_dnode_get_path(dnode, s.xpath, sizeof(s.xpath)); + config = hash_release(running_config_entries, &s); + if (config) { + entry = config->entry; + running_config_entry_free(config); + } + + /* Unset user pointers from the child nodes. */ + if (CHECK_FLAG(dnode->schema->nodetype, LYS_LIST | LYS_CONTAINER)) { + LY_TREE_FOR (dnode->child, child) { + (void)nb_running_unset_entry_helper(child); + } + } + + return entry; +} + +void *nb_running_unset_entry(const struct lyd_node *dnode) +{ + void *entry; + + entry = nb_running_unset_entry_helper(dnode); + assert(entry); + + return entry; +} + +void *nb_running_get_entry(const struct lyd_node *dnode, const char *xpath, + bool abort_if_not_found) +{ + const struct lyd_node *orig_dnode = dnode; + char xpath_buf[XPATH_MAXLEN]; + + assert(dnode || xpath); + + if (!dnode) + dnode = yang_dnode_get(running_config->dnode, xpath); + + while (dnode) { + struct nb_config_entry *config, s; + + yang_dnode_get_path(dnode, s.xpath, sizeof(s.xpath)); + config = hash_lookup(running_config_entries, &s); + if (config) + return config->entry; + + dnode = dnode->parent; + } + + if (!abort_if_not_found) + return NULL; + + yang_dnode_get_path(orig_dnode, xpath_buf, sizeof(xpath_buf)); + flog_err(EC_LIB_YANG_DNODE_NOT_FOUND, + "%s: failed to find entry [xpath %s]", __func__, xpath_buf); + zlog_backtrace(LOG_ERR); + abort(); +} + +/* Logging functions. */ const char *nb_event_name(enum nb_event event) { switch (event) { @@ -1677,14 +1756,11 @@ void nb_init(struct thread_master *tm, exit(1); } - /* Initialize the northbound database (used for the rollback log). */ - if (nb_db_init() != NB_OK) - flog_warn(EC_LIB_NB_DATABASE, - "%s: failed to initialize northbound database", - __func__); - /* Create an empty running configuration. */ running_config = nb_config_new(NULL); + running_config_entries = hash_create(running_config_entry_key_make, + running_config_entry_cmp, + "Running Configuration Entries"); /* Initialize the northbound CLI. */ nb_cli_init(tm); @@ -1699,5 +1775,7 @@ void nb_terminate(void) nb_nodes_delete(); /* Delete the running configuration. */ + hash_clean(running_config_entries, running_config_entry_free); + hash_free(running_config_entries); nb_config_free(running_config); } diff --git a/lib/northbound.h b/lib/northbound.h index bfa28b3f6..14f27c1d4 100644 --- a/lib/northbound.h +++ b/lib/northbound.h @@ -808,6 +808,75 @@ extern bool nb_operation_is_valid(enum nb_operation operation, extern int nb_notification_send(const char *xpath, struct list *arguments); /* + * Associate a user pointer to a configuration node. + * + * This should be called by northbound 'create' callbacks in the NB_EV_APPLY + * phase only. + * + * dnode + * libyang data node - only its XPath is used. + * + * entry + * Arbitrary user-specified pointer. + */ +extern void nb_running_set_entry(const struct lyd_node *dnode, void *entry); + +/* + * Unset the user pointer associated to a configuration node. + * + * This should be called by northbound 'destroy' callbacks in the NB_EV_APPLY + * phase only. + * + * dnode + * libyang data node - only its XPath is used. + * + * Returns: + * The user pointer that was unset. + */ +extern void *nb_running_unset_entry(const struct lyd_node *dnode); + +/* + * Find the user pointer (if any) associated to a configuration node. + * + * The XPath associated to the configuration node can be provided directly or + * indirectly through a libyang data node. + * + * If an user point is not found, this function follows the parent nodes in the + * running configuration until an user pointer is found or until the root node + * is reached. + * + * dnode + * libyang data node - only its XPath is used (can be NULL if 'xpath' is + * provided). + * + * xpath + * XPath of the configuration node (can be NULL if 'dnode' is provided). + * + * abort_if_not_found + * When set to true, abort the program if no user pointer is found. + * + * As a rule of thumb, this parameter should be set to true in the following + * scenarios: + * - Calling this function from any northbound configuration callback during + * the NB_EV_APPLY phase. + * - Calling this function from a 'delete' northbound configuration callback + * during any phase. + * + * In both the above cases, the given configuration node should contain an + * user pointer except when there's a bug in the code, in which case it's + * better to abort the program right away and eliminate the need for + * unnecessary NULL checks. + * + * In all other cases, this parameter should be set to false and the caller + * should check if the function returned NULL or not. + * + * Returns: + * User pointer if found, NULL otherwise. + */ +extern void *nb_running_get_entry(const struct lyd_node *dnode, const char *xpath, + bool abort_if_not_found); + +/* * Return a human-readable string representing a northbound event. * * event diff --git a/lib/northbound_sysrepo.c b/lib/northbound_sysrepo.c index 4359c39ca..33b6c2478 100644 --- a/lib/northbound_sysrepo.c +++ b/lib/northbound_sysrepo.c @@ -374,7 +374,7 @@ static int frr_sr_state_data_iter_cb(const struct lys_node *snode, /* Callback for state retrieval. */ static int frr_sr_state_cb(const char *xpath, sr_val_t **values, size_t *values_cnt, uint64_t request_id, - void *private_ctx) + const char *original_xpath, void *private_ctx) { struct list *elements; struct yang_data *data; diff --git a/lib/prefix.c b/lib/prefix.c index 52bb266f1..6b9196921 100644 --- a/lib/prefix.c +++ b/lib/prefix.c @@ -1359,6 +1359,35 @@ const char *prefix2str(union prefixconstptr pu, char *str, int size) return str; } +void prefix_mcast_inet4_dump(const char *onfail, struct in_addr addr, + char *buf, int buf_size) +{ + int save_errno = errno; + + if (addr.s_addr == INADDR_ANY) + strcpy(buf, "*"); + else { + if (!inet_ntop(AF_INET, &addr, buf, buf_size)) { + if (onfail) + snprintf(buf, buf_size, "%s", onfail); + } + } + + errno = save_errno; +} + +const char *prefix_sg2str(const struct prefix_sg *sg, char *sg_str) +{ + char src_str[INET_ADDRSTRLEN]; + char grp_str[INET_ADDRSTRLEN]; + + prefix_mcast_inet4_dump("<src?>", sg->src, src_str, sizeof(src_str)); + prefix_mcast_inet4_dump("<grp?>", sg->grp, grp_str, sizeof(grp_str)); + snprintf(sg_str, PREFIX_SG_STR_LEN, "(%s,%s)", src_str, grp_str); + + return sg_str; +} + struct prefix *prefix_new(void) { struct prefix *p; diff --git a/lib/prefix.h b/lib/prefix.h index a1c2086b8..d3c387e10 100644 --- a/lib/prefix.h +++ b/lib/prefix.h @@ -321,6 +321,15 @@ union prefixconstptr { /* Maximum string length of the result of prefix2str */ #define PREFIX_STRLEN 80 +/* + * Longest possible length of a (S,G) string is 36 bytes + * 123.123.123.123 = 15 * 2 + * (,) = 3 + * NULL Character at end = 1 + * (123.123.123.123,123.123.123.123) + */ +#define PREFIX_SG_STR_LEN 34 + /* Max bit/byte length of IPv4 address. */ #define IPV4_MAX_BYTELEN 4 #define IPV4_MAX_BITLEN 32 @@ -394,6 +403,9 @@ extern int str2prefix(const char *, struct prefix *); #define PREFIX2STR_BUFFER PREFIX_STRLEN +extern void prefix_mcast_inet4_dump(const char *onfail, struct in_addr addr, + char *buf, int buf_size); +extern const char *prefix_sg2str(const struct prefix_sg *sg, char *str); extern const char *prefix2str(union prefixconstptr, char *, int); extern int prefix_match(const struct prefix *, const struct prefix *); extern int prefix_match_network_statement(const struct prefix *, diff --git a/lib/privs.c b/lib/privs.c index 50f80e3c0..a3314c6c3 100644 --- a/lib/privs.c +++ b/lib/privs.c @@ -25,10 +25,25 @@ #include "privs.h" #include "memory.h" #include "lib_errors.h" +#include "lib/queue.h" +DEFINE_MTYPE_STATIC(LIB, PRIVS, "Privilege information") + +/* + * Different capabilities/privileges apis have different characteristics: some + * are process-wide, and some are per-thread. + */ #ifdef HAVE_CAPABILITIES +#ifdef HAVE_LCAPS +static const bool privs_per_process; /* = false */ +#elif defined(HAVE_SOLARIS_CAPABILITIES) +static const bool privs_per_process = true; +#endif +#else +static const bool privs_per_process = true; +#endif /* HAVE_CAPABILITIES */ -DEFINE_MTYPE_STATIC(LIB, PRIVS, "Privilege information") +#ifdef HAVE_CAPABILITIES /* sort out some generic internal types for: * @@ -698,25 +713,66 @@ static int getgrouplist(const char *user, gid_t group, gid_t *groups, } #endif /* HAVE_GETGROUPLIST */ +/* + * Helper function that locates a refcounting object to use: a process-wide + * object or a per-pthread object. + */ +static struct zebra_privs_refs_t *get_privs_refs(struct zebra_privs_t *privs) +{ + struct zebra_privs_refs_t *temp, *refs = NULL; + pthread_t tid; + + if (privs_per_process) + refs = &(privs->process_refs); + else { + /* Locate - or create - the object for the current pthread. */ + tid = pthread_self(); + + STAILQ_FOREACH(temp, &(privs->thread_refs), entry) { + if (pthread_equal(temp->tid, tid)) { + refs = temp; + break; + } + } + + /* Need to create a new refcounting object. */ + if (refs == NULL) { + refs = XCALLOC(MTYPE_PRIVS, + sizeof(struct zebra_privs_refs_t)); + refs->tid = tid; + STAILQ_INSERT_TAIL(&(privs->thread_refs), refs, entry); + } + } + + return refs; +} + struct zebra_privs_t *_zprivs_raise(struct zebra_privs_t *privs, const char *funcname) { int save_errno = errno; + struct zebra_privs_refs_t *refs; if (!privs) return NULL; - /* If we're already elevated, just return */ + /* + * Serialize 'raise' operations; particularly important for + * OSes where privs are process-wide. + */ pthread_mutex_lock(&(privs->mutex)); { - if (++(privs->refcount) == 1) { + /* Locate ref-counting object to use */ + refs = get_privs_refs(privs); + + if (++(refs->refcount) == 1) { errno = 0; if (privs->change(ZPRIVS_RAISE)) { zlog_err("%s: Failed to raise privileges (%s)", funcname, safe_strerror(errno)); } errno = save_errno; - privs->raised_in_funcname = funcname; + refs->raised_in_funcname = funcname; } } pthread_mutex_unlock(&(privs->mutex)); @@ -727,22 +783,27 @@ struct zebra_privs_t *_zprivs_raise(struct zebra_privs_t *privs, void _zprivs_lower(struct zebra_privs_t **privs) { int save_errno = errno; + struct zebra_privs_refs_t *refs; if (!*privs) return; - /* Don't lower privs if there's another caller */ + /* Serialize 'lower privs' operation - particularly important + * when OS privs are process-wide. + */ pthread_mutex_lock(&(*privs)->mutex); { - if (--((*privs)->refcount) == 0) { + refs = get_privs_refs(*privs); + + if (--(refs->refcount) == 0) { errno = 0; if ((*privs)->change(ZPRIVS_LOWER)) { zlog_err("%s: Failed to lower privileges (%s)", - (*privs)->raised_in_funcname, + refs->raised_in_funcname, safe_strerror(errno)); } errno = save_errno; - (*privs)->raised_in_funcname = NULL; + refs->raised_in_funcname = NULL; } } pthread_mutex_unlock(&(*privs)->mutex); @@ -761,7 +822,9 @@ void zprivs_preinit(struct zebra_privs_t *zprivs) } pthread_mutex_init(&(zprivs->mutex), NULL); - zprivs->refcount = 0; + zprivs->process_refs.refcount = 0; + zprivs->process_refs.raised_in_funcname = NULL; + STAILQ_INIT(&zprivs->thread_refs); if (zprivs->vty_group) { /* in a "NULL" setup, this is allowed to fail too, but still @@ -919,6 +982,8 @@ void zprivs_init(struct zebra_privs_t *zprivs) void zprivs_terminate(struct zebra_privs_t *zprivs) { + struct zebra_privs_refs_t *refs; + if (!zprivs) { fprintf(stderr, "%s: no privs struct given, terminating", __func__); @@ -941,6 +1006,11 @@ void zprivs_terminate(struct zebra_privs_t *zprivs) } #endif /* HAVE_LCAPS */ + while ((refs = STAILQ_FIRST(&(zprivs->thread_refs))) != NULL) { + STAILQ_REMOVE_HEAD(&(zprivs->thread_refs), entry); + XFREE(MTYPE_PRIVS, refs); + } + zprivs->change = zprivs_change_null; zprivs->current_state = zprivs_state_null; zprivs_null_state = ZPRIVS_LOWERED; diff --git a/lib/privs.h b/lib/privs.h index 01ddba462..2b0b44b3f 100644 --- a/lib/privs.h +++ b/lib/privs.h @@ -24,6 +24,7 @@ #define _ZEBRA_PRIVS_H #include <pthread.h> +#include "lib/queue.h" #ifdef __cplusplus extern "C" { @@ -56,6 +57,13 @@ typedef enum { ZPRIVS_LOWER, } zebra_privs_ops_t; +struct zebra_privs_refs_t { + STAILQ_ENTRY(zebra_privs_refs_t) entry; + pthread_t tid; + uint32_t refcount; + const char *raised_in_funcname; +}; + struct zebra_privs_t { zebra_capabilities_t *caps_p; /* caps required for operation */ zebra_capabilities_t *caps_i; /* caps to allow inheritance of */ @@ -63,11 +71,15 @@ struct zebra_privs_t { int cap_num_i; /* Mutex and counter used to avoid race conditions in multi-threaded - * processes. The privs elevation is process-wide, so we need to - * avoid changing the privilege status across threads. + * processes. If privs status is process-wide, we need to + * control changes to the privilege status among threads. + * If privs changes are per-thread, we need to be able to + * manage that too. */ pthread_mutex_t mutex; - uint32_t refcount; + struct zebra_privs_refs_t process_refs; + + STAILQ_HEAD(thread_refs_q, zebra_privs_refs_t) thread_refs; const char *user; /* user and group to run as */ const char *group; @@ -76,7 +88,6 @@ struct zebra_privs_t { int (*change)(zebra_privs_ops_t); /* change privileges, 0 on success */ zebra_privs_current_t (*current_state)( void); /* current privilege state */ - const char *raised_in_funcname; }; struct zprivs_ids_t { diff --git a/lib/vxlan.h b/lib/vxlan.h index 2a8077f8c..69d393959 100644 --- a/lib/vxlan.h +++ b/lib/vxlan.h @@ -38,6 +38,7 @@ typedef uint32_t vni_t; enum vxlan_flood_control { VXLAN_FLOOD_HEAD_END_REPL = 0, VXLAN_FLOOD_DISABLED, + VXLAN_FLOOD_PIM_SM, }; #ifdef __cplusplus diff --git a/lib/workqueue.c b/lib/workqueue.c index 066d81f35..54090d0d0 100644 --- a/lib/workqueue.c +++ b/lib/workqueue.c @@ -280,7 +280,7 @@ int work_queue_run(struct thread *thread) if (item->ran > wq->spec.max_retries) { /* run error handler, if any */ if (wq->spec.errorfunc) - wq->spec.errorfunc(wq, item->data); + wq->spec.errorfunc(wq, item); work_queue_item_remove(wq, item); continue; } diff --git a/lib/yang.c b/lib/yang.c index 7982d14fd..2a2c155de 100644 --- a/lib/yang.c +++ b/lib/yang.c @@ -20,7 +20,6 @@ #include <zebra.h> #include "log.h" -#include "log_int.h" #include "lib_errors.h" #include "yang.h" #include "yang_translator.h" @@ -513,42 +512,6 @@ void yang_dnode_change_leaf(struct lyd_node *dnode, const char *value) lyd_change_leaf((struct lyd_node_leaf_list *)dnode, value); } -void yang_dnode_set_entry(const struct lyd_node *dnode, void *entry) -{ - assert(CHECK_FLAG(dnode->schema->nodetype, LYS_LIST | LYS_CONTAINER)); - lyd_set_private(dnode, entry); -} - -void *yang_dnode_get_entry(const struct lyd_node *dnode, - bool abort_if_not_found) -{ - const struct lyd_node *orig_dnode = dnode; - char xpath[XPATH_MAXLEN]; - - while (dnode) { - switch (dnode->schema->nodetype) { - case LYS_CONTAINER: - case LYS_LIST: - if (dnode->priv) - return dnode->priv; - break; - default: - break; - } - - dnode = dnode->parent; - } - - if (!abort_if_not_found) - return NULL; - - yang_dnode_get_path(orig_dnode, xpath, sizeof(xpath)); - flog_err(EC_LIB_YANG_DNODE_NOT_FOUND, - "%s: failed to find entry [xpath %s]", __func__, xpath); - zlog_backtrace(LOG_ERR); - abort(); -} - struct lyd_node *yang_dnode_new(struct ly_ctx *ly_ctx, bool config_only) { struct lyd_node *dnode; @@ -629,12 +592,6 @@ struct yang_data *yang_data_list_find(const struct list *list, return NULL; } -static void *ly_dup_cb(const void *priv) -{ - /* Make a shallow copy of the priv pointer. */ - return (void *)priv; -} - /* Make libyang log its errors using FRR logging infrastructure. */ static void ly_log_cb(LY_LOG_LEVEL level, const char *msg, const char *path) { @@ -724,7 +681,6 @@ CPP_NOTICE("lib/yang: deprecated libyang <0.16.74 extension loading in use!") flog_err(EC_LIB_LIBYANG, "%s: ly_ctx_new() failed", __func__); exit(1); } - ly_ctx_set_priv_dup_clb(ly_native_ctx, ly_dup_cb); #ifndef LIBYANG_EXT_BUILTIN /* Detect if the required libyang plugin(s) were loaded successfully. */ diff --git a/lib/yang.h b/lib/yang.h index 15f0ec7ae..6f8c84ab6 100644 --- a/lib/yang.h +++ b/lib/yang.h @@ -403,51 +403,6 @@ extern bool yang_dnode_is_default_recursive(const struct lyd_node *dnode); extern void yang_dnode_change_leaf(struct lyd_node *dnode, const char *value); /* - * Set the libyang private pointer to a user pointer. Can only be used on YANG - * lists and containers. - * - * dnode - * libyang data node to operate on. - * - * entry - * Arbitrary user-specified pointer. - */ -extern void yang_dnode_set_entry(const struct lyd_node *dnode, void *entry); - -/* - * Find the user pointer associated to the given libyang data node. - * - * The data node is traversed by following the parent pointers until an user - * pointer is found or until the root node is reached. - * - * dnode - * libyang data node to operate on. - * - * abort_if_not_found - * When set to true, abort the program if no user pointer is found. - * - * As a rule of thumb, this parameter should be set to true in the following - * scenarios: - * - Calling this function from any northbound configuration callback during - * the NB_EV_APPLY phase. - * - Calling this function from a 'delete' northbound configuration callback - * during any phase. - * - * In both the above cases, the libyang data node should contain an user - * pointer except when there's a bug in the code, in which case it's better - * to abort the program right away and eliminate the need for unnecessary - * NULL checks. - * - * In all other cases, this parameter should be set to false and the caller - * should check if the function returned NULL or not. - * - * Returns: - * User pointer if found, NULL otherwise. - */ -extern void *yang_dnode_get_entry(const struct lyd_node *dnode, - bool abort_if_not_found); - -/* * Create a new libyang data node. * * ly_ctx diff --git a/lib/zclient.c b/lib/zclient.c index e5cab9e0f..4901c9274 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -2701,6 +2701,17 @@ static int zclient_read(struct thread *thread) (*zclient->iptable_notify_owner)(command, zclient, length, vrf_id); + break; + case ZEBRA_VXLAN_SG_ADD: + if (zclient->vxlan_sg_add) + (*zclient->vxlan_sg_add)(command, zclient, length, + vrf_id); + break; + case ZEBRA_VXLAN_SG_DEL: + if (zclient->vxlan_sg_del) + (*zclient->vxlan_sg_del)(command, zclient, length, + vrf_id); + break; default: break; } diff --git a/lib/zclient.h b/lib/zclient.h index 3a054e5e7..0926281f2 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -164,6 +164,8 @@ typedef enum { ZEBRA_IPTABLE_DELETE, ZEBRA_IPTABLE_NOTIFY_OWNER, ZEBRA_VXLAN_FLOOD_CONTROL, + ZEBRA_VXLAN_SG_ADD, + ZEBRA_VXLAN_SG_DEL, } zebra_message_types_t; struct redist_proto { @@ -275,6 +277,10 @@ struct zclient { struct zclient *zclient, uint16_t length, vrf_id_t vrf_id); + int (*vxlan_sg_add)(int command, struct zclient *client, + uint16_t length, vrf_id_t vrf_id); + int (*vxlan_sg_del)(int command, struct zclient *client, + uint16_t length, vrf_id_t vrf_id_t); }; /* Zebra API message flag. */ diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index 2d5acb87a..9c1cb3801 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -60,6 +60,7 @@ #include "pim_ssm.h" #include "pim_nht.h" #include "pim_bfd.h" +#include "pim_vxlan.h" #include "bfd.h" #ifndef VTYSH_EXTRACT_PL @@ -1696,7 +1697,8 @@ static void pim_show_join_helper(struct vty *vty, struct pim_interface *pim_ifp, } } -static void pim_show_join(struct pim_instance *pim, struct vty *vty, bool uj) +static void pim_show_join(struct pim_instance *pim, struct vty *vty, + struct prefix_sg *sg, bool uj) { struct pim_interface *pim_ifp; struct pim_ifchannel *ch; @@ -1718,6 +1720,12 @@ static void pim_show_join(struct pim_instance *pim, struct vty *vty, bool uj) continue; RB_FOREACH (ch, pim_ifchannel_rb, &pim_ifp->ifchannel_rb) { + if (sg->grp.s_addr != 0 + && sg->grp.s_addr != ch->sg.grp.s_addr) + continue; + if (sg->src.s_addr != 0 + && sg->src.s_addr != ch->sg.src.s_addr) + continue; pim_show_join_helper(vty, pim_ifp, ch, json, now, uj); } /* scan interface channels */ } @@ -1956,7 +1964,7 @@ static void pim_show_state(struct pim_instance *pim, struct vty *vty, json = json_object_new_object(); } else { vty_out(vty, - "Codes: J -> Pim Join, I -> IGMP Report, S -> Source, * -> Inherited from (*,G)"); + "Codes: J -> Pim Join, I -> IGMP Report, S -> Source, * -> Inherited from (*,G), V -> VxLAN"); vty_out(vty, "\nInstalled Source Group IIF OIL\n"); } @@ -2085,7 +2093,7 @@ static void pim_show_state(struct pim_instance *pim, struct vty *vty, } else { if (first_oif) { first_oif = 0; - vty_out(vty, "%s(%c%c%c%c)", out_ifname, + vty_out(vty, "%s(%c%c%c%c%c)", out_ifname, (c_oil->oif_flags[oif_vif_index] & PIM_OIF_FLAG_PROTO_IGMP) ? 'I' @@ -2095,6 +2103,10 @@ static void pim_show_state(struct pim_instance *pim, struct vty *vty, ? 'J' : ' ', (c_oil->oif_flags[oif_vif_index] + & PIM_OIF_FLAG_PROTO_VXLAN) + ? 'V' + : ' ', + (c_oil->oif_flags[oif_vif_index] & PIM_OIF_FLAG_PROTO_SOURCE) ? 'S' : ' ', @@ -2103,7 +2115,7 @@ static void pim_show_state(struct pim_instance *pim, struct vty *vty, ? '*' : ' '); } else - vty_out(vty, ", %s(%c%c%c%c)", + vty_out(vty, ", %s(%c%c%c%c%c)", out_ifname, (c_oil->oif_flags[oif_vif_index] & PIM_OIF_FLAG_PROTO_IGMP) @@ -2114,6 +2126,10 @@ static void pim_show_state(struct pim_instance *pim, struct vty *vty, ? 'J' : ' ', (c_oil->oif_flags[oif_vif_index] + & PIM_OIF_FLAG_PROTO_VXLAN) + ? 'V' + : ' ', + (c_oil->oif_flags[oif_vif_index] & PIM_OIF_FLAG_PROTO_SOURCE) ? 'S' : ' ', @@ -2295,6 +2311,41 @@ static void json_object_pim_upstream_add(json_object *json, /* XXX: need to print ths flag in the plain text display as well */ if (up->flags & PIM_UPSTREAM_FLAG_MASK_SRC_MSDP) json_object_boolean_true_add(json, "sourceMsdp"); + + if (up->flags & PIM_UPSTREAM_FLAG_MASK_SEND_SG_RPT_PRUNE) + json_object_boolean_true_add(json, "sendSGRptPrune"); + + if (up->flags & PIM_UPSTREAM_FLAG_MASK_SRC_LHR) + json_object_boolean_true_add(json, "lastHopRouter"); + + if (up->flags & PIM_UPSTREAM_FLAG_MASK_DISABLE_KAT_EXPIRY) + json_object_boolean_true_add(json, "disableKATExpiry"); + + if (up->flags & PIM_UPSTREAM_FLAG_MASK_STATIC_IIF) + json_object_boolean_true_add(json, "staticIncomingInterface"); + + if (up->flags & PIM_UPSTREAM_FLAG_MASK_ALLOW_IIF_IN_OIL) + json_object_boolean_true_add(json, + "allowIncomingInterfaceinOil"); + + if (up->flags & PIM_UPSTREAM_FLAG_MASK_NO_PIMREG_DATA) + json_object_boolean_true_add(json, "noPimRegistrationData"); + + if (up->flags & PIM_UPSTREAM_FLAG_MASK_FORCE_PIMREG) + json_object_boolean_true_add(json, "forcePimRegistration"); + + if (up->flags & PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_ORIG) + json_object_boolean_true_add(json, "sourceVxlanOrigination"); + + if (up->flags & PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_TERM) + json_object_boolean_true_add(json, "sourceVxlanTermination"); + + if (up->flags & PIM_UPSTREAM_FLAG_MASK_MLAG_VXLAN) + json_object_boolean_true_add(json, "mlagVxlan"); + + if (up->flags & PIM_UPSTREAM_FLAG_MASK_MLAG_NON_DF) + json_object_boolean_true_add(json, + "mlagNonDesignatedForwarder"); } static const char * @@ -2335,7 +2386,7 @@ static const char *pim_reg_state2brief_str(enum pim_reg_state reg_state, } static void pim_show_upstream(struct pim_instance *pim, struct vty *vty, - bool uj) + struct prefix_sg *sg, bool uj) { struct listnode *upnode; struct pim_upstream *up; @@ -2362,6 +2413,11 @@ static void pim_show_upstream(struct pim_instance *pim, struct vty *vty, char msdp_reg_timer[10]; char state_str[PIM_REG_STATE_STR_LEN]; + if (sg->grp.s_addr != 0 && sg->grp.s_addr != up->sg.grp.s_addr) + continue; + if (sg->src.s_addr != 0 && sg->src.s_addr != up->sg.src.s_addr) + continue; + pim_inet4_dump("<src?>", up->sg.src, src_str, sizeof(src_str)); pim_inet4_dump("<grp?>", up->sg.grp, grp_str, sizeof(grp_str)); pim_time_uptime(uptime, sizeof(uptime), @@ -3777,24 +3833,45 @@ DEFUN (show_ip_pim_interface_vrf_all, return CMD_SUCCESS; } -DEFUN (show_ip_pim_join, +DEFPY (show_ip_pim_join, show_ip_pim_join_cmd, - "show ip pim [vrf NAME] join [json]", + "show ip pim [vrf NAME] join [A.B.C.D$s_or_g [A.B.C.D$g]] [json$json]", SHOW_STR IP_STR PIM_STR VRF_CMD_HELP_STR "PIM interface join information\n" + "The Source or Group\n" + "The Group\n" JSON_STR) { - int idx = 2; - struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); - bool uj = use_json(argc, argv); + struct prefix_sg sg = {0}; + struct vrf *v; + bool uj = !!json; + struct pim_instance *pim; - if (!vrf) + v = vrf_lookup_by_name(vrf ? vrf : VRF_DEFAULT_NAME); + + if (!v) { + vty_out(vty, "%% Vrf specified: %s does not exist\n", vrf); return CMD_WARNING; + } + pim = pim_get_pim_instance(v->vrf_id); + + if (!pim) { + vty_out(vty, "%% Unable to find pim instance\n"); + return CMD_WARNING; + } + + if (s_or_g.s_addr != 0) { + if (g.s_addr != 0) { + sg.src = s_or_g; + sg.grp = g; + } else + sg.grp = s_or_g; + } - pim_show_join(vrf->info, vty, uj); + pim_show_join(pim, vty, &sg, uj); return CMD_SUCCESS; } @@ -3809,6 +3886,7 @@ DEFUN (show_ip_pim_join_vrf_all, "PIM interface join information\n" JSON_STR) { + struct prefix_sg sg = {0}; bool uj = use_json(argc, argv); struct vrf *vrf; bool first = true; @@ -3823,7 +3901,7 @@ DEFUN (show_ip_pim_join_vrf_all, first = false; } else vty_out(vty, "VRF: %s\n", vrf->name); - pim_show_join(vrf->info, vty, uj); + pim_show_join(vrf->info, vty, &sg, uj); } if (uj) vty_out(vty, "}\n"); @@ -4022,24 +4100,44 @@ DEFUN (show_ip_pim_state_vrf_all, return CMD_SUCCESS; } -DEFUN (show_ip_pim_upstream, +DEFPY (show_ip_pim_upstream, show_ip_pim_upstream_cmd, - "show ip pim [vrf NAME] upstream [json]", + "show ip pim [vrf NAME] upstream [A.B.C.D$s_or_g [A.B.C.D$g]] [json$json]", SHOW_STR IP_STR PIM_STR VRF_CMD_HELP_STR "PIM upstream information\n" + "The Source or Group\n" + "The Group\n" JSON_STR) { - int idx = 2; - struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); - bool uj = use_json(argc, argv); + struct prefix_sg sg = {0}; + struct vrf *v; + bool uj = !!json; + struct pim_instance *pim; - if (!vrf) + v = vrf_lookup_by_name(vrf ? vrf : VRF_DEFAULT_NAME); + + if (!v) { + vty_out(vty, "%% Vrf specified: %s does not exist\n", vrf); + return CMD_WARNING; + } + pim = pim_get_pim_instance(v->vrf_id); + + if (!pim) { + vty_out(vty, "%% Unable to find pim instance\n"); return CMD_WARNING; + } - pim_show_upstream(vrf->info, vty, uj); + if (s_or_g.s_addr != 0) { + if (g.s_addr != 0) { + sg.src = s_or_g; + sg.grp = g; + } else + sg.grp = s_or_g; + } + pim_show_upstream(pim, vty, &sg, uj); return CMD_SUCCESS; } @@ -4054,6 +4152,7 @@ DEFUN (show_ip_pim_upstream_vrf_all, "PIM upstream information\n" JSON_STR) { + struct prefix_sg sg = {0}; bool uj = use_json(argc, argv); struct vrf *vrf; bool first = true; @@ -4068,7 +4167,7 @@ DEFUN (show_ip_pim_upstream_vrf_all, first = false; } else vty_out(vty, "VRF: %s\n", vrf->name); - pim_show_upstream(vrf->info, vty, uj); + pim_show_upstream(vrf->info, vty, &sg, uj); } return CMD_SUCCESS; @@ -4493,8 +4592,8 @@ DEFUN (show_ip_multicast_vrf_all, return CMD_SUCCESS; } -static void show_mroute(struct pim_instance *pim, struct vty *vty, bool fill, - bool uj) +static void show_mroute(struct pim_instance *pim, struct vty *vty, + struct prefix_sg *sg, bool fill, bool uj) { struct listnode *node; struct channel_oil *c_oil; @@ -4531,6 +4630,13 @@ static void show_mroute(struct pim_instance *pim, struct vty *vty, bool fill, if (!c_oil->installed && !uj) continue; + if (sg->grp.s_addr != 0 && + sg->grp.s_addr != c_oil->oil.mfcc_mcastgrp.s_addr) + continue; + if (sg->src.s_addr != 0 && + sg->src.s_addr != c_oil->oil.mfcc_origin.s_addr) + continue; + pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp, grp_str, sizeof(grp_str)); pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin, src_str, @@ -4617,6 +4723,11 @@ static void show_mroute(struct pim_instance *pim, struct vty *vty, bool fill, json_ifp_out, "protocolIgmp"); if (c_oil->oif_flags[oif_vif_index] + & PIM_OIF_FLAG_PROTO_VXLAN) + json_object_boolean_true_add( + json_ifp_out, "protocolVxlan"); + + if (c_oil->oif_flags[oif_vif_index] & PIM_OIF_FLAG_PROTO_SOURCE) json_object_boolean_true_add( json_ifp_out, "protocolSource"); @@ -4659,6 +4770,11 @@ static void show_mroute(struct pim_instance *pim, struct vty *vty, bool fill, } if (c_oil->oif_flags[oif_vif_index] + & PIM_OIF_FLAG_PROTO_VXLAN) { + strcpy(proto, "VxLAN"); + } + + if (c_oil->oif_flags[oif_vif_index] & PIM_OIF_FLAG_PROTO_SOURCE) { strcpy(proto, "SRC"); } @@ -4818,28 +4934,43 @@ static void show_mroute(struct pim_instance *pim, struct vty *vty, bool fill, } } -DEFUN (show_ip_mroute, +DEFPY (show_ip_mroute, show_ip_mroute_cmd, - "show ip mroute [vrf NAME] [fill] [json]", + "show ip mroute [vrf NAME] [A.B.C.D$s_or_g [A.B.C.D$g]] [fill$fill] [json$json]", SHOW_STR IP_STR MROUTE_STR VRF_CMD_HELP_STR + "The Source or Group\n" + "The Group\n" "Fill in Assumed data\n" JSON_STR) { - bool uj = use_json(argc, argv); - bool fill = false; - int idx = 2; - struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); + struct prefix_sg sg = {0}; + struct pim_instance *pim; + struct vrf *v; - if (!vrf) + v = vrf_lookup_by_name(vrf ? vrf : VRF_DEFAULT_NAME); + + if (!v) { + vty_out(vty, "%% Vrf specified: %s does not exist\n", vrf); return CMD_WARNING; + } + pim = pim_get_pim_instance(v->vrf_id); - if (argv_find(argv, argc, "fill", &idx)) - fill = true; + if (!pim) { + vty_out(vty, "%% Unable to find pim instance\n"); + return CMD_WARNING; + } - show_mroute(vrf->info, vty, fill, uj); + if (s_or_g.s_addr != 0) { + if (g.s_addr != 0) { + sg.src = s_or_g; + sg.grp = g; + } else + sg.grp = s_or_g; + } + show_mroute(pim, vty, &sg, !!fill, !!json); return CMD_SUCCESS; } @@ -4853,6 +4984,7 @@ DEFUN (show_ip_mroute_vrf_all, "Fill in Assumed data\n" JSON_STR) { + struct prefix_sg sg = {0}; bool uj = use_json(argc, argv); int idx = 4; struct vrf *vrf; @@ -4872,7 +5004,7 @@ DEFUN (show_ip_mroute_vrf_all, first = false; } else vty_out(vty, "VRF: %s\n", vrf->name); - show_mroute(vrf->info, vty, fill, uj); + show_mroute(vrf->info, vty, &sg, fill, uj); } if (uj) vty_out(vty, "}\n"); @@ -5770,7 +5902,8 @@ static int pim_cmd_igmp_start(struct vty *vty, struct interface *ifp) pim_ifp = ifp->info; if (!pim_ifp) { - pim_ifp = pim_if_new(ifp, true, false, false); + pim_ifp = pim_if_new(ifp, true, false, false, + false /*vxlan_term*/); if (!pim_ifp) { vty_out(vty, "Could not enable IGMP on interface %s\n", ifp->name); @@ -6381,7 +6514,8 @@ static int pim_cmd_interface_add(struct interface *ifp) struct pim_interface *pim_ifp = ifp->info; if (!pim_ifp) { - pim_ifp = pim_if_new(ifp, false, true, false); + pim_ifp = pim_if_new(ifp, false, true, false, + false /*vxlan_term*/); if (!pim_ifp) { return 0; } @@ -7380,6 +7514,29 @@ DEFUN (no_debug_pim_zebra, return CMD_SUCCESS; } +DEFUN (debug_pim_vxlan, + debug_pim_vxlan_cmd, + "debug pim vxlan", + DEBUG_STR + DEBUG_PIM_STR + DEBUG_PIM_VXLAN_STR) +{ + PIM_DO_DEBUG_VXLAN; + return CMD_SUCCESS; +} + +DEFUN (no_debug_pim_vxlan, + no_debug_pim_vxlan_cmd, + "no debug pim vxlan", + NO_STR + DEBUG_STR + DEBUG_PIM_STR + DEBUG_PIM_VXLAN_STR) +{ + PIM_DONT_DEBUG_VXLAN; + return CMD_SUCCESS; +} + DEFUN (debug_msdp, debug_msdp_cmd, "debug msdp", @@ -8681,6 +8838,369 @@ DEFUN (show_ip_msdp_sa_sg_vrf_all, return CMD_SUCCESS; } +struct pim_sg_cache_walk_data { + struct vty *vty; + json_object *json; + json_object *json_group; + struct in_addr addr; + bool addr_match; +}; + +static void pim_show_vxlan_sg_entry(struct pim_vxlan_sg *vxlan_sg, + struct pim_sg_cache_walk_data *cwd) +{ + struct vty *vty = cwd->vty; + json_object *json = cwd->json; + char src_str[INET_ADDRSTRLEN]; + char grp_str[INET_ADDRSTRLEN]; + json_object *json_row; + bool installed = (vxlan_sg->up)?TRUE:FALSE; + const char *iif_name = vxlan_sg->iif?vxlan_sg->iif->name:"-"; + const char *oif_name; + + if (pim_vxlan_is_orig_mroute(vxlan_sg)) + oif_name = vxlan_sg->orig_oif?vxlan_sg->orig_oif->name:""; + else + oif_name = vxlan_sg->term_oif?vxlan_sg->term_oif->name:""; + + if (cwd->addr_match && (vxlan_sg->sg.src.s_addr != cwd->addr.s_addr) && + (vxlan_sg->sg.grp.s_addr != cwd->addr.s_addr)) { + return; + } + pim_inet4_dump("<src?>", vxlan_sg->sg.src, src_str, sizeof(src_str)); + pim_inet4_dump("<grp?>", vxlan_sg->sg.grp, grp_str, sizeof(grp_str)); + if (json) { + json_object_object_get_ex(json, grp_str, &cwd->json_group); + + if (!cwd->json_group) { + cwd->json_group = json_object_new_object(); + json_object_object_add(json, grp_str, + cwd->json_group); + } + + json_row = json_object_new_object(); + json_object_string_add(json_row, "source", src_str); + json_object_string_add(json_row, "group", grp_str); + json_object_string_add(json_row, "input", iif_name); + json_object_string_add(json_row, "output", oif_name); + if (installed) + json_object_boolean_true_add(json_row, "installed"); + else + json_object_boolean_false_add(json_row, "installed"); + json_object_object_add(cwd->json_group, src_str, json_row); + } else { + vty_out(vty, "%-15s %-15s %-15s %-15s %-5s\n", + src_str, grp_str, iif_name, oif_name, + installed?"I":""); + } +} + +static void pim_show_vxlan_sg_hash_entry(struct hash_backet *backet, void *arg) +{ + pim_show_vxlan_sg_entry((struct pim_vxlan_sg *)backet->data, + (struct pim_sg_cache_walk_data *)arg); +} + +static void pim_show_vxlan_sg(struct pim_instance *pim, + struct vty *vty, bool uj) +{ + json_object *json = NULL; + struct pim_sg_cache_walk_data cwd; + + if (uj) { + json = json_object_new_object(); + } else { + vty_out(vty, "Codes: I -> installed\n"); + vty_out(vty, + "Source Group Input Output Flags\n"); + } + + memset(&cwd, 0, sizeof(cwd)); + cwd.vty = vty; + cwd.json = json; + hash_iterate(pim->vxlan.sg_hash, pim_show_vxlan_sg_hash_entry, &cwd); + + if (uj) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } +} + +static void pim_show_vxlan_sg_match_addr(struct pim_instance *pim, + struct vty *vty, char *addr_str, bool uj) +{ + json_object *json = NULL; + struct pim_sg_cache_walk_data cwd; + int result = 0; + + memset(&cwd, 0, sizeof(cwd)); + result = inet_pton(AF_INET, addr_str, &cwd.addr); + if (result <= 0) { + vty_out(vty, "Bad address %s: errno=%d: %s\n", addr_str, + errno, safe_strerror(errno)); + return; + } + + if (uj) { + json = json_object_new_object(); + } else { + vty_out(vty, "Codes: I -> installed\n"); + vty_out(vty, + "Source Group Input Output Flags\n"); + } + + cwd.vty = vty; + cwd.json = json; + cwd.addr_match = TRUE; + hash_iterate(pim->vxlan.sg_hash, pim_show_vxlan_sg_hash_entry, &cwd); + + if (uj) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } +} + +static void pim_show_vxlan_sg_one(struct pim_instance *pim, + struct vty *vty, char *src_str, char *grp_str, bool uj) +{ + json_object *json = NULL; + struct prefix_sg sg; + int result = 0; + struct pim_vxlan_sg *vxlan_sg; + const char *iif_name; + bool installed; + const char *oif_name; + + result = inet_pton(AF_INET, src_str, &sg.src); + if (result <= 0) { + vty_out(vty, "Bad src address %s: errno=%d: %s\n", src_str, + errno, safe_strerror(errno)); + return; + } + result = inet_pton(AF_INET, grp_str, &sg.grp); + if (result <= 0) { + vty_out(vty, "Bad grp address %s: errno=%d: %s\n", grp_str, + errno, safe_strerror(errno)); + return; + } + + sg.family = AF_INET; + sg.prefixlen = IPV4_MAX_BITLEN; + if (uj) + json = json_object_new_object(); + + vxlan_sg = pim_vxlan_sg_find(pim, &sg); + if (vxlan_sg) { + installed = (vxlan_sg->up)?TRUE:FALSE; + iif_name = vxlan_sg->iif?vxlan_sg->iif->name:"-"; + + if (pim_vxlan_is_orig_mroute(vxlan_sg)) + oif_name = + vxlan_sg->orig_oif?vxlan_sg->orig_oif->name:""; + else + oif_name = + vxlan_sg->term_oif?vxlan_sg->term_oif->name:""; + + if (uj) { + json_object_string_add(json, "source", src_str); + json_object_string_add(json, "group", grp_str); + json_object_string_add(json, "input", iif_name); + json_object_string_add(json, "output", oif_name); + if (installed) + json_object_boolean_true_add(json, "installed"); + else + json_object_boolean_false_add(json, + "installed"); + } else { + vty_out(vty, "SG : %s\n", vxlan_sg->sg_str); + vty_out(vty, " Input : %s\n", iif_name); + vty_out(vty, " Output : %s\n", oif_name); + vty_out(vty, " installed : %s\n", + installed?"yes":"no"); + } + } + + if (uj) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } +} + +DEFUN (show_ip_pim_vxlan_sg, + show_ip_pim_vxlan_sg_cmd, + "show ip pim [vrf NAME] vxlan-groups [A.B.C.D [A.B.C.D]] [json]", + SHOW_STR + IP_STR + PIM_STR + VRF_CMD_HELP_STR + "VxLAN BUM groups\n" + "source or group ip\n" + "group ip\n" + JSON_STR) +{ + bool uj = use_json(argc, argv); + struct vrf *vrf; + int idx = 2; + + vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); + + if (!vrf) + return CMD_WARNING; + + char *src_ip = argv_find(argv, argc, "A.B.C.D", &idx) ? + argv[idx++]->arg:NULL; + char *grp_ip = idx < argc && argv_find(argv, argc, "A.B.C.D", &idx) ? + argv[idx]->arg:NULL; + + if (src_ip && grp_ip) + pim_show_vxlan_sg_one(vrf->info, vty, src_ip, grp_ip, uj); + else if (src_ip) + pim_show_vxlan_sg_match_addr(vrf->info, vty, src_ip, uj); + else + pim_show_vxlan_sg(vrf->info, vty, uj); + + return CMD_SUCCESS; +} + +static void pim_show_vxlan_sg_work(struct pim_instance *pim, + struct vty *vty, bool uj) +{ + json_object *json = NULL; + struct pim_sg_cache_walk_data cwd; + struct listnode *node; + struct pim_vxlan_sg *vxlan_sg; + + if (uj) { + json = json_object_new_object(); + } else { + vty_out(vty, "Codes: I -> installed\n"); + vty_out(vty, + "Source Group Input Flags\n"); + } + + memset(&cwd, 0, sizeof(cwd)); + cwd.vty = vty; + cwd.json = json; + for (ALL_LIST_ELEMENTS_RO(pim_vxlan_p->work_list, node, vxlan_sg)) + pim_show_vxlan_sg_entry(vxlan_sg, &cwd); + + if (uj) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } +} + +DEFUN_HIDDEN (show_ip_pim_vxlan_sg_work, + show_ip_pim_vxlan_sg_work_cmd, + "show ip pim [vrf NAME] vxlan-work [json]", + SHOW_STR + IP_STR + PIM_STR + VRF_CMD_HELP_STR + "VxLAN work list\n" + JSON_STR) +{ + bool uj = use_json(argc, argv); + struct vrf *vrf; + int idx = 2; + + vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); + + if (!vrf) + return CMD_WARNING; + + pim_show_vxlan_sg_work(vrf->info, vty, uj); + + return CMD_SUCCESS; +} + +DEFUN_HIDDEN (no_ip_pim_mlag, + no_ip_pim_mlag_cmd, + "no ip pim mlag", + NO_STR + IP_STR + PIM_STR + "MLAG\n") +{ + struct in_addr addr; + + addr.s_addr = 0; + pim_vxlan_mlag_update(TRUE /*mlag_enable*/, + FALSE /*peer_state*/, PIM_VXLAN_MLAG_ROLE_SECONDARY, + NULL/*peerlink*/, &addr); + + return CMD_SUCCESS; +} + +DEFUN_HIDDEN (ip_pim_mlag, + ip_pim_mlag_cmd, + "ip pim mlag INTERFACE role [primary|secondary] state [up|down] addr A.B.C.D", + IP_STR + PIM_STR + "MLAG\n" + "peerlink sub interface\n" + "MLAG role\n" + "MLAG role primary\n" + "MLAG role secondary\n" + "peer session state\n" + "peer session state up\n" + "peer session state down\n" + "configure PIP\n" + "unique ip address\n") +{ + struct interface *ifp; + const char *peerlink; + uint32_t role; + int idx; + bool peer_state; + int result; + struct in_addr reg_addr; + + idx = 3; + peerlink = argv[idx]->arg; + ifp = if_lookup_by_name(peerlink, VRF_DEFAULT); + if (!ifp) { + vty_out(vty, "No such interface name %s\n", peerlink); + return CMD_WARNING; + } + + idx += 2; + if (!strcmp(argv[idx]->arg, "primary")) { + role = PIM_VXLAN_MLAG_ROLE_PRIMARY; + } else if (!strcmp(argv[idx]->arg, "secondary")) { + role = PIM_VXLAN_MLAG_ROLE_SECONDARY; + } else { + vty_out(vty, "unknown MLAG role %s\n", argv[idx]->arg); + return CMD_WARNING; + } + + idx += 2; + if (!strcmp(argv[idx]->arg, "up")) { + peer_state = TRUE; + } else if (strcmp(argv[idx]->arg, "down")) { + peer_state = FALSE; + } else { + vty_out(vty, "unknown MLAG state %s\n", argv[idx]->arg); + return CMD_WARNING; + } + + idx += 2; + result = inet_pton(AF_INET, argv[idx]->arg, ®_addr); + if (result <= 0) { + vty_out(vty, "%% Bad reg address %s: errno=%d: %s\n", + argv[idx]->arg, + errno, safe_strerror(errno)); + return CMD_WARNING_CONFIG_FAILED; + } + pim_vxlan_mlag_update(TRUE, peer_state, role, ifp, ®_addr); + + return CMD_SUCCESS; +} + void pim_cmd_init(void) { install_node(&interface_node, @@ -8754,6 +9274,8 @@ void pim_cmd_init(void) install_element(VRF_NODE, &ip_pim_ecmp_rebalance_cmd); install_element(CONFIG_NODE, &no_ip_pim_ecmp_rebalance_cmd); install_element(VRF_NODE, &no_ip_pim_ecmp_rebalance_cmd); + install_element(CONFIG_NODE, &ip_pim_mlag_cmd); + install_element(CONFIG_NODE, &no_ip_pim_mlag_cmd); install_element(INTERFACE_NODE, &interface_ip_igmp_cmd); install_element(INTERFACE_NODE, &interface_no_ip_igmp_cmd); @@ -8880,6 +9402,8 @@ void pim_cmd_init(void) install_element(ENABLE_NODE, &no_debug_ssmpingd_cmd); install_element(ENABLE_NODE, &debug_pim_zebra_cmd); install_element(ENABLE_NODE, &no_debug_pim_zebra_cmd); + install_element(ENABLE_NODE, &debug_pim_vxlan_cmd); + install_element(ENABLE_NODE, &no_debug_pim_vxlan_cmd); install_element(ENABLE_NODE, &debug_msdp_cmd); install_element(ENABLE_NODE, &no_debug_msdp_cmd); install_element(ENABLE_NODE, &debug_msdp_events_cmd); @@ -8921,6 +9445,8 @@ void pim_cmd_init(void) install_element(CONFIG_NODE, &no_debug_ssmpingd_cmd); install_element(CONFIG_NODE, &debug_pim_zebra_cmd); install_element(CONFIG_NODE, &no_debug_pim_zebra_cmd); + install_element(CONFIG_NODE, &debug_pim_vxlan_cmd); + install_element(CONFIG_NODE, &no_debug_pim_vxlan_cmd); install_element(CONFIG_NODE, &debug_msdp_cmd); install_element(CONFIG_NODE, &no_debug_msdp_cmd); install_element(CONFIG_NODE, &debug_msdp_events_cmd); @@ -8948,6 +9474,8 @@ void pim_cmd_init(void) install_element(VIEW_NODE, &show_ip_msdp_mesh_group_vrf_all_cmd); install_element(VIEW_NODE, &show_ip_pim_ssm_range_cmd); install_element(VIEW_NODE, &show_ip_pim_group_type_cmd); + install_element(VIEW_NODE, &show_ip_pim_vxlan_sg_cmd); + install_element(VIEW_NODE, &show_ip_pim_vxlan_sg_work_cmd); install_element(INTERFACE_NODE, &interface_pim_use_source_cmd); install_element(INTERFACE_NODE, &interface_no_pim_use_source_cmd); /* Install BFD command */ diff --git a/pimd/pim_cmd.h b/pimd/pim_cmd.h index b58da30bd..67d6e43c3 100644 --- a/pimd/pim_cmd.h +++ b/pimd/pim_cmd.h @@ -52,6 +52,7 @@ #define DEBUG_PIM_PACKETDUMP_RECV_STR "Dump received packets\n" #define DEBUG_PIM_TRACE_STR "PIM internal daemon activity\n" #define DEBUG_PIM_ZEBRA_STR "ZEBRA protocol activity\n" +#define DEBUG_PIM_VXLAN_STR "PIM VxLAN events\n" #define DEBUG_SSMPINGD_STR "ssmpingd activity\n" #define CLEAR_IP_IGMP_STR "IGMP clear commands\n" #define CLEAR_IP_PIM_STR "PIM clear commands\n" diff --git a/pimd/pim_iface.c b/pimd/pim_iface.c index 92d21cf42..0fb7f176c 100644 --- a/pimd/pim_iface.c +++ b/pimd/pim_iface.c @@ -47,6 +47,7 @@ #include "pim_nht.h" #include "pim_jp_agg.h" #include "pim_igmp_join.h" +#include "pim_vxlan.h" static void pim_if_igmp_join_del_all(struct interface *ifp); static int igmp_join_sock(const char *ifname, ifindex_t ifindex, @@ -109,7 +110,7 @@ static int pim_sec_addr_comp(const void *p1, const void *p2) } struct pim_interface *pim_if_new(struct interface *ifp, bool igmp, bool pim, - bool ispimreg) + bool ispimreg, bool is_vxlan_term) { struct pim_interface *pim_ifp; @@ -178,7 +179,7 @@ struct pim_interface *pim_if_new(struct interface *ifp, bool igmp, bool pim, pim_sock_reset(ifp); - pim_if_add_vif(ifp, ispimreg); + pim_if_add_vif(ifp, ispimreg, is_vxlan_term); return pim_ifp; } @@ -628,7 +629,7 @@ void pim_if_addr_add(struct connected *ifc) address assigned, then try to create a vif_index. */ if (pim_ifp->mroute_vif_index < 0) { - pim_if_add_vif(ifp, false); + pim_if_add_vif(ifp, false, false /*vxlan_term*/); } pim_ifchannel_scan_forward_start(ifp); } @@ -761,7 +762,7 @@ void pim_if_addr_add_all(struct interface *ifp) * address assigned, then try to create a vif_index. */ if (pim_ifp->mroute_vif_index < 0) { - pim_if_add_vif(ifp, false); + pim_if_add_vif(ifp, false, false /*vxlan_term*/); } pim_ifchannel_scan_forward_start(ifp); @@ -926,7 +927,7 @@ static int pim_iface_next_vif_index(struct interface *ifp) see also pim_if_find_vifindex_by_ifindex() */ -int pim_if_add_vif(struct interface *ifp, bool ispimreg) +int pim_if_add_vif(struct interface *ifp, bool ispimreg, bool is_vxlan_term) { struct pim_interface *pim_ifp = ifp->info; struct in_addr ifaddr; @@ -948,7 +949,7 @@ int pim_if_add_vif(struct interface *ifp, bool ispimreg) } ifaddr = pim_ifp->primary_address; - if (!ispimreg && PIM_INADDR_IS_ANY(ifaddr)) { + if (!ispimreg && !is_vxlan_term && PIM_INADDR_IS_ANY(ifaddr)) { zlog_warn( "%s: could not get address for interface %s ifindex=%d", __PRETTY_FUNCTION__, ifp->name, ifp->ifindex); @@ -977,6 +978,10 @@ int pim_if_add_vif(struct interface *ifp, bool ispimreg) } pim_ifp->pim->iface_vif_index[pim_ifp->mroute_vif_index] = 1; + + /* if the device qualifies as pim_vxlan iif/oif update vxlan entries */ + pim_vxlan_add_vif(ifp); + return 0; } @@ -991,6 +996,9 @@ int pim_if_del_vif(struct interface *ifp) return -1; } + /* if the device was a pim_vxlan iif/oif update vxlan mroute entries */ + pim_vxlan_del_vif(ifp); + pim_mroute_del_vif(ifp); /* @@ -1469,7 +1477,8 @@ void pim_if_create_pimreg(struct pim_instance *pim) pim->regiface = if_create(pimreg_name, pim->vrf_id); pim->regiface->ifindex = PIM_OIF_PIM_REGISTER_VIF; - pim_if_new(pim->regiface, false, false, true); + pim_if_new(pim->regiface, false, false, true, + false /*vxlan_term*/); } } diff --git a/pimd/pim_iface.h b/pimd/pim_iface.h index 5066998cb..fe96c0775 100644 --- a/pimd/pim_iface.h +++ b/pimd/pim_iface.h @@ -158,7 +158,7 @@ void pim_if_init(struct pim_instance *pim); void pim_if_terminate(struct pim_instance *pim); struct pim_interface *pim_if_new(struct interface *ifp, bool igmp, bool pim, - bool ispimreg); + bool ispimreg, bool is_vxlan_term); void pim_if_delete(struct interface *ifp); void pim_if_addr_add(struct connected *ifc); void pim_if_addr_del(struct connected *ifc, int force_prim_as_any); @@ -167,7 +167,7 @@ void pim_if_addr_del_all(struct interface *ifp); void pim_if_addr_del_all_igmp(struct interface *ifp); void pim_if_addr_del_all_pim(struct interface *ifp); -int pim_if_add_vif(struct interface *ifp, bool ispimreg); +int pim_if_add_vif(struct interface *ifp, bool ispimreg, bool is_vxlan_term); int pim_if_del_vif(struct interface *ifp); void pim_if_add_vif_all(struct pim_instance *pim); void pim_if_del_vif_all(struct pim_instance *pim); diff --git a/pimd/pim_instance.c b/pimd/pim_instance.c index 092a2d76f..a2bf3d278 100644 --- a/pimd/pim_instance.c +++ b/pimd/pim_instance.c @@ -36,6 +36,8 @@ static void pim_instance_terminate(struct pim_instance *pim) { + pim_vxlan_exit(pim); + if (pim->ssm_info) { pim_ssm_terminate(pim->ssm_info); pim->ssm_info = NULL; @@ -86,6 +88,7 @@ static struct pim_instance *pim_instance_init(struct vrf *vrf) pim->spt.plist = NULL; pim_msdp_init(pim, router->master); + pim_vxlan_init(pim); snprintf(hash_name, 64, "PIM %s RPF Hash", vrf->name); pim->rpf_hash = hash_create_size(256, pim_rpf_hash_key, pim_rpf_equal, diff --git a/pimd/pim_instance.h b/pimd/pim_instance.h index e651356bf..1740bcc79 100644 --- a/pimd/pim_instance.h +++ b/pimd/pim_instance.h @@ -26,6 +26,7 @@ #include "pim_str.h" #include "pim_msdp.h" #include "pim_assert.h" +#include "pim_vxlan_instance.h" #if defined(HAVE_LINUX_MROUTE_H) #include <linux/mroute.h> @@ -110,6 +111,7 @@ struct pim_instance { struct hash *channel_oil_hash; struct pim_msdp msdp; + struct pim_vxlan_instance vxlan; struct list *ssmpingd_list; struct in_addr ssmpingd_group_addr; diff --git a/pimd/pim_memory.c b/pimd/pim_memory.c index dff16c416..2bbab67e4 100644 --- a/pimd/pim_memory.c +++ b/pimd/pim_memory.c @@ -52,3 +52,4 @@ DEFINE_MTYPE(PIMD, PIM_PIM_INSTANCE, "PIM global state") DEFINE_MTYPE(PIMD, PIM_NEXTHOP_CACHE, "PIM nexthop cache state") DEFINE_MTYPE(PIMD, PIM_SSM_INFO, "PIM SSM configuration") DEFINE_MTYPE(PIMD, PIM_SPT_PLIST_NAME, "PIM SPT Prefix List Name") +DEFINE_MTYPE(PIMD, PIM_VXLAN_SG, "PIM VxLAN mroute cache") diff --git a/pimd/pim_memory.h b/pimd/pim_memory.h index 01189aca7..e5ca57a15 100644 --- a/pimd/pim_memory.h +++ b/pimd/pim_memory.h @@ -51,5 +51,6 @@ DECLARE_MTYPE(PIM_PIM_INSTANCE) DECLARE_MTYPE(PIM_NEXTHOP_CACHE) DECLARE_MTYPE(PIM_SSM_INFO) DECLARE_MTYPE(PIM_SPT_PLIST_NAME); +DECLARE_MTYPE(PIM_VXLAN_SG) #endif /* _QUAGGA_PIM_MEMORY_H */ diff --git a/pimd/pim_mroute.c b/pimd/pim_mroute.c index a2d50aba5..866a19fc9 100644 --- a/pimd/pim_mroute.c +++ b/pimd/pim_mroute.c @@ -42,6 +42,7 @@ #include "pim_zlookup.h" #include "pim_ssm.h" #include "pim_sock.h" +#include "pim_vxlan.h" static void mroute_read_on(struct pim_instance *pim); @@ -896,6 +897,12 @@ int pim_mroute_add(struct channel_oil *c_oil, const char *name) int err; int orig = 0; int orig_iif_vif = 0; + struct pim_interface *pim_reg_ifp; + int orig_pimreg_ttl; + bool pimreg_ttl_reset = false; + struct pim_interface *vxlan_ifp; + int orig_term_ttl; + bool orig_term_ttl_reset = false; pim->mroute_add_last = pim_time_monotonic_sec(); ++pim->mroute_add_events; @@ -921,6 +928,37 @@ int pim_mroute_add(struct channel_oil *c_oil, const char *name) c_oil->oil.mfcc_ttls[c_oil->oil.mfcc_parent] = 1; } + if (c_oil->up) { + /* suppress pimreg in the OIL if the mroute is not supposed to + * trigger register encapsulated data + */ + if (PIM_UPSTREAM_FLAG_TEST_NO_PIMREG_DATA(c_oil->up->flags)) { + pim_reg_ifp = pim->regiface->info; + orig_pimreg_ttl = + c_oil->oil.mfcc_ttls[pim_reg_ifp->mroute_vif_index]; + c_oil->oil.mfcc_ttls[pim_reg_ifp->mroute_vif_index] = 0; + /* remember to flip it back after MFC programming */ + pimreg_ttl_reset = true; + } + + vxlan_ifp = pim_vxlan_get_term_ifp(pim); + /* 1. vxlan termination device must never be added to the + * origination mroute (and that can actually happen because + * of XG inheritance from the termination mroute) otherwise + * traffic will end up looping. + * 2. vxlan termination device should be removed from the non-DF + * to prevent duplicates to the overlay rxer + */ + if (vxlan_ifp && + (PIM_UPSTREAM_FLAG_TEST_SRC_VXLAN_ORIG(c_oil->up->flags) || + PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(c_oil->up->flags))) { + orig_term_ttl_reset = true; + orig_term_ttl = + c_oil->oil.mfcc_ttls[vxlan_ifp->mroute_vif_index]; + c_oil->oil.mfcc_ttls[vxlan_ifp->mroute_vif_index] = 0; + } + } + /* * If we have an unresolved cache entry for the S,G * it is owned by the pimreg for the incoming IIF @@ -947,6 +985,14 @@ int pim_mroute_add(struct channel_oil *c_oil, const char *name) if (c_oil->oil.mfcc_origin.s_addr == INADDR_ANY) c_oil->oil.mfcc_ttls[c_oil->oil.mfcc_parent] = orig; + if (pimreg_ttl_reset) + c_oil->oil.mfcc_ttls[pim_reg_ifp->mroute_vif_index] = + orig_pimreg_ttl; + + if (orig_term_ttl_reset) + c_oil->oil.mfcc_ttls[vxlan_ifp->mroute_vif_index] = + orig_term_ttl; + if (err) { zlog_warn( "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_MFC): errno=%d: %s", diff --git a/pimd/pim_oil.c b/pimd/pim_oil.c index 55d26113f..5945bc55f 100644 --- a/pimd/pim_oil.c +++ b/pimd/pim_oil.c @@ -320,6 +320,7 @@ int pim_channel_add_oif(struct channel_oil *channel_oil, struct interface *oif, { struct pim_interface *pim_ifp; int old_ttl; + bool allow_iif_in_oil = false; /* * If we've gotten here we've gone bad, but let's @@ -344,7 +345,14 @@ int pim_channel_add_oif(struct channel_oil *channel_oil, struct interface *oif, by both source and receiver attached to the same interface. See TODO T22. */ - if (pim_ifp->mroute_vif_index == channel_oil->oil.mfcc_parent) { + if (channel_oil->up && + PIM_UPSTREAM_FLAG_TEST_ALLOW_IIF_IN_OIL( + channel_oil->up->flags)) { + allow_iif_in_oil = true; + } + + if (!allow_iif_in_oil && + pim_ifp->mroute_vif_index == channel_oil->oil.mfcc_parent) { channel_oil->oil_inherited_rescan = 1; if (PIM_DEBUG_MROUTE) { char group_str[INET_ADDRSTRLEN]; diff --git a/pimd/pim_oil.h b/pimd/pim_oil.h index 8b9532414..c5106d01c 100644 --- a/pimd/pim_oil.h +++ b/pimd/pim_oil.h @@ -34,9 +34,11 @@ #define PIM_OIF_FLAG_PROTO_PIM (1 << 1) #define PIM_OIF_FLAG_PROTO_SOURCE (1 << 2) #define PIM_OIF_FLAG_PROTO_STAR (1 << 3) -#define PIM_OIF_FLAG_PROTO_ANY \ - (PIM_OIF_FLAG_PROTO_IGMP | PIM_OIF_FLAG_PROTO_PIM \ - | PIM_OIF_FLAG_PROTO_SOURCE | PIM_OIF_FLAG_PROTO_STAR) +#define PIM_OIF_FLAG_PROTO_VXLAN (1 << 4) +#define PIM_OIF_FLAG_PROTO_ANY \ + (PIM_OIF_FLAG_PROTO_IGMP | PIM_OIF_FLAG_PROTO_PIM \ + | PIM_OIF_FLAG_PROTO_SOURCE | PIM_OIF_FLAG_PROTO_STAR \ + | PIM_OIF_FLAG_PROTO_VXLAN) /* * We need a pimreg vif id from the kernel. diff --git a/pimd/pim_register.c b/pimd/pim_register.c index 386ed1d42..431236eeb 100644 --- a/pimd/pim_register.c +++ b/pimd/pim_register.c @@ -43,6 +43,7 @@ #include "pim_join.h" #include "pim_util.h" #include "pim_ssm.h" +#include "pim_vxlan.h" struct thread *send_test_packet_timer = NULL; @@ -60,6 +61,7 @@ void pim_register_join(struct pim_upstream *up) pim_channel_add_oif(up->channel_oil, pim->regiface, PIM_OIF_FLAG_PROTO_PIM); up->reg_state = PIM_REG_JOIN; + pim_vxlan_update_sg_reg_state(pim, up, TRUE /*reg_join*/); } void pim_register_stop_send(struct interface *ifp, struct prefix_sg *sg, @@ -145,6 +147,8 @@ int pim_register_stop_recv(struct interface *ifp, uint8_t *buf, int buf_size) pim_channel_del_oif(upstream->channel_oil, pim->regiface, PIM_OIF_FLAG_PROTO_PIM); pim_upstream_start_register_stop_timer(upstream, 0); + pim_vxlan_update_sg_reg_state(pim, upstream, + FALSE /*reg_join*/); break; case PIM_REG_JOIN_PENDING: upstream->reg_state = PIM_REG_PRUNE; @@ -219,6 +223,54 @@ void pim_register_send(const uint8_t *buf, int buf_size, struct in_addr src, } } +void pim_null_register_send(struct pim_upstream *up) +{ + struct ip ip_hdr; + struct pim_interface *pim_ifp; + struct pim_rpf *rpg; + struct in_addr src; + + pim_ifp = up->rpf.source_nexthop.interface->info; + if (!pim_ifp) { + if (PIM_DEBUG_TRACE) + zlog_debug( + "%s: Cannot send null-register for %s no valid iif", + __PRETTY_FUNCTION__, up->sg_str); + return; + } + + rpg = RP(pim_ifp->pim, up->sg.grp); + if (!rpg) { + if (PIM_DEBUG_TRACE) + zlog_debug( + "%s: Cannot send null-register for %s no RPF to the RP", + __PRETTY_FUNCTION__, up->sg_str); + return; + } + + memset(&ip_hdr, 0, sizeof(struct ip)); + ip_hdr.ip_p = PIM_IP_PROTO_PIM; + ip_hdr.ip_hl = 5; + ip_hdr.ip_v = 4; + ip_hdr.ip_src = up->sg.src; + ip_hdr.ip_dst = up->sg.grp; + ip_hdr.ip_len = htons(20); + + /* checksum is broken */ + src = pim_ifp->primary_address; + if (PIM_UPSTREAM_FLAG_TEST_SRC_VXLAN_ORIG(up->flags)) { + if (!pim_vxlan_get_register_src(pim_ifp->pim, up, &src)) { + if (PIM_DEBUG_TRACE) + zlog_debug( + "%s: Cannot send null-register for %s vxlan-aa PIP unavailable", + __PRETTY_FUNCTION__, up->sg_str); + return; + } + } + pim_register_send((uint8_t *)&ip_hdr, sizeof(struct ip), + src, rpg, 1, up); +} + /* * 4.4.2 Receiving Register Messages at the RP * diff --git a/pimd/pim_register.h b/pimd/pim_register.h index 906d093bb..c5a28fee4 100644 --- a/pimd/pim_register.h +++ b/pimd/pim_register.h @@ -42,5 +42,6 @@ void pim_register_send(const uint8_t *buf, int buf_size, struct in_addr src, void pim_register_stop_send(struct interface *ifp, struct prefix_sg *sg, struct in_addr src, struct in_addr originator); void pim_register_join(struct pim_upstream *up); +void pim_null_register_send(struct pim_upstream *up); #endif diff --git a/pimd/pim_rpf.c b/pimd/pim_rpf.c index 8cc8b7481..afe3886aa 100644 --- a/pimd/pim_rpf.c +++ b/pimd/pim_rpf.c @@ -204,6 +204,9 @@ enum pim_rpf_result pim_rpf_update(struct pim_instance *pim, struct prefix src, grp; bool neigh_needed = true; + if (PIM_UPSTREAM_FLAG_TEST_STATIC_IIF(up->flags)) + return PIM_RPF_OK; + if (up->upstream_addr.s_addr == INADDR_ANY) { zlog_debug("%s: RP is not configured yet for %s", __PRETTY_FUNCTION__, up->sg_str); diff --git a/pimd/pim_str.c b/pimd/pim_str.c index fa1a6e624..f6acd0873 100644 --- a/pimd/pim_str.c +++ b/pimd/pim_str.c @@ -42,47 +42,12 @@ void pim_addr_dump(const char *onfail, struct prefix *p, char *buf, errno = save_errno; } -void pim_inet4_dump(const char *onfail, struct in_addr addr, char *buf, - int buf_size) -{ - int save_errno = errno; - - if (addr.s_addr == INADDR_ANY) - strcpy(buf, "*"); - else { - if (!inet_ntop(AF_INET, &addr, buf, buf_size)) { - zlog_warn( - "pim_inet4_dump: inet_ntop(AF_INET,buf_size=%d): errno=%d: %s", - buf_size, errno, safe_strerror(errno)); - if (onfail) - snprintf(buf, buf_size, "%s", onfail); - } - } - - errno = save_errno; -} - char *pim_str_sg_dump(const struct prefix_sg *sg) { - char src_str[INET_ADDRSTRLEN]; - char grp_str[INET_ADDRSTRLEN]; static char sg_str[PIM_SG_LEN]; - pim_inet4_dump("<src?>", sg->src, src_str, sizeof(src_str)); - pim_inet4_dump("<grp?>", sg->grp, grp_str, sizeof(grp_str)); - snprintf(sg_str, PIM_SG_LEN, "(%s,%s)", src_str, grp_str); + pim_str_sg_set(sg, sg_str); return sg_str; } -char *pim_str_sg_set(const struct prefix_sg *sg, char *sg_str) -{ - char src_str[INET_ADDRSTRLEN]; - char grp_str[INET_ADDRSTRLEN]; - - pim_inet4_dump("<src?>", sg->src, src_str, sizeof(src_str)); - pim_inet4_dump("<grp?>", sg->grp, grp_str, sizeof(grp_str)); - snprintf(sg_str, PIM_SG_LEN, "(%s,%s)", src_str, grp_str); - - return sg_str; -} diff --git a/pimd/pim_str.h b/pimd/pim_str.h index 12a33a810..94ba32415 100644 --- a/pimd/pim_str.h +++ b/pimd/pim_str.h @@ -33,13 +33,14 @@ * NULL Character at end = 1 * (123.123.123.123,123,123,123,123) */ -#define PIM_SG_LEN 36 +#define PIM_SG_LEN PREFIX_SG_STR_LEN +#define pim_inet4_dump prefix_mcast_inet4_dump +#define pim_str_sg_set prefix_sg2str void pim_addr_dump(const char *onfail, struct prefix *p, char *buf, int buf_size); void pim_inet4_dump(const char *onfail, struct in_addr addr, char *buf, int buf_size); char *pim_str_sg_dump(const struct prefix_sg *sg); -char *pim_str_sg_set(const struct prefix_sg *sg, char *sg_str); #endif diff --git a/pimd/pim_upstream.c b/pimd/pim_upstream.c index 04137b0b7..b708e86a2 100644 --- a/pimd/pim_upstream.c +++ b/pimd/pim_upstream.c @@ -51,6 +51,7 @@ #include "pim_jp_agg.h" #include "pim_nht.h" #include "pim_ssm.h" +#include "pim_vxlan.h" static void join_timer_stop(struct pim_upstream *up); static void @@ -483,6 +484,13 @@ static int pim_upstream_could_register(struct pim_upstream *up) { struct pim_interface *pim_ifp = NULL; + /* FORCE_PIMREG is a generic flag to let an app like VxLAN-AA register + * a source on an upstream entry even if the source is not directly + * connected on the IIF. + */ + if (PIM_UPSTREAM_FLAG_TEST_FORCE_PIMREG(up->flags)) + return 1; + if (up->rpf.source_nexthop.interface) pim_ifp = up->rpf.source_nexthop.interface->info; else { @@ -576,9 +584,10 @@ void pim_upstream_switch(struct pim_instance *pim, struct pim_upstream *up, pim_upstream_update_assert_tracking_desired(up); if (new_state == PIM_UPSTREAM_JOINED) { + pim_upstream_inherited_olist_decide(pim, up); if (old_state != PIM_UPSTREAM_JOINED) { int old_fhr = PIM_UPSTREAM_FLAG_TEST_FHR(up->flags); - forward_on(up); + pim_msdp_up_join_state_changed(pim, up); if (pim_upstream_could_register(up)) { PIM_UPSTREAM_FLAG_SET_FHR(up->flags); @@ -593,8 +602,6 @@ void pim_upstream_switch(struct pim_instance *pim, struct pim_upstream *up, pim_upstream_send_join(up); join_timer_start(up); } - } else { - forward_on(up); } } else { @@ -647,6 +654,23 @@ int pim_upstream_compare(void *arg1, void *arg2) return 0; } +void pim_upstream_fill_static_iif(struct pim_upstream *up, + struct interface *incoming) +{ + up->rpf.source_nexthop.interface = incoming; + + /* reset other parameters to matched a connected incoming interface */ + up->rpf.source_nexthop.mrib_nexthop_addr.family = AF_INET; + up->rpf.source_nexthop.mrib_nexthop_addr.u.prefix4.s_addr = + PIM_NET_INADDR_ANY; + up->rpf.source_nexthop.mrib_metric_preference = + ZEBRA_CONNECT_DISTANCE_DEFAULT; + up->rpf.source_nexthop.mrib_route_metric = 0; + up->rpf.rpf_addr.family = AF_INET; + up->rpf.rpf_addr.u.prefix4.s_addr = PIM_NET_INADDR_ANY; + +} + static struct pim_upstream *pim_upstream_new(struct pim_instance *pim, struct prefix_sg *sg, struct interface *incoming, @@ -712,13 +736,19 @@ static struct pim_upstream *pim_upstream_new(struct pim_instance *pim, if (up->sg.src.s_addr != INADDR_ANY) wheel_add_item(pim->upstream_sg_wheel, up); - if (up->upstream_addr.s_addr == INADDR_ANY) + if (PIM_UPSTREAM_FLAG_TEST_STATIC_IIF(up->flags)) { + pim_upstream_fill_static_iif(up, incoming); + pim_ifp = up->rpf.source_nexthop.interface->info; + assert(pim_ifp); + up->channel_oil = pim_channel_oil_add(pim, + &up->sg, pim_ifp->mroute_vif_index); + } else if (up->upstream_addr.s_addr == INADDR_ANY) { /* Create a dummmy channel oil with incoming ineterface MAXVIFS, * since RP is not configured */ up->channel_oil = pim_channel_oil_add(pim, &up->sg, MAXVIFS); - else { + } else { rpf_result = pim_rpf_update(pim, up, NULL, 1); if (rpf_result == PIM_RPF_FAILURE) { if (PIM_DEBUG_TRACE) @@ -1117,14 +1147,21 @@ static void pim_upstream_fhr_kat_start(struct pim_upstream *up) * KAT expiry indicates that flow is inactive. If the flow was created or * maintained by activity now is the time to deref it. */ -static int pim_upstream_keep_alive_timer(struct thread *t) +struct pim_upstream *pim_upstream_keep_alive_timer_proc( + struct pim_upstream *up) { - struct pim_upstream *up; struct pim_instance *pim; - up = THREAD_ARG(t); pim = up->channel_oil->pim; + if (PIM_UPSTREAM_FLAG_TEST_DISABLE_KAT_EXPIRY(up->flags)) { + /* if the router is a PIM vxlan encapsulator we prevent expiry + * of KAT as the mroute is pre-setup without any traffic + */ + pim_upstream_keep_alive_timer_start(up, pim->keep_alive_time); + return up; + } + if (I_am_RP(pim, up->sg.grp)) { pim_br_clear_pmbr(&up->sg); /* @@ -1144,12 +1181,12 @@ static int pim_upstream_keep_alive_timer(struct thread *t) "kat expired on %s[%s]; remove stream reference", up->sg_str, pim->vrf->name); PIM_UPSTREAM_FLAG_UNSET_SRC_STREAM(up->flags); - pim_upstream_del(pim, up, __PRETTY_FUNCTION__); + up = pim_upstream_del(pim, up, __PRETTY_FUNCTION__); } else if (PIM_UPSTREAM_FLAG_TEST_SRC_LHR(up->flags)) { struct pim_upstream *parent = up->parent; PIM_UPSTREAM_FLAG_UNSET_SRC_LHR(up->flags); - pim_upstream_del(pim, up, __PRETTY_FUNCTION__); + up = pim_upstream_del(pim, up, __PRETTY_FUNCTION__); if (parent) { pim_jp_agg_single_upstream_send(&parent->rpf, parent, @@ -1157,6 +1194,15 @@ static int pim_upstream_keep_alive_timer(struct thread *t) } } + return up; +} +static int pim_upstream_keep_alive_timer(struct thread *t) +{ + struct pim_upstream *up; + + up = THREAD_ARG(t); + + pim_upstream_keep_alive_timer_proc(up); return 0; } @@ -1371,8 +1417,6 @@ static int pim_upstream_register_stop_timer(struct thread *t) struct pim_interface *pim_ifp; struct pim_instance *pim; struct pim_upstream *up; - struct pim_rpf *rpg; - struct ip ip_hdr; up = THREAD_ARG(t); pim = up->channel_oil->pim; @@ -1388,6 +1432,7 @@ static int pim_upstream_register_stop_timer(struct thread *t) up->reg_state = PIM_REG_JOIN; pim_channel_add_oif(up->channel_oil, pim->regiface, PIM_OIF_FLAG_PROTO_PIM); + pim_vxlan_update_sg_reg_state(pim, up, TRUE /*reg_join*/); break; case PIM_REG_JOIN: break; @@ -1420,24 +1465,7 @@ static int pim_upstream_register_stop_timer(struct thread *t) __PRETTY_FUNCTION__); return 0; } - rpg = RP(pim_ifp->pim, up->sg.grp); - if (!rpg) { - if (PIM_DEBUG_TRACE) - zlog_debug( - "%s: Cannot send register for %s no RPF to the RP", - __PRETTY_FUNCTION__, up->sg_str); - return 0; - } - memset(&ip_hdr, 0, sizeof(struct ip)); - ip_hdr.ip_p = PIM_IP_PROTO_PIM; - ip_hdr.ip_hl = 5; - ip_hdr.ip_v = 4; - ip_hdr.ip_src = up->sg.src; - ip_hdr.ip_dst = up->sg.grp; - ip_hdr.ip_len = htons(20); - // checksum is broken - pim_register_send((uint8_t *)&ip_hdr, sizeof(struct ip), - pim_ifp->primary_address, rpg, 1, up); + pim_null_register_send(up); break; default: break; diff --git a/pimd/pim_upstream.h b/pimd/pim_upstream.h index 70e70140d..13a3dcdf8 100644 --- a/pimd/pim_upstream.h +++ b/pimd/pim_upstream.h @@ -37,6 +37,43 @@ #define PIM_UPSTREAM_FLAG_MASK_SRC_MSDP (1 << 6) #define PIM_UPSTREAM_FLAG_MASK_SEND_SG_RPT_PRUNE (1 << 7) #define PIM_UPSTREAM_FLAG_MASK_SRC_LHR (1 << 8) +/* In the case of pim vxlan we prime the pump by registering the + * vxlan source and keeping the SPT (FHR-RP) alive by sending periodic + * NULL registers. So we need to prevent KAT expiry because of the + * lack of BUM traffic. + */ +#define PIM_UPSTREAM_FLAG_MASK_DISABLE_KAT_EXPIRY (1 << 9) +/* for pim vxlan we need to pin the IIF to lo or MLAG-ISL on the + * originating VTEP. This flag allows that by setting IIF to the + * value specified and preventing next-hop-tracking on the entry + */ +#define PIM_UPSTREAM_FLAG_MASK_STATIC_IIF (1 << 10) +#define PIM_UPSTREAM_FLAG_MASK_ALLOW_IIF_IN_OIL (1 << 11) +/* Disable pimreg encasulation for a flow */ +#define PIM_UPSTREAM_FLAG_MASK_NO_PIMREG_DATA (1 << 12) +/* For some MDTs we need to register the router as a source even + * if the not DR or directly connected on the IIF. This is typically + * needed on a VxLAN-AA (MLAG) setup. + */ +#define PIM_UPSTREAM_FLAG_MASK_FORCE_PIMREG (1 << 13) +/* VxLAN origination mroute - SG was registered by EVPN where S is the + * local VTEP IP and G is the BUM multicast group address + */ +#define PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_ORIG (1 << 14) +/* VxLAN termination mroute - *G entry where G is the BUM multicast group + * address + */ +#define PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_TERM (1 << 15) +/* MLAG mroute - synced to the MLAG peer and subject to DF (designated + * forwarder) election + */ +#define PIM_UPSTREAM_FLAG_MASK_MLAG_VXLAN (1 << 16) +/* MLAG mroute that lost the DF election with peer and is installed in + * a dormant state i.e. MLAG OIFs are removed from the MFC. + * In most cases the OIL is empty (but not not always) simply + * blackholing the traffic pulled down to the LHR. + */ +#define PIM_UPSTREAM_FLAG_MASK_MLAG_NON_DF (1 << 17) #define PIM_UPSTREAM_FLAG_ALL 0xFFFFFFFF #define PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED(flags) ((flags) & PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED) @@ -48,6 +85,16 @@ #define PIM_UPSTREAM_FLAG_TEST_SRC_MSDP(flags) ((flags) & PIM_UPSTREAM_FLAG_MASK_SRC_MSDP) #define PIM_UPSTREAM_FLAG_TEST_SEND_SG_RPT_PRUNE(flags) ((flags) & PIM_UPSTREAM_FLAG_MASK_SEND_SG_RPT_PRUNE) #define PIM_UPSTREAM_FLAG_TEST_SRC_LHR(flags) ((flags) & PIM_UPSTREAM_FLAG_MASK_SRC_LHR) +#define PIM_UPSTREAM_FLAG_TEST_DISABLE_KAT_EXPIRY(flags) ((flags) & PIM_UPSTREAM_FLAG_MASK_DISABLE_KAT_EXPIRY) +#define PIM_UPSTREAM_FLAG_TEST_STATIC_IIF(flags) ((flags) & PIM_UPSTREAM_FLAG_MASK_STATIC_IIF) +#define PIM_UPSTREAM_FLAG_TEST_ALLOW_IIF_IN_OIL(flags) ((flags) & PIM_UPSTREAM_FLAG_MASK_ALLOW_IIF_IN_OIL) +#define PIM_UPSTREAM_FLAG_TEST_NO_PIMREG_DATA(flags) ((flags) & PIM_UPSTREAM_FLAG_MASK_NO_PIMREG_DATA) +#define PIM_UPSTREAM_FLAG_TEST_FORCE_PIMREG(flags) ((flags) & PIM_UPSTREAM_FLAG_MASK_FORCE_PIMREG) +#define PIM_UPSTREAM_FLAG_TEST_SRC_VXLAN_ORIG(flags) ((flags) & PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_ORIG) +#define PIM_UPSTREAM_FLAG_TEST_SRC_VXLAN_TERM(flags) ((flags) & PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_TERM) +#define PIM_UPSTREAM_FLAG_TEST_SRC_VXLAN(flags) ((flags) & (PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_ORIG | PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_TERM)) +#define PIM_UPSTREAM_FLAG_TEST_MLAG_VXLAN(flags) ((flags) & PIM_UPSTREAM_FLAG_MASK_MLAG_VXLAN) +#define PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(flags) ((flags) & PIM_UPSTREAM_FLAG_MASK_MLAG_NON_DF) #define PIM_UPSTREAM_FLAG_SET_DR_JOIN_DESIRED(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED) #define PIM_UPSTREAM_FLAG_SET_DR_JOIN_DESIRED_UPDATED(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED_UPDATED) @@ -58,6 +105,15 @@ #define PIM_UPSTREAM_FLAG_SET_SRC_MSDP(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_SRC_MSDP) #define PIM_UPSTREAM_FLAG_SET_SEND_SG_RPT_PRUNE(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_SEND_SG_RPT_PRUNE) #define PIM_UPSTREAM_FLAG_SET_SRC_LHR(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_SRC_LHR) +#define PIM_UPSTREAM_FLAG_SET_DISABLE_KAT_EXPIRY(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_DISABLE_KAT_EXPIRY) +#define PIM_UPSTREAM_FLAG_SET_STATIC_IIF(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_STATIC_IIF) +#define PIM_UPSTREAM_FLAG_SET_ALLOW_IIF_IN_OIL(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_ALLOW_IIF_IN_OIL) +#define PIM_UPSTREAM_FLAG_SET_NO_PIMREG_DATA(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_NO_PIMREG_DATA) +#define PIM_UPSTREAM_FLAG_SET_FORCE_PIMREG(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_FORCE_PIMREG) +#define PIM_UPSTREAM_FLAG_SET_SRC_VXLAN_ORIG(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_ORIG) +#define PIM_UPSTREAM_FLAG_SET_SRC_VXLAN_TERM(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_TERM) +#define PIM_UPSTREAM_FLAG_SET_MLAG_VXLAN(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_MLAG_VXLAN) +#define PIM_UPSTREAM_FLAG_SET_MLAG_NON_DF(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_MLAG_NON_DF) #define PIM_UPSTREAM_FLAG_UNSET_DR_JOIN_DESIRED(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED) #define PIM_UPSTREAM_FLAG_UNSET_DR_JOIN_DESIRED_UPDATED(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED_UPDATED) @@ -68,6 +124,15 @@ #define PIM_UPSTREAM_FLAG_UNSET_SRC_MSDP(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_SRC_MSDP) #define PIM_UPSTREAM_FLAG_UNSET_SEND_SG_RPT_PRUNE(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_SEND_SG_RPT_PRUNE) #define PIM_UPSTREAM_FLAG_UNSET_SRC_LHR(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_SRC_LHR) +#define PIM_UPSTREAM_FLAG_UNSET_DISABLE_KAT_EXPIRY(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_DISABLE_KAT_EXPIRY) +#define PIM_UPSTREAM_FLAG_UNSET_STATIC_IIF(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_STATIC_IIF) +#define PIM_UPSTREAM_FLAG_UNSET_ALLOW_IIF_IN_OIL(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_ALLOW_IIF_IN_OIL) +#define PIM_UPSTREAM_FLAG_UNSET_NO_PIMREG_DATA(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_NO_PIMREG_DATA) +#define PIM_UPSTREAM_FLAG_UNSET_FORCE_PIMREG(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_FORCE_PIMREG) +#define PIM_UPSTREAM_FLAG_UNSET_SRC_VXLAN_ORIG(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_ORIG) +#define PIM_UPSTREAM_FLAG_UNSET_SRC_VXLAN_TERM(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_TERM) +#define PIM_UPSTREAM_FLAG_UNSET_MLAG_VXLAN(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_MLAG_VXLAN) +#define PIM_UPSTREAM_FLAG_UNSET_MLAG_NON_DF(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_MLAG_NON_DF) enum pim_upstream_state { PIM_UPSTREAM_NOTJOINED, @@ -245,4 +310,8 @@ void pim_upstream_spt_prefix_list_update(struct pim_instance *pim, unsigned int pim_upstream_hash_key(void *arg); bool pim_upstream_equal(const void *arg1, const void *arg2); +struct pim_upstream *pim_upstream_keep_alive_timer_proc( + struct pim_upstream *up); +void pim_upstream_fill_static_iif(struct pim_upstream *up, + struct interface *incoming); #endif /* PIM_UPSTREAM_H */ diff --git a/pimd/pim_vty.c b/pimd/pim_vty.c index 649578874..2654ebc58 100644 --- a/pimd/pim_vty.c +++ b/pimd/pim_vty.c @@ -39,6 +39,7 @@ #include "pim_msdp.h" #include "pim_ssm.h" #include "pim_bfd.h" +#include "pim_vxlan.h" int pim_debug_config_write(struct vty *vty) { @@ -119,6 +120,11 @@ int pim_debug_config_write(struct vty *vty) ++writes; } + if (PIM_DEBUG_VXLAN) { + vty_out(vty, "debug pim vxlan\n"); + ++writes; + } + if (PIM_DEBUG_SSMPINGD) { vty_out(vty, "debug ssmpingd\n"); ++writes; @@ -234,6 +240,8 @@ int pim_global_config_write_worker(struct pim_instance *pim, struct vty *vty) } } + pim_vxlan_config_write(vty, spaces, &writes); + return writes; } diff --git a/pimd/pim_vxlan.c b/pimd/pim_vxlan.c new file mode 100644 index 000000000..af76c6d73 --- /dev/null +++ b/pimd/pim_vxlan.c @@ -0,0 +1,1025 @@ +/* PIM support for VxLAN BUM flooding + * + * Copyright (C) 2019 Cumulus Networks, Inc. + * + * This file is part of FRR. + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <zebra.h> + +#include <hash.h> +#include <jhash.h> +#include <log.h> +#include <prefix.h> +#include <vrf.h> + +#include "pimd.h" +#include "pim_iface.h" +#include "pim_memory.h" +#include "pim_oil.h" +#include "pim_register.h" +#include "pim_str.h" +#include "pim_upstream.h" +#include "pim_ifchannel.h" +#include "pim_nht.h" +#include "pim_zebra.h" +#include "pim_vxlan.h" + +/* pim-vxlan global info */ +struct pim_vxlan vxlan_info, *pim_vxlan_p = &vxlan_info; + +static void pim_vxlan_work_timer_setup(bool start); +static void pim_vxlan_set_peerlink_rif(struct pim_instance *pim, + struct interface *ifp); + +/*************************** vxlan work list ********************************** + * A work list is maintained for staggered generation of pim null register + * messages for vxlan SG entries that are in a reg_join state. + * + * A max of 500 NULL registers are generated at one shot. If paused reg + * generation continues on the next second and so on till all register + * messages have been sent out. And the process is restarted every 60s. + * + * purpose of this null register generation is to setup the SPT and maintain + * independent of the presence of overlay BUM traffic. + ****************************************************************************/ +static void pim_vxlan_do_reg_work(void) +{ + struct listnode *listnode; + int work_cnt = 0; + struct pim_vxlan_sg *vxlan_sg; + static int sec_count; + + ++sec_count; + + if (sec_count > PIM_VXLAN_NULL_REG_INTERVAL) { + sec_count = 0; + listnode = vxlan_info.next_work ? + vxlan_info.next_work : + vxlan_info.work_list->head; + if (PIM_DEBUG_VXLAN && listnode) + zlog_debug("vxlan SG work %s", + vxlan_info.next_work ? "continues" : "starts"); + } else { + listnode = vxlan_info.next_work; + } + + for (; listnode; listnode = listnode->next) { + vxlan_sg = (struct pim_vxlan_sg *)listnode->data; + if (vxlan_sg->up && (vxlan_sg->up->reg_state == PIM_REG_JOIN)) { + if (PIM_DEBUG_VXLAN) + zlog_debug("vxlan SG %s periodic NULL register", + vxlan_sg->sg_str); + pim_null_register_send(vxlan_sg->up); + ++work_cnt; + } + + if (work_cnt > vxlan_info.max_work_cnt) { + vxlan_info.next_work = listnode->next; + if (PIM_DEBUG_VXLAN) + zlog_debug("vxlan SG %d work items proc and pause", + work_cnt); + return; + } + } + + if (work_cnt) { + if (PIM_DEBUG_VXLAN) + zlog_debug("vxlan SG %d work items proc", work_cnt); + } + vxlan_info.next_work = NULL; +} + +/* Staggered work related info is initialized when the first work comes + * along + */ +static void pim_vxlan_init_work(void) +{ + if (vxlan_info.flags & PIM_VXLANF_WORK_INITED) + return; + + vxlan_info.max_work_cnt = PIM_VXLAN_WORK_MAX; + vxlan_info.flags |= PIM_VXLANF_WORK_INITED; + vxlan_info.work_list = list_new(); + pim_vxlan_work_timer_setup(TRUE /* start */); +} + +static void pim_vxlan_add_work(struct pim_vxlan_sg *vxlan_sg) +{ + if (vxlan_sg->flags & PIM_VXLAN_SGF_DEL_IN_PROG) { + if (PIM_DEBUG_VXLAN) + zlog_debug("vxlan SG %s skip work list; del-in-prog", + vxlan_sg->sg_str); + return; + } + + pim_vxlan_init_work(); + + /* already a part of the work list */ + if (vxlan_sg->work_node) + return; + + if (PIM_DEBUG_VXLAN) + zlog_debug("vxlan SG %s work list add", + vxlan_sg->sg_str); + vxlan_sg->work_node = listnode_add(vxlan_info.work_list, vxlan_sg); + /* XXX: adjust max_work_cnt if needed */ +} + +static void pim_vxlan_del_work(struct pim_vxlan_sg *vxlan_sg) +{ + if (!vxlan_sg->work_node) + return; + + if (PIM_DEBUG_VXLAN) + zlog_debug("vxlan SG %s work list del", + vxlan_sg->sg_str); + + if (vxlan_sg->work_node == vxlan_info.next_work) + vxlan_info.next_work = vxlan_sg->work_node->next; + + list_delete_node(vxlan_info.work_list, vxlan_sg->work_node); + vxlan_sg->work_node = NULL; +} + +void pim_vxlan_update_sg_reg_state(struct pim_instance *pim, + struct pim_upstream *up, bool reg_join) +{ + struct pim_vxlan_sg *vxlan_sg; + + vxlan_sg = pim_vxlan_sg_find(pim, &up->sg); + if (!vxlan_sg) + return; + + /* add the vxlan sg entry to a work list for periodic reg joins. + * the entry will stay in the list as long as the register state is + * PIM_REG_JOIN + */ + if (reg_join) + pim_vxlan_add_work(vxlan_sg); + else + pim_vxlan_del_work(vxlan_sg); +} + +static int pim_vxlan_work_timer_cb(struct thread *t) +{ + pim_vxlan_do_reg_work(); + pim_vxlan_work_timer_setup(true /* start */); + return 0; +} + +/* global 1second timer used for periodic processing */ +static void pim_vxlan_work_timer_setup(bool start) +{ + THREAD_OFF(vxlan_info.work_timer); + if (start) + thread_add_timer(router->master, pim_vxlan_work_timer_cb, NULL, + PIM_VXLAN_WORK_TIME, &vxlan_info.work_timer); +} + +/**************************** vxlan origination mroutes *********************** + * For every (local-vtep-ip, bum-mcast-grp) registered by evpn an origination + * mroute is setup by pimd. The purpose of this mroute is to forward vxlan + * encapsulated BUM (broadcast, unknown-unicast and unknown-multicast packets + * over the underlay.) + * + * Sample mroute (single VTEP): + * (27.0.0.7, 239.1.1.100) Iif: lo Oifs: uplink-1 + * + * Sample mroute (anycast VTEP): + * (36.0.0.9, 239.1.1.100) Iif: peerlink-3.4094\ + * Oifs: peerlink-3.4094 uplink-1 + ***************************************************************************/ +static void pim_vxlan_orig_mr_up_del(struct pim_vxlan_sg *vxlan_sg) +{ + struct pim_upstream *up = vxlan_sg->up; + + if (!up) + return; + + if (PIM_DEBUG_VXLAN) + zlog_debug("vxlan SG %s orig mroute-up del", + vxlan_sg->sg_str); + + vxlan_sg->up = NULL; + if (up->flags & PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_ORIG) { + /* clear out all the vxlan properties */ + up->flags &= ~(PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_ORIG | + PIM_UPSTREAM_FLAG_MASK_STATIC_IIF | + PIM_UPSTREAM_FLAG_MASK_DISABLE_KAT_EXPIRY | + PIM_UPSTREAM_FLAG_MASK_FORCE_PIMREG | + PIM_UPSTREAM_FLAG_MASK_NO_PIMREG_DATA | + PIM_UPSTREAM_FLAG_MASK_ALLOW_IIF_IN_OIL); + + /* We bring things to a grinding halt by force expirying + * the kat. Doing this will also remove the reference we + * created as a "vxlan" source and delete the upstream entry + * if there are no other references. + */ + if (PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(up->flags)) { + THREAD_OFF(up->t_ka_timer); + up = pim_upstream_keep_alive_timer_proc(up); + } else { + /* this is really unexpected as we force vxlan + * origination mroutes active sources but just in + * case + */ + up = pim_upstream_del(vxlan_sg->pim, up, + __PRETTY_FUNCTION__); + } + /* if there are other references register the source + * for nht + */ + if (up) + pim_rpf_update(vxlan_sg->pim, up, NULL, 1 /* is_new */); + } +} + +static void pim_vxlan_orig_mr_up_iif_update(struct pim_vxlan_sg *vxlan_sg) +{ + int vif_index; + + /* update MFC with the new IIF */ + pim_upstream_fill_static_iif(vxlan_sg->up, vxlan_sg->iif); + vif_index = pim_if_find_vifindex_by_ifindex(vxlan_sg->pim, + vxlan_sg->iif->ifindex); + if (vif_index > 0) + pim_scan_individual_oil(vxlan_sg->up->channel_oil, + vif_index); + + if (PIM_DEBUG_VXLAN) + zlog_debug("vxlan SG %s orig mroute-up updated with iif %s vifi %d", + vxlan_sg->sg_str, + vxlan_sg->iif?vxlan_sg->iif->name:"-", vif_index); + +} + +/* For every VxLAN BUM multicast group we setup a SG-up that has the following + * "forced properties" - + * 1. Directly connected on a DR interface i.e. we must act as an FHR + * 2. We prime the pump i.e. no multicast data is needed to register this + * source with the FHR. To do that we send periodic null registers if + * the SG entry is in a register-join state. We also prevent expiry of + * KAT. + * 3. As this SG is setup without data there is no need to register encapsulate + * data traffic. This encapsulation is explicitly skipped for the following + * reasons - + * a) Many levels of encapsulation are needed creating MTU disc challenges. + * Overlay BUM is encapsulated in a vxlan/UDP/IP header and then + * encapsulated again in a pim-register header. + * b) On a vxlan-aa setup both switches rx a copy of each BUM packet. if + * they both reg encapsulated traffic the RP will accept the duplicates + * as there are no RPF checks for this encapsulated data. + * a), b) can be workarounded if needed, but there is really no need because + * of (2) i.e. the pump is primed without data. + */ +static void pim_vxlan_orig_mr_up_add(struct pim_vxlan_sg *vxlan_sg) +{ + struct pim_upstream *up; + int flags = 0; + struct prefix nht_p; + + if (vxlan_sg->up) { + /* nothing to do */ + return; + } + + if (PIM_DEBUG_VXLAN) + zlog_debug("vxlan SG %s orig mroute-up add with iif %s", + vxlan_sg->sg_str, + vxlan_sg->iif?vxlan_sg->iif->name:"-"); + + PIM_UPSTREAM_FLAG_SET_SRC_VXLAN_ORIG(flags); + /* pin the IIF to lo or peerlink-subinterface and disable NHT */ + PIM_UPSTREAM_FLAG_SET_STATIC_IIF(flags); + /* Fake traffic by setting SRC_STREAM and starting KAT */ + /* We intentionally skip updating ref count for SRC_STREAM/FHR. + * Setting SRC_VXLAN should have already created a reference + * preventing the entry from being deleted + */ + PIM_UPSTREAM_FLAG_SET_FHR(flags); + PIM_UPSTREAM_FLAG_SET_SRC_STREAM(flags); + /* Force pimreg even if non-DR. This is needed on a MLAG setup for + * VxLAN AA + */ + PIM_UPSTREAM_FLAG_SET_FORCE_PIMREG(flags); + /* prevent KAT expiry. we want the MDT setup even if there is no BUM + * traffic + */ + PIM_UPSTREAM_FLAG_SET_DISABLE_KAT_EXPIRY(flags); + /* SPT for vxlan BUM groups is primed and maintained via NULL + * registers so there is no need to reg-encapsulate + * vxlan-encapsulated overlay data traffic + */ + PIM_UPSTREAM_FLAG_SET_NO_PIMREG_DATA(flags); + /* On a MLAG setup we force a copy to the MLAG peer while also + * accepting traffic from the peer. To do this we set peerlink-rif as + * the IIF and also add it to the OIL + */ + PIM_UPSTREAM_FLAG_SET_ALLOW_IIF_IN_OIL(flags); + + /* XXX: todo: defer pim_upstream add if pim is not enabled on the iif */ + up = pim_upstream_find(vxlan_sg->pim, &vxlan_sg->sg); + if (up) { + /* if the iif is set to something other than the vxlan_sg->iif + * we must dereg the old nexthop and force to new "static" + * iif + */ + if (!PIM_UPSTREAM_FLAG_TEST_STATIC_IIF(up->flags)) { + nht_p.family = AF_INET; + nht_p.prefixlen = IPV4_MAX_BITLEN; + nht_p.u.prefix4 = up->upstream_addr; + pim_delete_tracked_nexthop(vxlan_sg->pim, + &nht_p, up, NULL); + } + pim_upstream_ref(up, flags, __PRETTY_FUNCTION__); + vxlan_sg->up = up; + pim_vxlan_orig_mr_up_iif_update(vxlan_sg); + } else { + up = pim_upstream_add(vxlan_sg->pim, &vxlan_sg->sg, + vxlan_sg->iif, flags, + __PRETTY_FUNCTION__, NULL); + vxlan_sg->up = up; + } + + if (!up) { + if (PIM_DEBUG_VXLAN) + zlog_debug("vxlan SG %s orig mroute-up add failed", + vxlan_sg->sg_str); + return; + } + + pim_upstream_keep_alive_timer_start(up, vxlan_sg->pim->keep_alive_time); + + /* register the source with the RP */ + if (up->reg_state == PIM_REG_NOINFO) { + pim_register_join(up); + pim_null_register_send(up); + } + + /* update the inherited OIL */ + pim_upstream_inherited_olist(vxlan_sg->pim, up); +} + +static void pim_vxlan_orig_mr_oif_add(struct pim_vxlan_sg *vxlan_sg) +{ + if (!vxlan_sg->up || !vxlan_sg->orig_oif) + return; + + if (PIM_DEBUG_VXLAN) + zlog_debug("vxlan SG %s oif %s add", + vxlan_sg->sg_str, vxlan_sg->orig_oif->name); + + vxlan_sg->flags |= PIM_VXLAN_SGF_OIF_INSTALLED; + pim_channel_add_oif(vxlan_sg->up->channel_oil, + vxlan_sg->orig_oif, PIM_OIF_FLAG_PROTO_VXLAN); +} + +static void pim_vxlan_orig_mr_oif_del(struct pim_vxlan_sg *vxlan_sg) +{ + struct interface *orig_oif; + + orig_oif = vxlan_sg->orig_oif; + vxlan_sg->orig_oif = NULL; + + if (!(vxlan_sg->flags & PIM_VXLAN_SGF_OIF_INSTALLED)) + return; + + if (PIM_DEBUG_VXLAN) + zlog_debug("vxlan SG %s oif %s del", + vxlan_sg->sg_str, orig_oif->name); + + vxlan_sg->flags &= ~PIM_VXLAN_SGF_OIF_INSTALLED; + pim_channel_del_oif(vxlan_sg->up->channel_oil, + orig_oif, PIM_OIF_FLAG_PROTO_VXLAN); +} + +static inline struct interface *pim_vxlan_orig_mr_oif_get( + struct pim_instance *pim) +{ + return (vxlan_mlag.flags & PIM_VXLAN_MLAGF_ENABLED) ? + pim->vxlan.peerlink_rif : NULL; +} + +/* Single VTEPs: IIF for the vxlan-origination-mroutes is lo or vrf-dev (if + * the mroute is in a non-default vrf). + * Anycast VTEPs: IIF is the MLAG ISL/peerlink. + */ +static inline struct interface *pim_vxlan_orig_mr_iif_get( + struct pim_instance *pim) +{ + return ((vxlan_mlag.flags & PIM_VXLAN_MLAGF_ENABLED) && + pim->vxlan.peerlink_rif) ? + pim->vxlan.peerlink_rif : pim->vxlan.default_iif; +} + +static bool pim_vxlan_orig_mr_add_is_ok(struct pim_vxlan_sg *vxlan_sg) +{ + struct pim_interface *pim_ifp; + + vxlan_sg->iif = pim_vxlan_orig_mr_iif_get(vxlan_sg->pim); + if (!vxlan_sg->iif) + return false; + + pim_ifp = (struct pim_interface *)vxlan_sg->iif->info; + if (!pim_ifp || (pim_ifp->mroute_vif_index < 0)) + return false; + + return true; +} + +static void pim_vxlan_orig_mr_install(struct pim_vxlan_sg *vxlan_sg) +{ + pim_vxlan_orig_mr_up_add(vxlan_sg); + + vxlan_sg->orig_oif = pim_vxlan_orig_mr_oif_get(vxlan_sg->pim); + pim_vxlan_orig_mr_oif_add(vxlan_sg); +} + +static void pim_vxlan_orig_mr_add(struct pim_vxlan_sg *vxlan_sg) +{ + if (!pim_vxlan_orig_mr_add_is_ok(vxlan_sg)) + return; + + if (PIM_DEBUG_VXLAN) + zlog_debug("vxlan SG %s orig-mr add", vxlan_sg->sg_str); + + pim_vxlan_orig_mr_install(vxlan_sg); +} + +static void pim_vxlan_orig_mr_del(struct pim_vxlan_sg *vxlan_sg) +{ + if (PIM_DEBUG_VXLAN) + zlog_debug("vxlan SG %s orig-mr del", vxlan_sg->sg_str); + + pim_vxlan_orig_mr_oif_del(vxlan_sg); + pim_vxlan_orig_mr_up_del(vxlan_sg); +} + +static void pim_vxlan_orig_mr_iif_update(struct hash_backet *backet, void *arg) +{ + struct interface *ifp = (struct interface *)arg; + struct pim_vxlan_sg *vxlan_sg = (struct pim_vxlan_sg *)backet->data; + struct interface *old_iif = vxlan_sg->iif; + + if (!pim_vxlan_is_orig_mroute(vxlan_sg)) + return; + + if (PIM_DEBUG_VXLAN) + zlog_debug("vxlan SG %s iif changed from %s to %s", + vxlan_sg->sg_str, + old_iif ? old_iif->name : "-", + ifp ? ifp->name : "-"); + + if (pim_vxlan_orig_mr_add_is_ok(vxlan_sg)) { + if (vxlan_sg->up) { + /* upstream exists but iif changed */ + pim_vxlan_orig_mr_up_iif_update(vxlan_sg); + } else { + /* install mroute */ + pim_vxlan_orig_mr_install(vxlan_sg); + } + } else { + pim_vxlan_orig_mr_del(vxlan_sg); + } +} + +/**************************** vxlan termination mroutes *********************** + * For every bum-mcast-grp registered by evpn a *G termination + * mroute is setup by pimd. The purpose of this mroute is to pull down vxlan + * packets with the bum-mcast-grp dip from the underlay and terminate the + * tunnel. This is done by including the vxlan termination device (ipmr-lo) in + * its OIL. The vxlan de-capsulated packets are subject to subsequent overlay + * bridging. + * + * Sample mroute: + * (0.0.0.0, 239.1.1.100) Iif: uplink-1 Oifs: ipmr-lo, uplink-1 + *****************************************************************************/ +struct pim_interface *pim_vxlan_get_term_ifp(struct pim_instance *pim) +{ + return pim->vxlan.term_if ? + (struct pim_interface *)pim->vxlan.term_if->info : NULL; +} + +static void pim_vxlan_term_mr_oif_add(struct pim_vxlan_sg *vxlan_sg) +{ + if (vxlan_sg->flags & PIM_VXLAN_SGF_OIF_INSTALLED) + return; + + if (PIM_DEBUG_VXLAN) + zlog_debug("vxlan SG %s term-oif %s add", + vxlan_sg->sg_str, vxlan_sg->term_oif->name); + + if (pim_ifchannel_local_membership_add(vxlan_sg->term_oif, + &vxlan_sg->sg)) { + vxlan_sg->flags |= PIM_VXLAN_SGF_OIF_INSTALLED; + } else { + zlog_warn("vxlan SG %s term-oif %s add failed", + vxlan_sg->sg_str, vxlan_sg->term_oif->name); + } +} + +static void pim_vxlan_term_mr_oif_del(struct pim_vxlan_sg *vxlan_sg) +{ + if (!(vxlan_sg->flags & PIM_VXLAN_SGF_OIF_INSTALLED)) + return; + + if (PIM_DEBUG_VXLAN) + zlog_debug("vxlan SG %s oif %s del", + vxlan_sg->sg_str, vxlan_sg->term_oif->name); + + vxlan_sg->flags &= ~PIM_VXLAN_SGF_OIF_INSTALLED; + pim_ifchannel_local_membership_del(vxlan_sg->term_oif, &vxlan_sg->sg); +} + +static void pim_vxlan_term_mr_up_add(struct pim_vxlan_sg *vxlan_sg) +{ + struct pim_upstream *up; + int flags = 0; + + if (vxlan_sg->up) { + /* nothing to do */ + return; + } + + if (PIM_DEBUG_VXLAN) + zlog_debug("vxlan SG %s term mroute-up add", + vxlan_sg->sg_str); + + PIM_UPSTREAM_FLAG_SET_SRC_VXLAN_TERM(flags); + /* enable MLAG designated-forwarder election on termination mroutes */ + PIM_UPSTREAM_FLAG_SET_MLAG_VXLAN(flags); + + up = pim_upstream_add(vxlan_sg->pim, &vxlan_sg->sg, + NULL /* iif */, flags, + __PRETTY_FUNCTION__, NULL); + vxlan_sg->up = up; + + if (!up) { + zlog_warn("vxlan SG %s term mroute-up add failed", + vxlan_sg->sg_str); + } +} + +static void pim_vxlan_term_mr_up_del(struct pim_vxlan_sg *vxlan_sg) +{ + struct pim_upstream *up = vxlan_sg->up; + + if (!up) + return; + + if (PIM_DEBUG_VXLAN) + zlog_debug("vxlan SG %s term mroute-up del", + vxlan_sg->sg_str); + vxlan_sg->up = NULL; + if (up->flags & PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_TERM) { + /* clear out all the vxlan related flags */ + up->flags &= ~(PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_TERM | + PIM_UPSTREAM_FLAG_MASK_MLAG_VXLAN); + + pim_upstream_del(vxlan_sg->pim, up, + __PRETTY_FUNCTION__); + } +} + +static void pim_vxlan_term_mr_add(struct pim_vxlan_sg *vxlan_sg) +{ + if (PIM_DEBUG_VXLAN) + zlog_debug("vxlan SG %s term mroute add", vxlan_sg->sg_str); + + vxlan_sg->term_oif = vxlan_sg->pim->vxlan.term_if; + if (!vxlan_sg->term_oif) + /* defer termination mroute till we have a termination device */ + return; + + pim_vxlan_term_mr_up_add(vxlan_sg); + /* set up local membership for the term-oif */ + pim_vxlan_term_mr_oif_add(vxlan_sg); +} + +static void pim_vxlan_term_mr_del(struct pim_vxlan_sg *vxlan_sg) +{ + if (PIM_DEBUG_VXLAN) + zlog_debug("vxlan SG %s term mroute del", vxlan_sg->sg_str); + + /* remove local membership associated with the term oif */ + pim_vxlan_term_mr_oif_del(vxlan_sg); + /* remove references to the upstream entry */ + pim_vxlan_term_mr_up_del(vxlan_sg); +} + +/************************** vxlan SG cache management ************************/ +static unsigned int pim_vxlan_sg_hash_key_make(void *p) +{ + struct pim_vxlan_sg *vxlan_sg = p; + + return (jhash_2words(vxlan_sg->sg.src.s_addr, + vxlan_sg->sg.grp.s_addr, 0)); +} + +static bool pim_vxlan_sg_hash_eq(const void *p1, const void *p2) +{ + const struct pim_vxlan_sg *sg1 = p1; + const struct pim_vxlan_sg *sg2 = p2; + + return ((sg1->sg.src.s_addr == sg2->sg.src.s_addr) + && (sg1->sg.grp.s_addr == sg2->sg.grp.s_addr)); +} + +static struct pim_vxlan_sg *pim_vxlan_sg_new(struct pim_instance *pim, + struct prefix_sg *sg) +{ + struct pim_vxlan_sg *vxlan_sg; + + vxlan_sg = XCALLOC(MTYPE_PIM_VXLAN_SG, sizeof(*vxlan_sg)); + + vxlan_sg->pim = pim; + vxlan_sg->sg = *sg; + pim_str_sg_set(sg, vxlan_sg->sg_str); + + if (PIM_DEBUG_VXLAN) + zlog_debug("vxlan SG %s alloc", vxlan_sg->sg_str); + + vxlan_sg = hash_get(pim->vxlan.sg_hash, vxlan_sg, hash_alloc_intern); + + return vxlan_sg; +} + +struct pim_vxlan_sg *pim_vxlan_sg_find(struct pim_instance *pim, + struct prefix_sg *sg) +{ + struct pim_vxlan_sg lookup; + + lookup.sg = *sg; + return hash_lookup(pim->vxlan.sg_hash, &lookup); +} + +struct pim_vxlan_sg *pim_vxlan_sg_add(struct pim_instance *pim, + struct prefix_sg *sg) +{ + struct pim_vxlan_sg *vxlan_sg; + + vxlan_sg = pim_vxlan_sg_find(pim, sg); + if (vxlan_sg) + return vxlan_sg; + + vxlan_sg = pim_vxlan_sg_new(pim, sg); + + if (pim_vxlan_is_orig_mroute(vxlan_sg)) + pim_vxlan_orig_mr_add(vxlan_sg); + else + pim_vxlan_term_mr_add(vxlan_sg); + + return vxlan_sg; +} + +void pim_vxlan_sg_del(struct pim_instance *pim, struct prefix_sg *sg) +{ + struct pim_vxlan_sg *vxlan_sg; + + vxlan_sg = pim_vxlan_sg_find(pim, sg); + if (!vxlan_sg) + return; + + vxlan_sg->flags |= PIM_VXLAN_SGF_DEL_IN_PROG; + + pim_vxlan_del_work(vxlan_sg); + + if (pim_vxlan_is_orig_mroute(vxlan_sg)) + pim_vxlan_orig_mr_del(vxlan_sg); + else + pim_vxlan_term_mr_del(vxlan_sg); + + hash_release(vxlan_sg->pim->vxlan.sg_hash, vxlan_sg); + + if (PIM_DEBUG_VXLAN) + zlog_debug("vxlan SG %s free", vxlan_sg->sg_str); + + XFREE(MTYPE_PIM_VXLAN_SG, vxlan_sg); +} + +/******************************* MLAG handling *******************************/ +/* The peerlink sub-interface is added as an OIF to the origination-mroute. + * This is done to send a copy of the multicast-vxlan encapsulated traffic + * to the MLAG peer which may mroute it over the underlay if there are any + * interested receivers. + */ +static void pim_vxlan_sg_peerlink_update(struct hash_backet *backet, void *arg) +{ + struct interface *new_oif = (struct interface *)arg; + struct pim_vxlan_sg *vxlan_sg = (struct pim_vxlan_sg *)backet->data; + + if (!pim_vxlan_is_orig_mroute(vxlan_sg)) + return; + + if (vxlan_sg->orig_oif == new_oif) + return; + + pim_vxlan_orig_mr_oif_del(vxlan_sg); + + vxlan_sg->orig_oif = new_oif; + pim_vxlan_orig_mr_oif_add(vxlan_sg); +} + +/* In the case of anycast VTEPs the VTEP-PIP must be used as the + * register source. + */ +bool pim_vxlan_get_register_src(struct pim_instance *pim, + struct pim_upstream *up, struct in_addr *src_p) +{ + if (!(vxlan_mlag.flags & PIM_VXLAN_MLAGF_ENABLED)) + return true; + + /* if address is not available suppress the pim-register */ + if (vxlan_mlag.reg_addr.s_addr == INADDR_ANY) + return false; + + *src_p = vxlan_mlag.reg_addr; + return true; +} + +void pim_vxlan_mlag_update(bool enable, bool peer_state, uint32_t role, + struct interface *peerlink_rif, + struct in_addr *reg_addr) +{ + struct pim_instance *pim; + struct interface *old_oif; + struct interface *new_oif; + char addr_buf[INET_ADDRSTRLEN]; + struct pim_interface *pim_ifp = NULL; + + if (PIM_DEBUG_VXLAN) { + inet_ntop(AF_INET, reg_addr, + addr_buf, INET_ADDRSTRLEN); + zlog_debug("vxlan MLAG update %s state %s role %d rif %s addr %s", + enable ? "enable" : "disable", + peer_state ? "up" : "down", + role, + peerlink_rif ? peerlink_rif->name : "-", + addr_buf); + } + + /* XXX: for now vxlan termination is only possible in the default VRF + * when that changes this will need to change to iterate all VRFs + */ + pim = pim_get_pim_instance(VRF_DEFAULT); + + old_oif = pim_vxlan_orig_mr_oif_get(pim); + + if (enable) + vxlan_mlag.flags |= PIM_VXLAN_MLAGF_ENABLED; + else + vxlan_mlag.flags &= ~PIM_VXLAN_MLAGF_ENABLED; + + if (vxlan_mlag.peerlink_rif != peerlink_rif) + vxlan_mlag.peerlink_rif = peerlink_rif; + + vxlan_mlag.reg_addr = *reg_addr; + vxlan_mlag.peer_state = peer_state; + vxlan_mlag.role = role; + + /* process changes */ + if (vxlan_mlag.peerlink_rif) + pim_ifp = (struct pim_interface *)vxlan_mlag.peerlink_rif->info; + if ((vxlan_mlag.flags & PIM_VXLAN_MLAGF_ENABLED) && + pim_ifp && (pim_ifp->mroute_vif_index > 0)) + pim_vxlan_set_peerlink_rif(pim, peerlink_rif); + else + pim_vxlan_set_peerlink_rif(pim, NULL); + + new_oif = pim_vxlan_orig_mr_oif_get(pim); + if (old_oif != new_oif) + hash_iterate(pim->vxlan.sg_hash, pim_vxlan_sg_peerlink_update, + new_oif); +} + +/****************************** misc callbacks *******************************/ +void pim_vxlan_config_write(struct vty *vty, char *spaces, int *writes) +{ + char addr_buf[INET_ADDRSTRLEN]; + + if ((vxlan_mlag.flags & PIM_VXLAN_MLAGF_ENABLED) && + vxlan_mlag.peerlink_rif) { + + inet_ntop(AF_INET, &vxlan_mlag.reg_addr, + addr_buf, sizeof(addr_buf)); + vty_out(vty, + "%sip pim mlag %s role %s state %s addr %s\n", + spaces, + vxlan_mlag.peerlink_rif->name, + (vxlan_mlag.role == PIM_VXLAN_MLAG_ROLE_PRIMARY) ? + "primary":"secondary", + vxlan_mlag.peer_state ? "up" : "down", + addr_buf); + *writes += 1; + } +} + +static void pim_vxlan_set_default_iif(struct pim_instance *pim, + struct interface *ifp) +{ + struct interface *old_iif; + + if (pim->vxlan.default_iif == ifp) + return; + + old_iif = pim->vxlan.default_iif; + if (PIM_DEBUG_VXLAN) + zlog_debug("%s: vxlan default iif changed from %s to %s", + __PRETTY_FUNCTION__, + old_iif ? old_iif->name : "-", + ifp ? ifp->name : "-"); + + old_iif = pim_vxlan_orig_mr_iif_get(pim); + pim->vxlan.default_iif = ifp; + ifp = pim_vxlan_orig_mr_iif_get(pim); + if (old_iif == ifp) + return; + + if (PIM_DEBUG_VXLAN) + zlog_debug("%s: vxlan orig iif changed from %s to %s", + __PRETTY_FUNCTION__, old_iif ? old_iif->name : "-", + ifp ? ifp->name : "-"); + + /* add/del upstream entries for the existing vxlan SG when the + * interface becomes available + */ + if (pim->vxlan.sg_hash) + hash_iterate(pim->vxlan.sg_hash, + pim_vxlan_orig_mr_iif_update, ifp); +} + +static void pim_vxlan_set_peerlink_rif(struct pim_instance *pim, + struct interface *ifp) +{ + struct interface *old_iif; + + if (pim->vxlan.peerlink_rif == ifp) + return; + + old_iif = pim->vxlan.peerlink_rif; + if (PIM_DEBUG_VXLAN) + zlog_debug("%s: vxlan peerlink_rif changed from %s to %s", + __PRETTY_FUNCTION__, old_iif ? old_iif->name : "-", + ifp ? ifp->name : "-"); + + old_iif = pim_vxlan_orig_mr_iif_get(pim); + pim->vxlan.peerlink_rif = ifp; + ifp = pim_vxlan_orig_mr_iif_get(pim); + if (old_iif == ifp) + return; + + if (PIM_DEBUG_VXLAN) + zlog_debug("%s: vxlan orig iif changed from %s to %s", + __PRETTY_FUNCTION__, old_iif ? old_iif->name : "-", + ifp ? ifp->name : "-"); + + /* add/del upstream entries for the existing vxlan SG when the + * interface becomes available + */ + if (pim->vxlan.sg_hash) + hash_iterate(pim->vxlan.sg_hash, + pim_vxlan_orig_mr_iif_update, ifp); +} + +void pim_vxlan_add_vif(struct interface *ifp) +{ + struct pim_interface *pim_ifp = ifp->info; + struct pim_instance *pim = pim_ifp->pim; + + if (pim->vrf_id != VRF_DEFAULT) + return; + + if (if_is_loopback_or_vrf(ifp)) + pim_vxlan_set_default_iif(pim, ifp); + + if (vxlan_mlag.flags & PIM_VXLAN_MLAGF_ENABLED && + (ifp == vxlan_mlag.peerlink_rif)) + pim_vxlan_set_peerlink_rif(pim, ifp); +} + +void pim_vxlan_del_vif(struct interface *ifp) +{ + struct pim_interface *pim_ifp = ifp->info; + struct pim_instance *pim = pim_ifp->pim; + + if (pim->vrf_id != VRF_DEFAULT) + return; + + if (pim->vxlan.default_iif == ifp) + pim_vxlan_set_default_iif(pim, NULL); + + if (pim->vxlan.peerlink_rif == ifp) + pim_vxlan_set_peerlink_rif(pim, NULL); +} + +static void pim_vxlan_term_mr_oif_update(struct hash_backet *backet, void *arg) +{ + struct interface *ifp = (struct interface *)arg; + struct pim_vxlan_sg *vxlan_sg = (struct pim_vxlan_sg *)backet->data; + + if (pim_vxlan_is_orig_mroute(vxlan_sg)) + return; + + if (vxlan_sg->term_oif == ifp) + return; + + if (PIM_DEBUG_VXLAN) + zlog_debug("vxlan SG %s term oif changed from %s to %s", + vxlan_sg->sg_str, + vxlan_sg->term_oif ? vxlan_sg->term_oif->name : "-", + ifp ? ifp->name : "-"); + + pim_vxlan_term_mr_del(vxlan_sg); + vxlan_sg->term_oif = ifp; + pim_vxlan_term_mr_add(vxlan_sg); +} + +void pim_vxlan_add_term_dev(struct pim_instance *pim, + struct interface *ifp) +{ + struct pim_interface *pim_ifp; + + if (pim->vxlan.term_if == ifp) + return; + + if (PIM_DEBUG_VXLAN) + zlog_debug("vxlan term oif changed from %s to %s", + pim->vxlan.term_if ? pim->vxlan.term_if->name : "-", + ifp->name); + + /* enable pim on the term ifp */ + pim_ifp = (struct pim_interface *)ifp->info; + if (pim_ifp) { + PIM_IF_DO_PIM(pim_ifp->options); + } else { + pim_ifp = pim_if_new(ifp, false /*igmp*/, true /*pim*/, + false /*pimreg*/, true /*vxlan_term*/); + /* ensure that pimreg existss before using the newly created + * vxlan termination device + */ + pim_if_create_pimreg(pim); + } + + pim->vxlan.term_if = ifp; + + if (pim->vxlan.sg_hash) + hash_iterate(pim_ifp->pim->vxlan.sg_hash, + pim_vxlan_term_mr_oif_update, ifp); +} + +void pim_vxlan_del_term_dev(struct pim_instance *pim) +{ + struct interface *ifp = pim->vxlan.term_if; + struct pim_interface *pim_ifp; + + if (PIM_DEBUG_VXLAN) + zlog_debug("vxlan term oif changed from %s to -", ifp->name); + + pim->vxlan.term_if = NULL; + + if (pim->vxlan.sg_hash) + hash_iterate(pim->vxlan.sg_hash, + pim_vxlan_term_mr_oif_update, NULL); + + pim_ifp = (struct pim_interface *)ifp->info; + if (pim_ifp) { + PIM_IF_DONT_PIM(pim_ifp->options); + if (!PIM_IF_TEST_IGMP(pim_ifp->options)) + pim_if_delete(ifp); + } + +} + +void pim_vxlan_init(struct pim_instance *pim) +{ + char hash_name[64]; + + snprintf(hash_name, sizeof(hash_name), + "PIM %s vxlan SG hash", pim->vrf->name); + pim->vxlan.sg_hash = hash_create(pim_vxlan_sg_hash_key_make, + pim_vxlan_sg_hash_eq, hash_name); +} + +void pim_vxlan_exit(struct pim_instance *pim) +{ + if (pim->vxlan.sg_hash) { + hash_clean(pim->vxlan.sg_hash, NULL); + hash_free(pim->vxlan.sg_hash); + pim->vxlan.sg_hash = NULL; + } +} diff --git a/pimd/pim_vxlan.h b/pimd/pim_vxlan.h new file mode 100644 index 000000000..f0a66e6b7 --- /dev/null +++ b/pimd/pim_vxlan.h @@ -0,0 +1,139 @@ +/* PIM support for VxLAN BUM flooding + * + * Copyright (C) 2019 Cumulus Networks, Inc. + * + * This file is part of FRR. + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef PIM_VXLAN_H +#define PIM_VXLAN_H + +/* global timer used for miscellaneous staggered processing */ +#define PIM_VXLAN_WORK_TIME 1 +/* number of SG entries processed at one shot */ +#define PIM_VXLAN_WORK_MAX 500 +/* frequency of periodic NULL registers */ +#define PIM_VXLAN_NULL_REG_INTERVAL 60 /* seconds */ + +#define vxlan_mlag (vxlan_info.mlag) + +enum pim_vxlan_sg_flags { + PIM_VXLAN_SGF_NONE = 0, + PIM_VXLAN_SGF_DEL_IN_PROG = (1 << 0), + PIM_VXLAN_SGF_OIF_INSTALLED = (1 << 1) +}; + +struct pim_vxlan_sg { + struct pim_instance *pim; + + /* key */ + struct prefix_sg sg; + char sg_str[PIM_SG_LEN]; + + enum pim_vxlan_sg_flags flags; + struct pim_upstream *up; + struct listnode *work_node; /* to pim_vxlan.work_list */ + + /* termination info (only applicable to termination XG mroutes) + * term_if - termination device ipmr-lo is added to the OIL + * as local/IGMP membership to allow termination of vxlan traffic + */ + struct interface *term_oif; + + /* origination info + * iif - lo/vrf or peerlink (on MLAG setups) + * peerlink_oif - added to the OIL to send encapsulated BUM traffic to + * the MLAG peer switch + */ + struct interface *iif; + /* on a MLAG setup the peerlink is added as a static OIF */ + struct interface *orig_oif; +}; + +enum pim_vxlan_mlag_flags { + PIM_VXLAN_MLAGF_NONE = 0, + PIM_VXLAN_MLAGF_ENABLED = (1 << 0) +}; + +enum pim_vxlan_mlag_role { + PIM_VXLAN_MLAG_ROLE_SECONDARY = 0, + PIM_VXLAN_MLAG_ROLE_PRIMARY +}; + +struct pim_vxlan_mlag { + enum pim_vxlan_mlag_flags flags; + enum pim_vxlan_mlag_role role; + bool peer_state; + /* routed interface setup on top of MLAG peerlink */ + struct interface *peerlink_rif; + struct in_addr reg_addr; +}; + +enum pim_vxlan_flags { + PIM_VXLANF_NONE = 0, + PIM_VXLANF_WORK_INITED = (1 << 0) +}; + +struct pim_vxlan { + enum pim_vxlan_flags flags; + + struct thread *work_timer; + struct list *work_list; + struct listnode *next_work; + int max_work_cnt; + + struct pim_vxlan_mlag mlag; +}; + +/* zebra adds- + * 1. one (S, G) entry where S=local-VTEP-IP and G==BUM-mcast-grp for + * each BUM MDT. This is the origination entry. + * 2. and one (*, G) entry each MDT. This is the termination place holder. + * + * Note: This doesn't mean that only (*, G) mroutes are used for tunnel + * termination. (S, G) mroutes with ipmr-lo in the OIL can also be + * used for tunnel termiation if SPT switchover happens; however such + * SG entries are created by traffic and will NOT be a part of the vxlan SG + * database. + */ +static inline bool pim_vxlan_is_orig_mroute(struct pim_vxlan_sg *vxlan_sg) +{ + return (vxlan_sg->sg.src.s_addr != 0); +} + +extern struct pim_vxlan *pim_vxlan_p; +extern struct pim_vxlan_sg *pim_vxlan_sg_find(struct pim_instance *pim, + struct prefix_sg *sg); +extern struct pim_vxlan_sg *pim_vxlan_sg_add(struct pim_instance *pim, + struct prefix_sg *sg); +extern void pim_vxlan_sg_del(struct pim_instance *pim, struct prefix_sg *sg); +extern void pim_vxlan_update_sg_reg_state(struct pim_instance *pim, + struct pim_upstream *up, bool reg_join); +extern struct pim_interface *pim_vxlan_get_term_ifp(struct pim_instance *pim); +extern void pim_vxlan_add_vif(struct interface *ifp); +extern void pim_vxlan_del_vif(struct interface *ifp); +extern void pim_vxlan_add_term_dev(struct pim_instance *pim, + struct interface *ifp); +extern void pim_vxlan_del_term_dev(struct pim_instance *pim); +extern bool pim_vxlan_get_register_src(struct pim_instance *pim, + struct pim_upstream *up, struct in_addr *src_p); +extern void pim_vxlan_mlag_update(bool enable, bool peer_state, uint32_t role, + struct interface *peerlink_rif, + struct in_addr *reg_addr); +extern void pim_vxlan_config_write(struct vty *vty, char *spaces, int *writes); + +#endif /* PIM_VXLAN_H */ diff --git a/pimd/pim_vxlan_instance.h b/pimd/pim_vxlan_instance.h new file mode 100644 index 000000000..3f99483fb --- /dev/null +++ b/pimd/pim_vxlan_instance.h @@ -0,0 +1,45 @@ +/* PIM support for VxLAN BUM flooding + * + * Copyright (C) 2019 Cumulus Networks, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + */ + +#ifndef PIM_VXLAN_INSTANCE_H +#define PIM_VXLAN_INSTANCE_H + +/* pim termination device is expected to include the substring ipmr-lo */ +#define PIM_VXLAN_TERM_DEV_NAME "ipmr-lo" + +struct pim_vxlan_instance { + struct hash *sg_hash; + + /* this is lo for default instance and vrf-dev for non-default + * instances + */ + struct interface *default_iif; + + /* In a MLAG/VxLAN-AA setup the peerlink sub-interface (ISL-rif) is + * used as the IIF in + */ + struct interface *peerlink_rif; + + /* device used by the dataplane to terminate multicast encapsulated + * vxlan traffic + */ + struct interface *term_if; +}; + +extern void pim_vxlan_init(struct pim_instance *pim); +extern void pim_vxlan_exit(struct pim_instance *pim); + +#endif /* PIM_VXLAN_INSTANCE_H */ diff --git a/pimd/pim_zebra.c b/pimd/pim_zebra.c index b20f31082..aeaea7d69 100644 --- a/pimd/pim_zebra.c +++ b/pimd/pim_zebra.c @@ -45,6 +45,7 @@ #include "pim_jp_agg.h" #include "pim_nht.h" #include "pim_ssm.h" +#include "pim_vxlan.h" #undef PIM_DEBUG_IFADDR_DUMP #define PIM_DEBUG_IFADDR_DUMP @@ -110,13 +111,18 @@ static int pim_zebra_if_add(int command, struct zclient *zclient, struct pim_interface *pim_ifp; if (!ifp->info) { - pim_ifp = pim_if_new(ifp, false, false, false); + pim_ifp = pim_if_new(ifp, false, false, false, + false /*vxlan_term*/); ifp->info = pim_ifp; } pim_sock_add(ifp); } + if (!strncmp(ifp->name, PIM_VXLAN_TERM_DEV_NAME, + sizeof(PIM_VXLAN_TERM_DEV_NAME))) + pim_vxlan_add_term_dev(pim, ifp); + return 0; } @@ -124,6 +130,7 @@ static int pim_zebra_if_del(int command, struct zclient *zclient, zebra_size_t length, vrf_id_t vrf_id) { struct interface *ifp; + struct pim_instance *pim; /* zebra api adds/dels interfaces using the same call @@ -152,6 +159,10 @@ static int pim_zebra_if_del(int command, struct zclient *zclient, if_set_index(ifp, IFINDEX_INTERNAL); + pim = pim_get_pim_instance(vrf_id); + if (pim && pim->vxlan.term_if == ifp) + pim_vxlan_del_term_dev(pim); + return 0; } @@ -543,6 +554,41 @@ void pim_zebra_upstream_rpf_changed(struct pim_instance *pim, pim_upstream_update_join_desired(pim, up); } +static int pim_zebra_vxlan_sg_proc(int command, struct zclient *zclient, + zebra_size_t length, vrf_id_t vrf_id) +{ + struct stream *s; + struct pim_instance *pim; + struct prefix_sg sg; + + pim = pim_get_pim_instance(vrf_id); + if (!pim) + return 0; + + s = zclient->ibuf; + + sg.family = AF_INET; + sg.prefixlen = stream_getl(s); + stream_get(&sg.src.s_addr, s, sg.prefixlen); + stream_get(&sg.grp.s_addr, s, sg.prefixlen); + + if (PIM_DEBUG_ZEBRA) { + char sg_str[PIM_SG_LEN]; + + pim_str_sg_set(&sg, sg_str); + zlog_debug("%u:recv SG %s %s", vrf_id, + (command == ZEBRA_VXLAN_SG_ADD)?"add":"del", + sg_str); + } + + if (command == ZEBRA_VXLAN_SG_ADD) + pim_vxlan_sg_add(pim, &sg); + else + pim_vxlan_sg_del(pim, &sg); + + return 0; +} + void pim_scan_individual_oil(struct channel_oil *c_oil, int in_vif_index) { struct in_addr vif_source; @@ -769,6 +815,8 @@ void pim_zebra_init(void) zclient->interface_address_delete = pim_zebra_if_address_del; zclient->interface_vrf_update = pim_zebra_interface_vrf_update; zclient->nexthop_update = pim_parse_nexthop_update; + zclient->vxlan_sg_add = pim_zebra_vxlan_sg_proc; + zclient->vxlan_sg_del = pim_zebra_vxlan_sg_proc; zclient_init(zclient, ZEBRA_ROUTE_PIM, 0, &pimd_privs); if (PIM_DEBUG_PIM_TRACE) { diff --git a/pimd/pimd.h b/pimd/pimd.h index 50c19658d..2f2a87037 100644 --- a/pimd/pimd.h +++ b/pimd/pimd.h @@ -113,6 +113,7 @@ #define PIM_MASK_PIM_NHT_DETAIL (1 << 23) #define PIM_MASK_PIM_NHT_RP (1 << 24) #define PIM_MASK_MTRACE (1 << 25) +#define PIM_MASK_VXLAN (1 << 26) /* Remember 32 bits!!! */ /* PIM error codes */ @@ -180,6 +181,7 @@ extern uint8_t qpim_ecmp_rebalance_enable; #define PIM_DEBUG_PIM_NHT_DETAIL (router->debugs & PIM_MASK_PIM_NHT_DETAIL) #define PIM_DEBUG_PIM_NHT_RP (router->debugs & PIM_MASK_PIM_NHT_RP) #define PIM_DEBUG_MTRACE (router->debugs & PIM_MASK_MTRACE) +#define PIM_DEBUG_VXLAN (router->debugs & PIM_MASK_VXLAN) #define PIM_DEBUG_EVENTS \ (router->debugs \ @@ -220,6 +222,7 @@ extern uint8_t qpim_ecmp_rebalance_enable; #define PIM_DO_DEBUG_PIM_NHT (router->debugs |= PIM_MASK_PIM_NHT) #define PIM_DO_DEBUG_PIM_NHT_RP (router->debugs |= PIM_MASK_PIM_NHT_RP) #define PIM_DO_DEBUG_MTRACE (router->debugs |= PIM_MASK_MTRACE) +#define PIM_DO_DEBUG_VXLAN (router->debugs |= PIM_MASK_VXLAN) #define PIM_DONT_DEBUG_PIM_EVENTS (router->debugs &= ~PIM_MASK_PIM_EVENTS) #define PIM_DONT_DEBUG_PIM_PACKETS (router->debugs &= ~PIM_MASK_PIM_PACKETS) @@ -249,6 +252,7 @@ extern uint8_t qpim_ecmp_rebalance_enable; #define PIM_DONT_DEBUG_PIM_NHT (router->debugs &= ~PIM_MASK_PIM_NHT) #define PIM_DONT_DEBUG_PIM_NHT_RP (router->debugs &= ~PIM_MASK_PIM_NHT_RP) #define PIM_DONT_DEBUG_MTRACE (router->debugs &= ~PIM_MASK_MTRACE) +#define PIM_DONT_DEBUG_VXLAN (router->debugs &= ~PIM_MASK_VXLAN) void pim_router_init(void); void pim_router_terminate(void); diff --git a/pimd/subdir.am b/pimd/subdir.am index 7d8df7d10..7f4810722 100644 --- a/pimd/subdir.am +++ b/pimd/subdir.am @@ -60,6 +60,7 @@ pimd_libpim_a_SOURCES = \ pimd/pim_vty.c \ pimd/pim_zebra.c \ pimd/pim_zlookup.c \ + pimd/pim_vxlan.c \ pimd/pimd.c \ # end @@ -110,6 +111,8 @@ noinst_HEADERS += \ pimd/pim_vty.h \ pimd/pim_zebra.h \ pimd/pim_zlookup.h \ + pimd/pim_vxlan.h \ + pimd/pim_vxlan_instance.h \ pimd/pimd.h \ pimd/mtracebis_netlink.h \ pimd/mtracebis_routeget.h \ diff --git a/ports/Makefile b/ports/Makefile deleted file mode 100644 index 86f77bd00..000000000 --- a/ports/Makefile +++ /dev/null @@ -1,59 +0,0 @@ -# New ports collection makefile for: zebra -# Version required: 2.1.5 -# Date created: 28 Feb 1998 -# Whom: seirios@matrix.iri.co.jp -# - -#DISTNAME= zebra-980224 -DISTNAME= zebra-current -PKGNAME= zebra -CATEGORIES= net -MASTER_SITES= ftp://ftp.zebra.org/pub/zebra/ - -MAINTAINER= seirios@matrix.iri.co.jp - -WRKSRC= ${WRKDIR}/zebra-current - -#### Under constructing, We cannot support md5 -NO_CHECKSUM= yes - -do-build: - @(cd ${WRKSRC}; sh ./configure; make) - -post-install: - @if [ ! -f ${PREFIX}/etc/rc.d/zebra.sh ]; then \ - echo "Installing ${PREFIX}/etc/rc.d/zebra.sh startup file."; \ - echo "#!/bin/sh" > ${PREFIX}/etc/rc.d/zebra.sh; \ - echo "# zebra" >> ${PREFIX}/etc/rc.d/zebra.sh; \ - echo "if [ -x /usr/local/sbin/zebra -a ! -f /var/run/zebra.pid -a -f /usr/local/etc/zebra.conf ]; then" >> ${PREFIX}/etc/rc.d/zebra.sh; \ - echo " /usr/local/sbin/zebra -d -f /usr/local/etc/zebra.conf" >> ${PREFIX}/etc/rc.d/zebra.sh; \ - echo " echo -n ' zebra'" >> ${PREFIX}/etc/rc.d/zebra.sh; \ - echo "fi" >> ${PREFIX}/etc/rc.d/zebra.sh; \ - echo "# bgpd" >> ${PREFIX}/etc/rc.d/zebra.sh; \ - echo "if [ -x /usr/local/sbin/bgpd -a ! -f /var/run/bgpd.pid -a -f /usr/local/etc/bgpd.conf ]; then" >> ${PREFIX}/etc/rc.d/zebra.sh; \ - echo " /usr/local/sbin/bgpd -d -f /usr/local/etc/bgpd.conf" >> ${PREFIX}/etc/rc.d/zebra.sh; \ - echo " echo -n ' bgpd'" >> ${PREFIX}/etc/rc.d/zebra.sh; \ - echo "fi" >> ${PREFIX}/etc/rc.d/zebra.sh; \ - echo "# ripd" >> ${PREFIX}/etc/rc.d/zebra.sh; \ - echo "if [ -x /usr/local/sbin/ripd -a ! -f /var/run/ripd.pid -a -f /usr/local/etc/ripd.conf ]; then" >> ${PREFIX}/etc/rc.d/zebra.sh; \ - echo " /usr/local/sbin/ripd -d -f /usr/local/etc/ripd.conf" >> ${PREFIX}/etc/rc.d/zebra.sh; \ - echo " echo -n ' ripd'" >> ${PREFIX}/etc/rc.d/zebra.sh; \ - echo "fi" >> ${PREFIX}/etc/rc.d/zebra.sh; \ - echo "# ripngd" >> ${PREFIX}/etc/rc.d/zebra.sh; \ - echo "if [ -x /usr/local/sbin/ripngd -a ! -f /var/run/ripd.pid -a -f /usr/local/etc/ripd.conf ]; then" >> ${PREFIX}/etc/rc.d/zebra.sh; \ - echo " /usr/local/sbin/ripngd -d -f /usr/local/etc/ripd.conf" >> ${PREFIX}/etc/rc.d/zebra.sh; \ - echo " echo -n ' ripngd'" >> ${PREFIX}/etc/rc.d/zebra.sh; \ - echo "fi" >> ${PREFIX}/etc/rc.d/zebra.sh; \ - chmod 751 ${PREFIX}/etc/rc.d/zebra.sh; \ - fi - @echo "If you will access zebra,bgpd,ripd,ripngd with telnet,"; - @echo "then you add some line (written under this line) to /etc/services"; - @echo " zebrasrv 2600/tcp # zebra service"; - @echo " zebra 2601/tcp # zebra vty"; - @echo " ripd 2602/tcp # RIPd vty"; - @echo " ripngd 2603/tcp # RIPngd vty"; - @echo " ospfd 2604/tcp # OSPFd vty"; - @echo " bgpd 2605/tcp # BGPd vty"; - @echo " pimd 2611/tcp # PIMd vty"; - -.include <bsd.port.mk> diff --git a/ports/README b/ports/README deleted file mode 100644 index a650eaa2e..000000000 --- a/ports/README +++ /dev/null @@ -1 +0,0 @@ -This directory contain files for making FreeBSD package. diff --git a/ports/files/md5 b/ports/files/md5 deleted file mode 100644 index 520c348c7..000000000 --- a/ports/files/md5 +++ /dev/null @@ -1 +0,0 @@ -MD5 (zebra-980224.tar.gz) = c6887645741200c43341156c168c7034 diff --git a/ports/pkg/COMMENT b/ports/pkg/COMMENT deleted file mode 100644 index 53c55e365..000000000 --- a/ports/pkg/COMMENT +++ /dev/null @@ -1 +0,0 @@ -Zebra Routing protocol daemon diff --git a/ports/pkg/DESCR b/ports/pkg/DESCR deleted file mode 100644 index aeb1950e2..000000000 --- a/ports/pkg/DESCR +++ /dev/null @@ -1,76 +0,0 @@ -============= -WHAT IS ZEBRA -============= - Zebra is a free software that manages TCP/IP based routing protocol. - It takes multi-server and multi-thread approach to resolve the current -complexity of the Internet. - - Currently zebra is still under development, so If you want to use zebra, -I strongly recommend you to get the latest version of zebra. - Zebra snapshot is released on every monday. - -=================== -SUPPORTED Protocols -=================== - Zebra supports both IPv4 and IPv6 :-) - For supporting IPv4 Routing protocols is here - RIP (both version1 and version2) - RIPv2 supports both Multicast and Broadcast - BGP (only support BGP4) - - For supporting IPv6 Routing protocols is here - RIPng - BGP4+ - -=================== -Supported plat-home -=================== - Now zebra is testing on - o FreeBSD 2.2.8 - -- without IPv6 ;-) - -- with KAME - -- with INRIA IPv6 protocol stack. - - o GNU/Linux 2.2.2 - o GNU/Linux 2.0.36 - -=========== -ZEBRA Ports -=========== - Each daemon has each own terminal interface. Also zebra has communication -port which provides several services to other daemons. Below is zebra ports -list. - -zebrasrv 2600/tcp # zebra service -zebra 2601/tcp # zebra vty -ripd 2602/tcp # RIPd vty -ripngd 2603/tcp # RIPngd vty -ospfd 2604/tcp # OSPFd vty -bgpd 2605/tcp # BGPd vty -pimd 2611/tcp # PIMd vty - -I recommend you to add upper list to /etc/services. - -==================== -For More Information -==================== - Web page is located at: - http://www.zebra.org/ - - Alpha version source file can be found at: - ftp://ftp.zebra.org/pub/zebra/ - - Mailing List is here - zebra@zebra.org - zebra-jp@zebra.org - - If you want to join zebra mailing list, mail to - majordomo@zebra.org - and you write - subscribe zebra - -- if you want to talk with English - subscribe zebra-jp - -- if you want to talk with Japanese - on Mail BODY (Not Subject). - -Enjoy. diff --git a/ports/pkg/PLIST b/ports/pkg/PLIST deleted file mode 100644 index ccc69ebed..000000000 --- a/ports/pkg/PLIST +++ /dev/null @@ -1,8 +0,0 @@ -sbin/zebra -sbin/bgpd -sbin/ripd -etc/bgpd.conf.sample -etc/ripd.conf.sample -etc/zebra.conf.sample -etc/rc.d/zebra.sh -info/zebra.info diff --git a/ripd/rip_northbound.c b/ripd/rip_northbound.c index c1ff678f2..d4fde5519 100644 --- a/ripd/rip_northbound.c +++ b/ripd/rip_northbound.c @@ -79,7 +79,7 @@ static int ripd_instance_create(enum nb_event event, socket = -1; rip = rip_create(vrf_name, vrf, socket); - yang_dnode_set_entry(dnode, rip); + nb_running_set_entry(dnode, rip); break; } @@ -94,7 +94,7 @@ static int ripd_instance_destroy(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - rip = yang_dnode_get_entry(dnode, true); + rip = nb_running_unset_entry(dnode); rip_clean(rip); return NB_OK; @@ -144,7 +144,7 @@ static int ripd_instance_allow_ecmp_modify(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - rip = yang_dnode_get_entry(dnode, true); + rip = nb_running_get_entry(dnode, NULL, true); rip->ecmp = yang_dnode_get_bool(dnode, NULL); if (!rip->ecmp) rip_ecmp_disable(rip); @@ -167,7 +167,7 @@ ripd_instance_default_information_originate_modify(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - rip = yang_dnode_get_entry(dnode, true); + rip = nb_running_get_entry(dnode, NULL, true); default_information = yang_dnode_get_bool(dnode, NULL); memset(&p, 0, sizeof(struct prefix_ipv4)); @@ -199,7 +199,7 @@ static int ripd_instance_default_metric_modify(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - rip = yang_dnode_get_entry(dnode, true); + rip = nb_running_get_entry(dnode, NULL, true); rip->default_metric = yang_dnode_get_uint8(dnode, NULL); /* rip_update_default_metric (); */ @@ -218,7 +218,7 @@ static int ripd_instance_distance_default_modify(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - rip = yang_dnode_get_entry(dnode, true); + rip = nb_running_get_entry(dnode, NULL, true); rip->distance = yang_dnode_get_uint8(dnode, NULL); return NB_OK; @@ -242,10 +242,10 @@ static int ripd_instance_distance_source_create(enum nb_event event, apply_mask_ipv4(&prefix); /* Get RIP distance node. */ - rip = yang_dnode_get_entry(dnode, true); + rip = nb_running_get_entry(dnode, NULL, true); rn = route_node_get(rip->distance_table, (struct prefix *)&prefix); rn->info = rip_distance_new(); - yang_dnode_set_entry(dnode, rn); + nb_running_set_entry(dnode, rn); return NB_OK; } @@ -259,7 +259,7 @@ static int ripd_instance_distance_source_destroy(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - rn = yang_dnode_get_entry(dnode, true); + rn = nb_running_unset_entry(dnode); rdistance = rn->info; rip_distance_free(rdistance); rn->info = NULL; @@ -284,7 +284,7 @@ ripd_instance_distance_source_distance_modify(enum nb_event event, return NB_OK; /* Set distance value. */ - rn = yang_dnode_get_entry(dnode, true); + rn = nb_running_get_entry(dnode, NULL, true); distance = yang_dnode_get_uint8(dnode, NULL); rdistance = rn->info; rdistance->distance = distance; @@ -310,7 +310,7 @@ ripd_instance_distance_source_access_list_modify(enum nb_event event, acl_name = yang_dnode_get_string(dnode, NULL); /* Set access-list */ - rn = yang_dnode_get_entry(dnode, true); + rn = nb_running_get_entry(dnode, NULL, true); rdistance = rn->info; if (rdistance->access_list) free(rdistance->access_list); @@ -330,7 +330,7 @@ ripd_instance_distance_source_access_list_destroy(enum nb_event event, return NB_OK; /* Reset access-list configuration. */ - rn = yang_dnode_get_entry(dnode, true); + rn = nb_running_get_entry(dnode, NULL, true); rdistance = rn->info; free(rdistance->access_list); rdistance->access_list = NULL; @@ -351,7 +351,7 @@ static int ripd_instance_explicit_neighbor_create(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - rip = yang_dnode_get_entry(dnode, true); + rip = nb_running_get_entry(dnode, NULL, true); p.family = AF_INET; p.prefixlen = IPV4_MAX_BITLEN; yang_dnode_get_ipv4(&p.prefix, dnode, NULL); @@ -368,7 +368,7 @@ static int ripd_instance_explicit_neighbor_destroy(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - rip = yang_dnode_get_entry(dnode, true); + rip = nb_running_get_entry(dnode, NULL, true); p.family = AF_INET; p.prefixlen = IPV4_MAX_BITLEN; yang_dnode_get_ipv4(&p.prefix, dnode, NULL); @@ -389,7 +389,7 @@ static int ripd_instance_network_create(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - rip = yang_dnode_get_entry(dnode, true); + rip = nb_running_get_entry(dnode, NULL, true); yang_dnode_get_ipv4p(&p, dnode, NULL); apply_mask_ipv4((struct prefix_ipv4 *)&p); @@ -405,7 +405,7 @@ static int ripd_instance_network_destroy(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - rip = yang_dnode_get_entry(dnode, true); + rip = nb_running_get_entry(dnode, NULL, true); yang_dnode_get_ipv4p(&p, dnode, NULL); apply_mask_ipv4((struct prefix_ipv4 *)&p); @@ -425,7 +425,7 @@ static int ripd_instance_interface_create(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - rip = yang_dnode_get_entry(dnode, true); + rip = nb_running_get_entry(dnode, NULL, true); ifname = yang_dnode_get_string(dnode, NULL); return rip_enable_if_add(rip, ifname); @@ -440,7 +440,7 @@ static int ripd_instance_interface_destroy(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - rip = yang_dnode_get_entry(dnode, true); + rip = nb_running_get_entry(dnode, NULL, true); ifname = yang_dnode_get_string(dnode, NULL); return rip_enable_if_delete(rip, ifname); @@ -460,11 +460,11 @@ static int ripd_instance_offset_list_create(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - rip = yang_dnode_get_entry(dnode, true); + rip = nb_running_get_entry(dnode, NULL, true); ifname = yang_dnode_get_string(dnode, "./interface"); offset = rip_offset_list_new(rip, ifname); - yang_dnode_set_entry(dnode, offset); + nb_running_set_entry(dnode, offset); return NB_OK; } @@ -480,7 +480,7 @@ static int ripd_instance_offset_list_destroy(enum nb_event event, direct = yang_dnode_get_enum(dnode, "./direction"); - offset = yang_dnode_get_entry(dnode, true); + offset = nb_running_unset_entry(dnode); if (offset->direct[direct].alist_name) { free(offset->direct[direct].alist_name); offset->direct[direct].alist_name = NULL; @@ -510,7 +510,7 @@ ripd_instance_offset_list_access_list_modify(enum nb_event event, direct = yang_dnode_get_enum(dnode, "../direction"); alist_name = yang_dnode_get_string(dnode, NULL); - offset = yang_dnode_get_entry(dnode, true); + offset = nb_running_get_entry(dnode, NULL, true); if (offset->direct[direct].alist_name) free(offset->direct[direct].alist_name); offset->direct[direct].alist_name = strdup(alist_name); @@ -535,7 +535,7 @@ static int ripd_instance_offset_list_metric_modify(enum nb_event event, direct = yang_dnode_get_enum(dnode, "../direction"); metric = yang_dnode_get_uint8(dnode, NULL); - offset = yang_dnode_get_entry(dnode, true); + offset = nb_running_get_entry(dnode, NULL, true); offset->direct[direct].metric = metric; return NB_OK; @@ -553,7 +553,7 @@ static int ripd_instance_passive_default_modify(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - rip = yang_dnode_get_entry(dnode, true); + rip = nb_running_get_entry(dnode, NULL, true); rip->passive_default = yang_dnode_get_bool(dnode, NULL); rip_passive_nondefault_clean(rip); @@ -573,7 +573,7 @@ static int ripd_instance_passive_interface_create(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - rip = yang_dnode_get_entry(dnode, true); + rip = nb_running_get_entry(dnode, NULL, true); ifname = yang_dnode_get_string(dnode, NULL); return rip_passive_nondefault_set(rip, ifname); @@ -588,7 +588,7 @@ static int ripd_instance_passive_interface_destroy(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - rip = yang_dnode_get_entry(dnode, true); + rip = nb_running_get_entry(dnode, NULL, true); ifname = yang_dnode_get_string(dnode, NULL); return rip_passive_nondefault_unset(rip, ifname); @@ -608,7 +608,7 @@ ripd_instance_non_passive_interface_create(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - rip = yang_dnode_get_entry(dnode, true); + rip = nb_running_get_entry(dnode, NULL, true); ifname = yang_dnode_get_string(dnode, NULL); return rip_passive_nondefault_unset(rip, ifname); @@ -624,7 +624,7 @@ ripd_instance_non_passive_interface_destroy(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - rip = yang_dnode_get_entry(dnode, true); + rip = nb_running_get_entry(dnode, NULL, true); ifname = yang_dnode_get_string(dnode, NULL); return rip_passive_nondefault_set(rip, ifname); @@ -643,7 +643,7 @@ static int ripd_instance_redistribute_create(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - rip = yang_dnode_get_entry(dnode, true); + rip = nb_running_get_entry(dnode, NULL, true); type = yang_dnode_get_enum(dnode, "./protocol"); rip->redist[type].enabled = true; @@ -660,7 +660,7 @@ static int ripd_instance_redistribute_destroy(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - rip = yang_dnode_get_entry(dnode, true); + rip = nb_running_get_entry(dnode, NULL, true); type = yang_dnode_get_enum(dnode, "./protocol"); rip->redist[type].enabled = false; @@ -684,7 +684,7 @@ ripd_instance_redistribute_apply_finish(const struct lyd_node *dnode) struct rip *rip; int type; - rip = yang_dnode_get_entry(dnode, true); + rip = nb_running_get_entry(dnode, NULL, true); type = yang_dnode_get_enum(dnode, "./protocol"); if (rip->enabled) @@ -706,7 +706,7 @@ ripd_instance_redistribute_route_map_modify(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - rip = yang_dnode_get_entry(dnode, true); + rip = nb_running_get_entry(dnode, NULL, true); type = yang_dnode_get_enum(dnode, "../protocol"); rmap_name = yang_dnode_get_string(dnode, NULL); @@ -728,7 +728,7 @@ ripd_instance_redistribute_route_map_destroy(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - rip = yang_dnode_get_entry(dnode, true); + rip = nb_running_get_entry(dnode, NULL, true); type = yang_dnode_get_enum(dnode, "../protocol"); free(rip->redist[type].route_map.name); @@ -753,7 +753,7 @@ ripd_instance_redistribute_metric_modify(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - rip = yang_dnode_get_entry(dnode, true); + rip = nb_running_get_entry(dnode, NULL, true); type = yang_dnode_get_enum(dnode, "../protocol"); metric = yang_dnode_get_uint8(dnode, NULL); @@ -773,7 +773,7 @@ ripd_instance_redistribute_metric_destroy(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - rip = yang_dnode_get_entry(dnode, true); + rip = nb_running_get_entry(dnode, NULL, true); type = yang_dnode_get_enum(dnode, "../protocol"); rip->redist[type].metric_config = false; @@ -796,7 +796,7 @@ static int ripd_instance_static_route_create(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - rip = yang_dnode_get_entry(dnode, true); + rip = nb_running_get_entry(dnode, NULL, true); yang_dnode_get_ipv4p(&p, dnode, NULL); apply_mask_ipv4(&p); @@ -817,7 +817,7 @@ static int ripd_instance_static_route_destroy(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - rip = yang_dnode_get_entry(dnode, true); + rip = nb_running_get_entry(dnode, NULL, true); yang_dnode_get_ipv4p(&p, dnode, NULL); apply_mask_ipv4(&p); @@ -833,7 +833,7 @@ static void ripd_instance_timers_apply_finish(const struct lyd_node *dnode) { struct rip *rip; - rip = yang_dnode_get_entry(dnode, true); + rip = nb_running_get_entry(dnode, NULL, true); /* Reset update timer thread. */ rip_event(rip, RIP_UPDATE_EVENT, 0); @@ -852,7 +852,7 @@ ripd_instance_timers_flush_interval_modify(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - rip = yang_dnode_get_entry(dnode, true); + rip = nb_running_get_entry(dnode, NULL, true); rip->garbage_time = yang_dnode_get_uint32(dnode, NULL); return NB_OK; @@ -871,7 +871,7 @@ ripd_instance_timers_holddown_interval_modify(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - rip = yang_dnode_get_entry(dnode, true); + rip = nb_running_get_entry(dnode, NULL, true); rip->timeout_time = yang_dnode_get_uint32(dnode, NULL); return NB_OK; @@ -890,7 +890,7 @@ ripd_instance_timers_update_interval_modify(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - rip = yang_dnode_get_entry(dnode, true); + rip = nb_running_get_entry(dnode, NULL, true); rip->update_time = yang_dnode_get_uint32(dnode, NULL); return NB_OK; @@ -908,7 +908,7 @@ static int ripd_instance_version_receive_modify(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - rip = yang_dnode_get_entry(dnode, true); + rip = nb_running_get_entry(dnode, NULL, true); rip->version_recv = yang_dnode_get_enum(dnode, NULL); return NB_OK; @@ -926,7 +926,7 @@ static int ripd_instance_version_send_modify(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - rip = yang_dnode_get_entry(dnode, true); + rip = nb_running_get_entry(dnode, NULL, true); rip->version_send = yang_dnode_get_enum(dnode, NULL); return NB_OK; @@ -945,7 +945,7 @@ static int lib_interface_rip_split_horizon_modify(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - ifp = yang_dnode_get_entry(dnode, true); + ifp = nb_running_get_entry(dnode, NULL, true); ri = ifp->info; ri->split_horizon = yang_dnode_get_enum(dnode, NULL); @@ -965,7 +965,7 @@ static int lib_interface_rip_v2_broadcast_modify(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - ifp = yang_dnode_get_entry(dnode, true); + ifp = nb_running_get_entry(dnode, NULL, true); ri = ifp->info; ri->v2_broadcast = yang_dnode_get_bool(dnode, NULL); @@ -986,7 +986,7 @@ lib_interface_rip_version_receive_modify(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - ifp = yang_dnode_get_entry(dnode, true); + ifp = nb_running_get_entry(dnode, NULL, true); ri = ifp->info; ri->ri_receive = yang_dnode_get_enum(dnode, NULL); @@ -1006,7 +1006,7 @@ static int lib_interface_rip_version_send_modify(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - ifp = yang_dnode_get_entry(dnode, true); + ifp = nb_running_get_entry(dnode, NULL, true); ri = ifp->info; ri->ri_send = yang_dnode_get_enum(dnode, NULL); @@ -1026,7 +1026,7 @@ static int lib_interface_rip_authentication_scheme_mode_modify( if (event != NB_EV_APPLY) return NB_OK; - ifp = yang_dnode_get_entry(dnode, true); + ifp = nb_running_get_entry(dnode, NULL, true); ri = ifp->info; ri->auth_type = yang_dnode_get_enum(dnode, NULL); @@ -1047,7 +1047,7 @@ static int lib_interface_rip_authentication_scheme_md5_auth_length_modify( if (event != NB_EV_APPLY) return NB_OK; - ifp = yang_dnode_get_entry(dnode, true); + ifp = nb_running_get_entry(dnode, NULL, true); ri = ifp->info; ri->md5_auth_len = yang_dnode_get_enum(dnode, NULL); @@ -1063,7 +1063,7 @@ static int lib_interface_rip_authentication_scheme_md5_auth_length_destroy( if (event != NB_EV_APPLY) return NB_OK; - ifp = yang_dnode_get_entry(dnode, true); + ifp = nb_running_get_entry(dnode, NULL, true); ri = ifp->info; ri->md5_auth_len = yang_get_default_enum( "%s/authentication-scheme/md5-auth-length", RIP_IFACE); @@ -1085,7 +1085,7 @@ lib_interface_rip_authentication_password_modify(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - ifp = yang_dnode_get_entry(dnode, true); + ifp = nb_running_get_entry(dnode, NULL, true); ri = ifp->info; XFREE(MTYPE_RIP_INTERFACE_STRING, ri->auth_str); ri->auth_str = XSTRDUP(MTYPE_RIP_INTERFACE_STRING, @@ -1104,7 +1104,7 @@ lib_interface_rip_authentication_password_destroy(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - ifp = yang_dnode_get_entry(dnode, true); + ifp = nb_running_get_entry(dnode, NULL, true); ri = ifp->info; XFREE(MTYPE_RIP_INTERFACE_STRING, ri->auth_str); @@ -1125,7 +1125,7 @@ lib_interface_rip_authentication_key_chain_modify(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - ifp = yang_dnode_get_entry(dnode, true); + ifp = nb_running_get_entry(dnode, NULL, true); ri = ifp->info; XFREE(MTYPE_RIP_INTERFACE_STRING, ri->key_chain); ri->key_chain = XSTRDUP(MTYPE_RIP_INTERFACE_STRING, @@ -1144,7 +1144,7 @@ lib_interface_rip_authentication_key_chain_destroy(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - ifp = yang_dnode_get_entry(dnode, true); + ifp = nb_running_get_entry(dnode, NULL, true); ri = ifp->info; XFREE(MTYPE_RIP_INTERFACE_STRING, ri->key_chain); diff --git a/ripd/rip_offset.c b/ripd/rip_offset.c index b3f84fe50..8307a95d2 100644 --- a/ripd/rip_offset.c +++ b/ripd/rip_offset.c @@ -50,6 +50,11 @@ struct rip_offset_list *rip_offset_list_new(struct rip *rip, const char *ifname) void offset_list_del(struct rip_offset_list *offset) { listnode_delete(offset->rip->offset_list_master, offset); + offset_list_free(offset); +} + +void offset_list_free(struct rip_offset_list *offset) +{ if (OFFSET_LIST_IN_NAME(offset)) free(OFFSET_LIST_IN_NAME(offset)); if (OFFSET_LIST_OUT_NAME(offset)) diff --git a/ripd/ripd.c b/ripd/ripd.c index 201de9a46..3a1ffd17a 100644 --- a/ripd/ripd.c +++ b/ripd/ripd.c @@ -2712,7 +2712,7 @@ struct rip *rip_create(const char *vrf_name, struct vrf *vrf, int socket) rip->passive_nondefault = vector_init(1); rip->offset_list_master = list_new(); rip->offset_list_master->cmp = (int (*)(void *, void *))offset_list_cmp; - rip->offset_list_master->del = (void (*)(void *))offset_list_del; + rip->offset_list_master->del = (void (*)(void *))offset_list_free; /* Distribute list install. */ rip->distribute_ctx = distribute_list_ctx_create(vrf); diff --git a/ripd/ripd.h b/ripd/ripd.h index 7b196a16b..44f5932fb 100644 --- a/ripd/ripd.h +++ b/ripd/ripd.h @@ -507,6 +507,7 @@ extern struct rip_info *rip_ecmp_delete(struct rip *rip, extern struct rip_offset_list *rip_offset_list_new(struct rip *rip, const char *ifname); extern void offset_list_del(struct rip_offset_list *offset); +extern void offset_list_free(struct rip_offset_list *offset); extern struct rip_offset_list *rip_offset_list_lookup(struct rip *rip, const char *ifname); extern int rip_offset_list_apply_in(struct prefix_ipv4 *, struct interface *, diff --git a/ripngd/ripng_northbound.c b/ripngd/ripng_northbound.c index c483ad65f..f8ac4a5cd 100644 --- a/ripngd/ripng_northbound.c +++ b/ripngd/ripng_northbound.c @@ -81,7 +81,7 @@ static int ripngd_instance_create(enum nb_event event, socket = -1; ripng = ripng_create(vrf_name, vrf, socket); - yang_dnode_set_entry(dnode, ripng); + nb_running_set_entry(dnode, ripng); break; } @@ -96,7 +96,7 @@ static int ripngd_instance_destroy(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - ripng = yang_dnode_get_entry(dnode, true); + ripng = nb_running_unset_entry(dnode); ripng_clean(ripng); return NB_OK; @@ -147,7 +147,7 @@ static int ripngd_instance_allow_ecmp_modify(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - ripng = yang_dnode_get_entry(dnode, true); + ripng = nb_running_get_entry(dnode, NULL, true); ripng->ecmp = yang_dnode_get_bool(dnode, NULL); if (!ripng->ecmp) ripng_ecmp_disable(ripng); @@ -169,7 +169,7 @@ static int ripngd_instance_default_information_originate_modify( if (event != NB_EV_APPLY) return NB_OK; - ripng = yang_dnode_get_entry(dnode, true); + ripng = nb_running_get_entry(dnode, NULL, true); default_information = yang_dnode_get_bool(dnode, NULL); str2prefix_ipv6("::/0", &p); @@ -196,7 +196,7 @@ static int ripngd_instance_default_metric_modify(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - ripng = yang_dnode_get_entry(dnode, true); + ripng = nb_running_get_entry(dnode, NULL, true); ripng->default_metric = yang_dnode_get_uint8(dnode, NULL); return NB_OK; @@ -215,7 +215,7 @@ static int ripngd_instance_network_create(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - ripng = yang_dnode_get_entry(dnode, true); + ripng = nb_running_get_entry(dnode, NULL, true); yang_dnode_get_ipv6p(&p, dnode, NULL); apply_mask_ipv6((struct prefix_ipv6 *)&p); @@ -231,7 +231,7 @@ static int ripngd_instance_network_destroy(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - ripng = yang_dnode_get_entry(dnode, true); + ripng = nb_running_get_entry(dnode, NULL, true); yang_dnode_get_ipv6p(&p, dnode, NULL); apply_mask_ipv6((struct prefix_ipv6 *)&p); @@ -251,7 +251,7 @@ static int ripngd_instance_interface_create(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - ripng = yang_dnode_get_entry(dnode, true); + ripng = nb_running_get_entry(dnode, NULL, true); ifname = yang_dnode_get_string(dnode, NULL); return ripng_enable_if_add(ripng, ifname); @@ -266,7 +266,7 @@ static int ripngd_instance_interface_destroy(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - ripng = yang_dnode_get_entry(dnode, true); + ripng = nb_running_get_entry(dnode, NULL, true); ifname = yang_dnode_get_string(dnode, NULL); return ripng_enable_if_delete(ripng, ifname); @@ -286,11 +286,11 @@ static int ripngd_instance_offset_list_create(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - ripng = yang_dnode_get_entry(dnode, true); + ripng = nb_running_get_entry(dnode, NULL, true); ifname = yang_dnode_get_string(dnode, "./interface"); offset = ripng_offset_list_new(ripng, ifname); - yang_dnode_set_entry(dnode, offset); + nb_running_set_entry(dnode, offset); return NB_OK; } @@ -306,7 +306,7 @@ static int ripngd_instance_offset_list_destroy(enum nb_event event, direct = yang_dnode_get_enum(dnode, "./direction"); - offset = yang_dnode_get_entry(dnode, true); + offset = nb_running_unset_entry(dnode); if (offset->direct[direct].alist_name) { free(offset->direct[direct].alist_name); offset->direct[direct].alist_name = NULL; @@ -336,7 +336,7 @@ ripngd_instance_offset_list_access_list_modify(enum nb_event event, direct = yang_dnode_get_enum(dnode, "../direction"); alist_name = yang_dnode_get_string(dnode, NULL); - offset = yang_dnode_get_entry(dnode, true); + offset = nb_running_get_entry(dnode, NULL, true); if (offset->direct[direct].alist_name) free(offset->direct[direct].alist_name); offset->direct[direct].alist_name = strdup(alist_name); @@ -362,7 +362,7 @@ ripngd_instance_offset_list_metric_modify(enum nb_event event, direct = yang_dnode_get_enum(dnode, "../direction"); metric = yang_dnode_get_uint8(dnode, NULL); - offset = yang_dnode_get_entry(dnode, true); + offset = nb_running_get_entry(dnode, NULL, true); offset->direct[direct].metric = metric; return NB_OK; @@ -382,7 +382,7 @@ ripngd_instance_passive_interface_create(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - ripng = yang_dnode_get_entry(dnode, true); + ripng = nb_running_get_entry(dnode, NULL, true); ifname = yang_dnode_get_string(dnode, NULL); return ripng_passive_interface_set(ripng, ifname); @@ -398,7 +398,7 @@ ripngd_instance_passive_interface_destroy(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - ripng = yang_dnode_get_entry(dnode, true); + ripng = nb_running_get_entry(dnode, NULL, true); ifname = yang_dnode_get_string(dnode, NULL); return ripng_passive_interface_unset(ripng, ifname); @@ -417,7 +417,7 @@ static int ripngd_instance_redistribute_create(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - ripng = yang_dnode_get_entry(dnode, true); + ripng = nb_running_get_entry(dnode, NULL, true); type = yang_dnode_get_enum(dnode, "./protocol"); ripng->redist[type].enabled = true; @@ -434,7 +434,7 @@ static int ripngd_instance_redistribute_destroy(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - ripng = yang_dnode_get_entry(dnode, true); + ripng = nb_running_get_entry(dnode, NULL, true); type = yang_dnode_get_enum(dnode, "./protocol"); ripng->redist[type].enabled = false; @@ -458,7 +458,7 @@ ripngd_instance_redistribute_apply_finish(const struct lyd_node *dnode) struct ripng *ripng; int type; - ripng = yang_dnode_get_entry(dnode, true); + ripng = nb_running_get_entry(dnode, NULL, true); type = yang_dnode_get_enum(dnode, "./protocol"); if (ripng->enabled) @@ -480,7 +480,7 @@ ripngd_instance_redistribute_route_map_modify(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - ripng = yang_dnode_get_entry(dnode, true); + ripng = nb_running_get_entry(dnode, NULL, true); type = yang_dnode_get_enum(dnode, "../protocol"); rmap_name = yang_dnode_get_string(dnode, NULL); @@ -502,7 +502,7 @@ ripngd_instance_redistribute_route_map_destroy(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - ripng = yang_dnode_get_entry(dnode, true); + ripng = nb_running_get_entry(dnode, NULL, true); type = yang_dnode_get_enum(dnode, "../protocol"); free(ripng->redist[type].route_map.name); @@ -527,7 +527,7 @@ ripngd_instance_redistribute_metric_modify(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - ripng = yang_dnode_get_entry(dnode, true); + ripng = nb_running_get_entry(dnode, NULL, true); type = yang_dnode_get_enum(dnode, "../protocol"); metric = yang_dnode_get_uint8(dnode, NULL); @@ -547,7 +547,7 @@ ripngd_instance_redistribute_metric_destroy(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - ripng = yang_dnode_get_entry(dnode, true); + ripng = nb_running_get_entry(dnode, NULL, true); type = yang_dnode_get_enum(dnode, "../protocol"); ripng->redist[type].metric_config = false; @@ -569,7 +569,7 @@ static int ripngd_instance_static_route_create(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - ripng = yang_dnode_get_entry(dnode, true); + ripng = nb_running_get_entry(dnode, NULL, true); yang_dnode_get_ipv6p(&p, dnode, NULL); apply_mask_ipv6(&p); @@ -588,7 +588,7 @@ static int ripngd_instance_static_route_destroy(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - ripng = yang_dnode_get_entry(dnode, true); + ripng = nb_running_get_entry(dnode, NULL, true); yang_dnode_get_ipv6p(&p, dnode, NULL); apply_mask_ipv6(&p); @@ -612,7 +612,7 @@ ripngd_instance_aggregate_address_create(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - ripng = yang_dnode_get_entry(dnode, true); + ripng = nb_running_get_entry(dnode, NULL, true); yang_dnode_get_ipv6p(&p, dnode, NULL); apply_mask_ipv6(&p); @@ -631,7 +631,7 @@ ripngd_instance_aggregate_address_destroy(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - ripng = yang_dnode_get_entry(dnode, true); + ripng = nb_running_get_entry(dnode, NULL, true); yang_dnode_get_ipv6p(&p, dnode, NULL); apply_mask_ipv6(&p); @@ -647,7 +647,7 @@ static void ripngd_instance_timers_apply_finish(const struct lyd_node *dnode) { struct ripng *ripng; - ripng = yang_dnode_get_entry(dnode, true); + ripng = nb_running_get_entry(dnode, NULL, true); /* Reset update timer thread. */ ripng_event(ripng, RIPNG_UPDATE_EVENT, 0); @@ -666,7 +666,7 @@ ripngd_instance_timers_flush_interval_modify(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - ripng = yang_dnode_get_entry(dnode, true); + ripng = nb_running_get_entry(dnode, NULL, true); ripng->garbage_time = yang_dnode_get_uint16(dnode, NULL); return NB_OK; @@ -685,7 +685,7 @@ ripngd_instance_timers_holddown_interval_modify(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - ripng = yang_dnode_get_entry(dnode, true); + ripng = nb_running_get_entry(dnode, NULL, true); ripng->timeout_time = yang_dnode_get_uint16(dnode, NULL); return NB_OK; @@ -704,7 +704,7 @@ ripngd_instance_timers_update_interval_modify(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - ripng = yang_dnode_get_entry(dnode, true); + ripng = nb_running_get_entry(dnode, NULL, true); ripng->update_time = yang_dnode_get_uint16(dnode, NULL); return NB_OK; @@ -999,7 +999,7 @@ lib_interface_ripng_split_horizon_modify(enum nb_event event, if (event != NB_EV_APPLY) return NB_OK; - ifp = yang_dnode_get_entry(dnode, true); + ifp = nb_running_get_entry(dnode, NULL, true); ri = ifp->info; ri->split_horizon = yang_dnode_get_enum(dnode, NULL); diff --git a/ripngd/ripng_offset.c b/ripngd/ripng_offset.c index 41ba2360b..fe95ccfc2 100644 --- a/ripngd/ripng_offset.c +++ b/ripngd/ripng_offset.c @@ -56,6 +56,11 @@ struct ripng_offset_list *ripng_offset_list_new(struct ripng *ripng, void ripng_offset_list_del(struct ripng_offset_list *offset) { listnode_delete(offset->ripng->offset_list_master, offset); + ripng_offset_list_free(offset); +} + +void ripng_offset_list_free(struct ripng_offset_list *offset) +{ if (OFFSET_LIST_IN_NAME(offset)) free(OFFSET_LIST_IN_NAME(offset)); if (OFFSET_LIST_OUT_NAME(offset)) diff --git a/ripngd/ripngd.c b/ripngd/ripngd.c index b36cee2c5..411689a7a 100644 --- a/ripngd/ripngd.c +++ b/ripngd/ripngd.c @@ -1889,7 +1889,7 @@ struct ripng *ripng_create(const char *vrf_name, struct vrf *vrf, int socket) ripng->offset_list_master->cmp = (int (*)(void *, void *))offset_list_cmp; ripng->offset_list_master->del = - (void (*)(void *))ripng_offset_list_del; + (void (*)(void *))ripng_offset_list_free; ripng->distribute_ctx = distribute_list_ctx_create(vrf); distribute_list_add_hook(ripng->distribute_ctx, ripng_distribute_update); diff --git a/ripngd/ripngd.h b/ripngd/ripngd.h index dcc61ae58..dc425b695 100644 --- a/ripngd/ripngd.h +++ b/ripngd/ripngd.h @@ -421,6 +421,7 @@ extern void ripng_peer_list_del(void *arg); extern struct ripng_offset_list *ripng_offset_list_new(struct ripng *ripng, const char *ifname); extern void ripng_offset_list_del(struct ripng_offset_list *offset); +extern void ripng_offset_list_free(struct ripng_offset_list *offset); extern struct ripng_offset_list *ripng_offset_list_lookup(struct ripng *ripng, const char *ifname); extern int ripng_offset_list_apply_in(struct ripng *ripng, diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/adjacencies.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/adjacencies.py index 28ecfeec5..5674120b9 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/adjacencies.py +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/adjacencies.py @@ -1,7 +1,7 @@ from lutil import luCommand luCommand('ce1','vtysh -c "show bgp summary"',' 00:0','wait','Adjacencies up',180) -luCommand('ce2','vtysh -c "show bgp summary"',' 00:0','wait','Adjacencies up') -luCommand('ce3','vtysh -c "show bgp summary"',' 00:0','wait','Adjacencies up') +luCommand('ce2','vtysh -c "show bgp summary"',' 00:0','wait','Adjacencies up',180) +luCommand('ce3','vtysh -c "show bgp summary"',' 00:0','wait','Adjacencies up',180) luCommand('ce4','vtysh -c "show bgp vrf all summary"',' 00:0','wait','Adjacencies up',180) luCommand('r1','ping 2.2.2.2 -c 1',' 0. packet loss','wait','PE->P2 (loopback) ping',60) luCommand('r3','ping 2.2.2.2 -c 1',' 0. packet loss','wait','PE->P2 (loopback) ping',60) diff --git a/tests/topotests/bgp_maximum_prefix_invalid_update/__init__.py b/tests/topotests/bgp_maximum_prefix_invalid_update/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/tests/topotests/bgp_maximum_prefix_invalid_update/__init__.py diff --git a/tests/topotests/bgp_maximum_prefix_invalid_update/r1/bgpd.conf b/tests/topotests/bgp_maximum_prefix_invalid_update/r1/bgpd.conf new file mode 100644 index 000000000..235b42b3d --- /dev/null +++ b/tests/topotests/bgp_maximum_prefix_invalid_update/r1/bgpd.conf @@ -0,0 +1,4 @@ +router bgp 65000 + neighbor 192.168.255.2 remote-as 65001 + address-family ipv4 unicast + redistribute connected diff --git a/tests/topotests/bgp_maximum_prefix_invalid_update/r1/zebra.conf b/tests/topotests/bgp_maximum_prefix_invalid_update/r1/zebra.conf new file mode 100644 index 000000000..0a283c06d --- /dev/null +++ b/tests/topotests/bgp_maximum_prefix_invalid_update/r1/zebra.conf @@ -0,0 +1,9 @@ +! +interface lo + ip address 172.16.255.254/32 +! +interface r1-eth0 + ip address 192.168.255.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_maximum_prefix_invalid_update/r2/bgpd.conf b/tests/topotests/bgp_maximum_prefix_invalid_update/r2/bgpd.conf new file mode 100644 index 000000000..e01628415 --- /dev/null +++ b/tests/topotests/bgp_maximum_prefix_invalid_update/r2/bgpd.conf @@ -0,0 +1,4 @@ +router bgp 65001 + neighbor 192.168.255.1 remote-as 65000 + address-family ipv4 + neighbor 192.168.255.1 maximum-prefix 1 diff --git a/tests/topotests/bgp_maximum_prefix_invalid_update/r2/zebra.conf b/tests/topotests/bgp_maximum_prefix_invalid_update/r2/zebra.conf new file mode 100644 index 000000000..606c17bec --- /dev/null +++ b/tests/topotests/bgp_maximum_prefix_invalid_update/r2/zebra.conf @@ -0,0 +1,6 @@ +! +interface r2-eth0 + ip address 192.168.255.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_maximum_prefix_invalid_update/test_bgp_maximum_prefix_invalid_update.py b/tests/topotests/bgp_maximum_prefix_invalid_update/test_bgp_maximum_prefix_invalid_update.py new file mode 100644 index 000000000..69b8c7ca5 --- /dev/null +++ b/tests/topotests/bgp_maximum_prefix_invalid_update/test_bgp_maximum_prefix_invalid_update.py @@ -0,0 +1,113 @@ +#!/usr/bin/env python + +# +# bgp_local_as_private_remove.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2019 by +# Network Device Education Foundation, Inc. ("NetDEF") +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +bgp_maximum_prefix_invalid_update.py: +Test if unnecesarry UPDATE message like below: + +[Error] Error parsing NLRI +%NOTIFICATION: sent to neighbor X.X.X.X 3/10 (UPDATE Message Error/Invalid Network Field) 0 bytes + +is not sent if maximum-prefix count is overflow. +""" + +import os +import sys +import json +import time +import pytest + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, '../')) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger +from mininet.topo import Topo + +class TemplateTopo(Topo): + def build(self, *_args, **_opts): + tgen = get_topogen(self) + + for routern in range(1, 3): + tgen.add_router('r{}'.format(routern)) + + switch = tgen.add_switch('s1') + switch.add_link(tgen.gears['r1']) + switch.add_link(tgen.gears['r2']) + +def setup_module(mod): + tgen = Topogen(TemplateTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.iteritems(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, + os.path.join(CWD, '{}/zebra.conf'.format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, + os.path.join(CWD, '{}/bgpd.conf'.format(rname)) + ) + + tgen.start_router() + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + +def test_bgp_maximum_prefix_invalid(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + def _bgp_converge(router): + while True: + output = json.loads(tgen.gears[router].vtysh_cmd("show ip bgp neighbor 192.168.255.1 json")) + if output['192.168.255.1']['connectionsEstablished'] > 3: + return True + time.sleep(1) + + def _bgp_parsing_nlri(router): + cmd_max_exceeded = 'grep "%MAXPFXEXCEED: No. of IPv4 Unicast prefix received" bgpd.log' + cmdt_error_parsing_nlri = 'grep "Error parsing NLRI" bgpd.log' + output_max_exceeded = tgen.gears[router].run(cmd_max_exceeded) + output_error_parsing_nlri = tgen.gears[router].run(cmdt_error_parsing_nlri) + + if len(output_max_exceeded) > 0: + if len(output_error_parsing_nlri) > 0: + return False + return True + + + if _bgp_converge('r2'): + assert _bgp_parsing_nlri('r2') == True + +if __name__ == '__main__': + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tools/frr-reload.py b/tools/frr-reload.py index 59f1bcf52..ce1db770d 100755 --- a/tools/frr-reload.py +++ b/tools/frr-reload.py @@ -742,6 +742,27 @@ def ignore_delete_re_add_lines(lines_to_add, lines_to_del): lines_to_add_to_del.append((tmp_ctx_keys, swpx_peergroup)) ''' + Changing the bfd timers on neighbors is allowed without doing + a delete/add process. Since doing a "no neighbor blah bfd ..." + will cause the peer to bounce unnecessarily, just skip the delete + and just do the add. + ''' + re_nbr_bfd_timers = re.search(r'neighbor (\S+) bfd (\S+) (\S+) (\S+)', line) + + if re_nbr_bfd_timers: + nbr = re_nbr_bfd_timers.group(1) + bfd_nbr = "neighbor %s" % nbr + + for (ctx_keys, add_line) in lines_to_add: + re_add_nbr_bfd_timers = re.search(r'neighbor (\S+) bfd (\S+) (\S+) (\S+)', add_line) + + if re_add_nbr_bfd_timers: + found_add_bfd_nbr = line_exist(lines_to_add, ctx_keys, bfd_nbr, False) + + if found_add_bfd_nbr: + lines_to_del_to_del.append((ctx_keys, line)) + + ''' We changed how we display the neighbor interface command. Older versions of frr would display the following: neighbor swp1 interface diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index b8da90ca8..eff1e996e 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -2172,7 +2172,7 @@ DEFUNSH(VTYSH_VRF, vtysh_quit_vrf, vtysh_quit_vrf_cmd, "quit", return vtysh_exit_vrf(self, vty, argc, argv); } -DEFUNSH(VTYSH_PBRD, vtysh_exit_nexthop_group, vtysh_exit_nexthop_group_cmd, +DEFUNSH(VTYSH_PBRD | VTYSH_SHARPD, vtysh_exit_nexthop_group, vtysh_exit_nexthop_group_cmd, "exit", "Exit current mode and down to previous mode\n") { return vtysh_exit(vty); diff --git a/yang/frr-isisd.yang b/yang/frr-isisd.yang index d0d11c867..7b132cb61 100644 --- a/yang/frr-isisd.yang +++ b/yang/frr-isisd.yang @@ -707,16 +707,16 @@ module frr-isisd { description "Log changes to the IS-IS adjacencies in this area."; } - } - container mpls-te { - presence "Present if MPLS-TE is enabled."; - description - "Enable MPLS-TE functionality."; - leaf router-address { - type inet:ipv4-address; + container mpls-te { + presence "Present if MPLS-TE is enabled."; description - "Stable IP address of the advertising router."; + "Enable MPLS-TE functionality."; + leaf router-address { + type inet:ipv4-address; + description + "Stable IP address of the advertising router."; + } } } } diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c index b2f470bc8..ba518ea57 100644 --- a/zebra/if_netlink.c +++ b/zebra/if_netlink.c @@ -481,6 +481,11 @@ static int netlink_extract_vxlan_info(struct rtattr *link_data, vxl_info->vtep_ip = vtep_ip_in_msg; } + if (attr[IFLA_VXLAN_GROUP]) { + vxl_info->mcast_grp = + *(struct in_addr *)RTA_DATA(attr[IFLA_VXLAN_GROUP]); + } + return 0; } @@ -834,11 +839,12 @@ int kernel_interface_set_master(struct interface *master, } /* Interface address modification. */ -static int netlink_address(int cmd, int family, struct interface *ifp, - struct connected *ifc) +static int netlink_address_ctx(const struct zebra_dplane_ctx *ctx) { int bytelen; - struct prefix *p; + const struct prefix *p; + int cmd; + const char *label; struct { struct nlmsghdr n; @@ -846,72 +852,59 @@ static int netlink_address(int cmd, int family, struct interface *ifp, char buf[NL_PKT_BUF_SIZE]; } req; - struct zebra_ns *zns; - - if (vrf_is_backend_netns()) - zns = zebra_ns_lookup((ns_id_t)ifp->vrf_id); - else - zns = zebra_ns_lookup(NS_DEFAULT); - p = ifc->address; - memset(&req, 0, sizeof req - NL_PKT_BUF_SIZE); + p = dplane_ctx_get_intf_addr(ctx); + memset(&req, 0, sizeof(req) - NL_PKT_BUF_SIZE); - bytelen = (family == AF_INET ? 4 : 16); + bytelen = (p->family == AF_INET ? 4 : 16); req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)); req.n.nlmsg_flags = NLM_F_REQUEST; - req.n.nlmsg_type = cmd; - req.n.nlmsg_pid = zns->netlink_cmd.snl.nl_pid; - req.ifa.ifa_family = family; + if (dplane_ctx_get_op(ctx) == DPLANE_OP_ADDR_INSTALL) + cmd = RTM_NEWADDR; + else + cmd = RTM_DELADDR; + + req.n.nlmsg_type = cmd; + req.ifa.ifa_family = p->family; - req.ifa.ifa_index = ifp->ifindex; + req.ifa.ifa_index = dplane_ctx_get_ifindex(ctx); - addattr_l(&req.n, sizeof req, IFA_LOCAL, &p->u.prefix, bytelen); + addattr_l(&req.n, sizeof(req), IFA_LOCAL, &p->u.prefix, bytelen); - if (family == AF_INET) { - if (CONNECTED_PEER(ifc)) { - p = ifc->destination; - addattr_l(&req.n, sizeof req, IFA_ADDRESS, &p->u.prefix, - bytelen); - } else if (cmd == RTM_NEWADDR && ifc->destination) { - p = ifc->destination; - addattr_l(&req.n, sizeof req, IFA_BROADCAST, + if (p->family == AF_INET) { + if (dplane_ctx_intf_is_connected(ctx)) { + p = dplane_ctx_get_intf_dest(ctx); + addattr_l(&req.n, sizeof(req), IFA_ADDRESS, + &p->u.prefix, bytelen); + } else if (cmd == RTM_NEWADDR && + dplane_ctx_intf_has_dest(ctx)) { + p = dplane_ctx_get_intf_dest(ctx); + addattr_l(&req.n, sizeof(req), IFA_BROADCAST, &p->u.prefix, bytelen); } } - /* p is now either ifc->address or ifc->destination */ + /* p is now either address or destination/bcast addr */ req.ifa.ifa_prefixlen = p->prefixlen; - if (CHECK_FLAG(ifc->flags, ZEBRA_IFA_SECONDARY)) + if (dplane_ctx_intf_is_secondary(ctx)) SET_FLAG(req.ifa.ifa_flags, IFA_F_SECONDARY); - if (ifc->label) - addattr_l(&req.n, sizeof req, IFA_LABEL, ifc->label, - strlen(ifc->label) + 1); - - return netlink_talk(netlink_talk_filter, &req.n, &zns->netlink_cmd, zns, - 0); -} - -int kernel_address_add_ipv4(struct interface *ifp, struct connected *ifc) -{ - return netlink_address(RTM_NEWADDR, AF_INET, ifp, ifc); -} - -int kernel_address_delete_ipv4(struct interface *ifp, struct connected *ifc) -{ - return netlink_address(RTM_DELADDR, AF_INET, ifp, ifc); -} + if (dplane_ctx_intf_has_label(ctx)) { + label = dplane_ctx_get_intf_label(ctx); + addattr_l(&req.n, sizeof(req), IFA_LABEL, label, + strlen(label) + 1); + } -int kernel_address_add_ipv6(struct interface *ifp, struct connected *ifc) -{ - return netlink_address(RTM_NEWADDR, AF_INET6, ifp, ifc); + return netlink_talk_info(netlink_talk_filter, &req.n, + dplane_ctx_get_ns(ctx), 0); } -int kernel_address_delete_ipv6(struct interface *ifp, struct connected *ifc) +enum zebra_dplane_result kernel_address_update_ctx(struct zebra_dplane_ctx *ctx) { - return netlink_address(RTM_DELADDR, AF_INET6, ifp, ifc); + return (netlink_address_ctx(ctx) == 0 ? + ZEBRA_DPLANE_REQUEST_SUCCESS : ZEBRA_DPLANE_REQUEST_FAILURE); } int netlink_interface_addr(struct nlmsghdr *h, ns_id_t ns_id, int startup) diff --git a/zebra/interface.c b/zebra/interface.c index 229f9c1da..10f1f9210 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -441,7 +441,7 @@ static void if_addr_wakeup(struct interface *ifp) struct listnode *node, *nnode; struct connected *ifc; struct prefix *p; - int ret; + enum zebra_dplane_result dplane_res; for (ALL_LIST_ELEMENTS(ifp->connected, node, nnode, ifc)) { p = ifc->address; @@ -479,12 +479,13 @@ static void if_addr_wakeup(struct interface *ifp) if_refresh(ifp); } - ret = if_set_prefix(ifp, ifc); - if (ret < 0) { + dplane_res = dplane_intf_addr_set(ifp, ifc); + if (dplane_res == + ZEBRA_DPLANE_REQUEST_FAILURE) { flog_err_sys( EC_ZEBRA_IFACE_ADDR_ADD_FAILED, "Can't set interface's address: %s", - safe_strerror(errno)); + dplane_res2str(dplane_res)); continue; } @@ -502,12 +503,14 @@ static void if_addr_wakeup(struct interface *ifp) if_refresh(ifp); } - ret = if_prefix_add_ipv6(ifp, ifc); - if (ret < 0) { + + dplane_res = dplane_intf_addr_set(ifp, ifc); + if (dplane_res == + ZEBRA_DPLANE_REQUEST_FAILURE) { flog_err_sys( EC_ZEBRA_IFACE_ADDR_ADD_FAILED, "Can't set interface's address: %s", - safe_strerror(errno)); + dplane_res2str(dplane_res)); continue; } @@ -1369,8 +1372,11 @@ static void if_dump_vty(struct vty *vty, struct interface *ifp) vty_out(vty, " VTEP IP: %s", inet_ntoa(vxlan_info->vtep_ip)); if (vxlan_info->access_vlan) - vty_out(vty, " Access VLAN Id %u", + vty_out(vty, " Access VLAN Id %u\n", vxlan_info->access_vlan); + if (vxlan_info->mcast_grp.s_addr != INADDR_ANY) + vty_out(vty, " Mcast Group %s", + inet_ntoa(vxlan_info->mcast_grp)); vty_out(vty, "\n"); } @@ -2626,6 +2632,7 @@ static int ip_address_install(struct vty *vty, struct interface *ifp, struct connected *ifc; struct prefix_ipv4 *p; int ret; + enum zebra_dplane_result dplane_res; if_data = ifp->info; @@ -2699,10 +2706,10 @@ static int ip_address_install(struct vty *vty, struct interface *ifp, if_refresh(ifp); } - ret = if_set_prefix(ifp, ifc); - if (ret < 0) { + dplane_res = dplane_intf_addr_set(ifp, ifc); + if (dplane_res == ZEBRA_DPLANE_REQUEST_FAILURE) { vty_out(vty, "%% Can't set interface IP address: %s.\n", - safe_strerror(errno)); + dplane_res2str(dplane_res)); return CMD_WARNING_CONFIG_FAILED; } @@ -2723,6 +2730,7 @@ static int ip_address_uninstall(struct vty *vty, struct interface *ifp, struct prefix_ipv4 lp, pp; struct connected *ifc; int ret; + enum zebra_dplane_result dplane_res; /* Convert to prefix structure. */ ret = str2prefix_ipv4(addr_str, &lp); @@ -2767,10 +2775,10 @@ static int ip_address_uninstall(struct vty *vty, struct interface *ifp, } /* This is real route. */ - ret = if_unset_prefix(ifp, ifc); - if (ret < 0) { + dplane_res = dplane_intf_addr_unset(ifp, ifc); + if (dplane_res == ZEBRA_DPLANE_REQUEST_FAILURE) { vty_out(vty, "%% Can't unset interface IP address: %s.\n", - safe_strerror(errno)); + dplane_res2str(dplane_res)); return CMD_WARNING_CONFIG_FAILED; } UNSET_FLAG(ifc->conf, ZEBRA_IFC_QUEUED); @@ -2877,6 +2885,7 @@ static int ipv6_address_install(struct vty *vty, struct interface *ifp, struct connected *ifc; struct prefix_ipv6 *p; int ret; + enum zebra_dplane_result dplane_res; if_data = ifp->info; @@ -2923,11 +2932,10 @@ static int ipv6_address_install(struct vty *vty, struct interface *ifp, if_refresh(ifp); } - ret = if_prefix_add_ipv6(ifp, ifc); - - if (ret < 0) { + dplane_res = dplane_intf_addr_set(ifp, ifc); + if (dplane_res == ZEBRA_DPLANE_REQUEST_FAILURE) { vty_out(vty, "%% Can't set interface IP address: %s.\n", - safe_strerror(errno)); + dplane_res2str(dplane_res)); return CMD_WARNING_CONFIG_FAILED; } @@ -2961,6 +2969,7 @@ static int ipv6_address_uninstall(struct vty *vty, struct interface *ifp, struct prefix_ipv6 cp; struct connected *ifc; int ret; + enum zebra_dplane_result dplane_res; /* Convert to prefix structure. */ ret = str2prefix_ipv6(addr_str, &cp); @@ -2991,10 +3000,10 @@ static int ipv6_address_uninstall(struct vty *vty, struct interface *ifp, } /* This is real route. */ - ret = if_prefix_delete_ipv6(ifp, ifc); - if (ret < 0) { + dplane_res = dplane_intf_addr_unset(ifp, ifc); + if (dplane_res == ZEBRA_DPLANE_REQUEST_FAILURE) { vty_out(vty, "%% Can't unset interface IP address: %s.\n", - safe_strerror(errno)); + dplane_res2str(dplane_res)); return CMD_WARNING_CONFIG_FAILED; } diff --git a/zebra/ioctl.c b/zebra/ioctl.c index 9499c731e..322527015 100644 --- a/zebra/ioctl.c +++ b/zebra/ioctl.c @@ -34,6 +34,7 @@ #include "zebra/rt.h" #include "zebra/interface.h" #include "zebra/zebra_errors.h" +#include "zebra/debug.h" #ifndef SUNOS_5 @@ -180,40 +181,72 @@ void if_get_mtu(struct interface *ifp) #endif } -#ifdef HAVE_NETLINK -/* Interface address setting via netlink interface. */ -int if_set_prefix(struct interface *ifp, struct connected *ifc) -{ - return kernel_address_add_ipv4(ifp, ifc); -} +/* + * Handler for interface address programming via the zebra dplane, + * for non-netlink platforms. This handler dispatches to per-platform + * helpers, based on the operation requested. + */ +#ifndef HAVE_NETLINK -/* Interface address is removed using netlink interface. */ -int if_unset_prefix(struct interface *ifp, struct connected *ifc) +/* Prototypes: these are placed in this block so that they're only seen + * on non-netlink platforms. + */ +static int if_set_prefix_ctx(const struct zebra_dplane_ctx *ctx); +static int if_unset_prefix_ctx(const struct zebra_dplane_ctx *ctx); +static int if_set_prefix6_ctx(const struct zebra_dplane_ctx *ctx); +static int if_unset_prefix6_ctx(const struct zebra_dplane_ctx *ctx); + +enum zebra_dplane_result kernel_address_update_ctx( + struct zebra_dplane_ctx *ctx) { - return kernel_address_delete_ipv4(ifp, ifc); + int ret = -1; + const struct prefix *p; + + p = dplane_ctx_get_intf_addr(ctx); + + if (dplane_ctx_get_op(ctx) == DPLANE_OP_ADDR_INSTALL) { + if (p->family == AF_INET) + ret = if_set_prefix_ctx(ctx); + else + ret = if_set_prefix6_ctx(ctx); + } else if (dplane_ctx_get_op(ctx) == DPLANE_OP_ADDR_UNINSTALL) { + if (p->family == AF_INET) + ret = if_unset_prefix_ctx(ctx); + else + ret = if_unset_prefix6_ctx(ctx); + } else { + if (IS_ZEBRA_DEBUG_DPLANE) + zlog_debug("Invalid op in interface-addr install"); + } + + return (ret == 0 ? + ZEBRA_DPLANE_REQUEST_SUCCESS : ZEBRA_DPLANE_REQUEST_FAILURE); } + +#endif /* !HAVE_NETLINK */ + +#ifdef HAVE_NETLINK + +/* TODO -- remove; no use of these apis with netlink any longer */ + #else /* ! HAVE_NETLINK */ #ifdef HAVE_STRUCT_IFALIASREQ -/* Set up interface's IP address, netmask (and broadcas? ). *BSD may - has ifaliasreq structure. */ -int if_set_prefix(struct interface *ifp, struct connected *ifc) + +/* + * Helper for interface-addr install, non-netlink + */ +static int if_set_prefix_ctx(const struct zebra_dplane_ctx *ctx) { int ret; struct ifaliasreq addreq; struct sockaddr_in addr, mask, peer; struct prefix_ipv4 *p; - /* don't configure PtP addresses on broadcast ifs or reverse */ - if (!(ifp->flags & IFF_POINTOPOINT) != !CONNECTED_PEER(ifc)) { - errno = EINVAL; - return -1; - } + p = (struct prefix_ipv4 *)dplane_ctx_get_intf_addr(ctx); - p = (struct prefix_ipv4 *)ifc->address; - rib_lookup_and_pushup(p, ifp->vrf_id); - - memset(&addreq, 0, sizeof addreq); - strlcpy(addreq.ifra_name, ifp->name, sizeof(addreq.ifra_name)); + memset(&addreq, 0, sizeof(addreq)); + strncpy((char *)&addreq.ifra_name, dplane_ctx_get_ifname(ctx), + sizeof(addreq.ifra_name)); memset(&addr, 0, sizeof(struct sockaddr_in)); addr.sin_addr = p->prefix; @@ -223,8 +256,8 @@ int if_set_prefix(struct interface *ifp, struct connected *ifc) #endif memcpy(&addreq.ifra_addr, &addr, sizeof(struct sockaddr_in)); - if (CONNECTED_PEER(ifc)) { - p = (struct prefix_ipv4 *)ifc->destination; + if (dplane_ctx_intf_is_connected(ctx)) { + p = (struct prefix_ipv4 *)dplane_ctx_get_intf_dest(ctx); memset(&mask, 0, sizeof(struct sockaddr_in)); peer.sin_addr = p->prefix; peer.sin_family = p->family; @@ -247,27 +280,24 @@ int if_set_prefix(struct interface *ifp, struct connected *ifc) if (ret < 0) return ret; return 0; + } -/* Set up interface's IP address, netmask (and broadcas? ). *BSD may - has ifaliasreq structure. */ -int if_unset_prefix(struct interface *ifp, struct connected *ifc) +/* + * Helper for interface-addr un-install, non-netlink + */ +static int if_unset_prefix_ctx(const struct zebra_dplane_ctx *ctx) { int ret; struct ifaliasreq addreq; struct sockaddr_in addr, mask, peer; struct prefix_ipv4 *p; - /* this would probably wreak havoc */ - if (!(ifp->flags & IFF_POINTOPOINT) != !CONNECTED_PEER(ifc)) { - errno = EINVAL; - return -1; - } + p = (struct prefix_ipv4 *)dplane_ctx_get_intf_addr(ctx); - p = (struct prefix_ipv4 *)ifc->address; - - memset(&addreq, 0, sizeof addreq); - strlcpy(addreq.ifra_name, ifp->name, sizeof(addreq.ifra_name)); + memset(&addreq, 0, sizeof(addreq)); + strncpy((char *)&addreq.ifra_name, dplane_ctx_get_ifname(ctx), + sizeof(addreq.ifra_name)); memset(&addr, 0, sizeof(struct sockaddr_in)); addr.sin_addr = p->prefix; @@ -277,8 +307,8 @@ int if_unset_prefix(struct interface *ifp, struct connected *ifc) #endif memcpy(&addreq.ifra_addr, &addr, sizeof(struct sockaddr_in)); - if (CONNECTED_PEER(ifc)) { - p = (struct prefix_ipv4 *)ifc->destination; + if (dplane_ctx_intf_is_connected(ctx)) { + p = (struct prefix_ipv4 *)dplane_ctx_get_intf_dest(ctx); memset(&mask, 0, sizeof(struct sockaddr_in)); peer.sin_addr = p->prefix; peer.sin_family = p->family; @@ -305,7 +335,7 @@ int if_unset_prefix(struct interface *ifp, struct connected *ifc) #else /* Set up interface's address, netmask (and broadcas? ). Linux or Solaris uses ifname:number semantics to set IP address aliases. */ -int if_set_prefix(struct interface *ifp, struct connected *ifc) +int if_set_prefix_ctx(const struct zebra_dplane_ctx *ctx) { int ret; struct ifreq ifreq; @@ -315,11 +345,12 @@ int if_set_prefix(struct interface *ifp, struct connected *ifc) struct prefix_ipv4 ifaddr; struct prefix_ipv4 *p; - p = (struct prefix_ipv4 *)ifc->address; + p = (struct prefix_ipv4 *)dplane_ctx_get_intf_addr(ctx); ifaddr = *p; - ifreq_set_name(&ifreq, ifp); + strlcpy(ifreq.ifr_name, dplane_ctx_get_ifname(ctx), + sizeof(ifreq.ifr_name)); addr.sin_addr = p->prefix; addr.sin_family = p->family; @@ -331,7 +362,7 @@ int if_set_prefix(struct interface *ifp, struct connected *ifc) /* We need mask for make broadcast addr. */ masklen2ip(p->prefixlen, &mask.sin_addr); - if (if_is_broadcast(ifp)) { + if (dplane_ctx_intf_is_broadcast(ctx)) { apply_mask_ipv4(&ifaddr); addr.sin_addr = ifaddr.prefix; @@ -350,7 +381,7 @@ int if_set_prefix(struct interface *ifp, struct connected *ifc) #ifdef SUNOS_5 memcpy(&mask, &ifreq.ifr_addr, sizeof(mask)); #else - memcpy(&ifreq.ifr_netmask, &mask, sizeof(struct sockaddr_in)); + memcpy(&ifreq.ifr_addr, &mask, sizeof(struct sockaddr_in)); #endif /* SUNOS5 */ ret = if_ioctl(SIOCSIFNETMASK, (caddr_t)&ifreq); if (ret < 0) @@ -361,16 +392,17 @@ int if_set_prefix(struct interface *ifp, struct connected *ifc) /* Set up interface's address, netmask (and broadcas? ). Linux or Solaris uses ifname:number semantics to set IP address aliases. */ -int if_unset_prefix(struct interface *ifp, struct connected *ifc) +int if_unset_prefix_ctx(const struct zebra_dplane_ctx *ctx) { int ret; struct ifreq ifreq; struct sockaddr_in addr; struct prefix_ipv4 *p; - p = (struct prefix_ipv4 *)ifc->address; + p = (struct prefix_ipv4 *)dplane_ctx_get_intf_addr(ctx); - ifreq_set_name(&ifreq, ifp); + strlcpy(ifreq.ifr_name, dplane_ctx_get_ifname(ctx), + sizeof(ifreq.ifr_name)); memset(&addr, 0, sizeof(struct sockaddr_in)); addr.sin_family = p->family; @@ -475,35 +507,17 @@ int if_unset_flags(struct interface *ifp, uint64_t flags) return 0; } -#ifdef LINUX_IPV6 -#ifndef _LINUX_IN6_H -/* linux/include/net/ipv6.h */ -struct in6_ifreq { - struct in6_addr ifr6_addr; - uint32_t ifr6_prefixlen; - int ifr6_ifindex; -}; -#endif /* _LINUX_IN6_H */ -/* Interface's address add/delete functions. */ -int if_prefix_add_ipv6(struct interface *ifp, struct connected *ifc) -{ -#ifdef HAVE_NETLINK - return kernel_address_add_ipv6(ifp, ifc); -#endif /* HAVE_NETLINK */ -} +#ifndef LINUX_IPV6 /* Netlink has its own code */ -int if_prefix_delete_ipv6(struct interface *ifp, struct connected *ifc) -{ -#ifdef HAVE_NETLINK - return kernel_address_delete_ipv6(ifp, ifc); -#endif /* HAVE_NETLINK */ -} -#else /* LINUX_IPV6 */ #ifdef HAVE_STRUCT_IN6_ALIASREQ #ifndef ND6_INFINITE_LIFETIME #define ND6_INFINITE_LIFETIME 0xffffffffL #endif /* ND6_INFINITE_LIFETIME */ -int if_prefix_add_ipv6(struct interface *ifp, struct connected *ifc) + +/* + * Helper for interface-addr install, non-netlink + */ +static int if_set_prefix6_ctx(const struct zebra_dplane_ctx *ctx) { int ret; struct in6_aliasreq addreq; @@ -511,10 +525,11 @@ int if_prefix_add_ipv6(struct interface *ifp, struct connected *ifc) struct sockaddr_in6 mask; struct prefix_ipv6 *p; - p = (struct prefix_ipv6 *)ifc->address; + p = (struct prefix_ipv6 *)dplane_ctx_get_intf_addr(ctx); - memset(&addreq, 0, sizeof addreq); - strlcpy(addreq.ifra_name, ifp->name, sizeof(addreq.ifra_name)); + memset(&addreq, 0, sizeof(addreq)); + strlcpy((char *)&addreq.ifra_name, + dplane_ctx_get_ifname(ctx), sizeof(addreq.ifra_name)); memset(&addr, 0, sizeof(struct sockaddr_in6)); addr.sin6_addr = p->prefix; @@ -546,7 +561,10 @@ int if_prefix_add_ipv6(struct interface *ifp, struct connected *ifc) return 0; } -int if_prefix_delete_ipv6(struct interface *ifp, struct connected *ifc) +/* + * Helper for interface-addr un-install, non-netlink + */ +static int if_unset_prefix6_ctx(const struct zebra_dplane_ctx *ctx) { int ret; struct in6_aliasreq addreq; @@ -554,10 +572,11 @@ int if_prefix_delete_ipv6(struct interface *ifp, struct connected *ifc) struct sockaddr_in6 mask; struct prefix_ipv6 *p; - p = (struct prefix_ipv6 *)ifc->address; + p = (struct prefix_ipv6 *)dplane_ctx_get_intf_addr(ctx); - memset(&addreq, 0, sizeof addreq); - strlcpy(addreq.ifra_name, ifp->name, sizeof(addreq.ifra_name)); + memset(&addreq, 0, sizeof(addreq)); + strlcpy((char *)&addreq.ifra_name, + dplane_ctx_get_ifname(ctx), sizeof(addreq.ifra_name)); memset(&addr, 0, sizeof(struct sockaddr_in6)); addr.sin6_addr = p->prefix; @@ -586,12 +605,15 @@ int if_prefix_delete_ipv6(struct interface *ifp, struct connected *ifc) return 0; } #else -int if_prefix_add_ipv6(struct interface *ifp, struct connected *ifc) +/* The old, pre-dataplane code here just returned, so we're retaining that + * choice. + */ +static int if_set_prefix6_ctx(const struct zebra_dplane_ctx *ctx) { return 0; } -int if_prefix_delete_ipv6(struct interface *ifp, struct connected *ifc) +static int if_unset_prefix6_ctx(const struct zebra_dplane_ctx *ctx) { return 0; } diff --git a/zebra/ioctl.h b/zebra/ioctl.h index 67ffd45a0..03f3911d5 100644 --- a/zebra/ioctl.h +++ b/zebra/ioctl.h @@ -35,15 +35,9 @@ extern int if_set_flags(struct interface *, uint64_t); extern int if_unset_flags(struct interface *, uint64_t); extern void if_get_flags(struct interface *); -extern int if_set_prefix(struct interface *, struct connected *); -extern int if_unset_prefix(struct interface *, struct connected *); - extern void if_get_metric(struct interface *); extern void if_get_mtu(struct interface *); -extern int if_prefix_add_ipv6(struct interface *, struct connected *); -extern int if_prefix_delete_ipv6(struct interface *, struct connected *); - #ifdef SOLARIS_IPV6 extern int if_ioctl_ipv6(unsigned long, caddr_t); extern struct connected *if_lookup_linklocal(struct interface *); diff --git a/zebra/ioctl_solaris.c b/zebra/ioctl_solaris.c index c523ee983..ccfa7a4a4 100644 --- a/zebra/ioctl_solaris.c +++ b/zebra/ioctl_solaris.c @@ -38,9 +38,16 @@ #include "zebra/interface.h" #include "zebra/ioctl_solaris.h" #include "zebra/zebra_errors.h" +#include "zebra/debug.h" extern struct zebra_privs_t zserv_privs; +/* Prototypes */ +static int if_set_prefix_ctx(const struct zebra_dplane_ctx *ctx); +static int if_unset_prefix_ctx(const struct zebra_dplane_ctx *ctx); +static int if_set_prefix6_ctx(const struct zebra_dplane_ctx *ctx); +static int if_unset_prefix6_ctx(const struct zebra_dplane_ctx *ctx); + /* clear and set interface name string */ void lifreq_set_name(struct lifreq *lifreq, const char *ifname) { @@ -183,23 +190,52 @@ void if_get_mtu(struct interface *ifp) zebra_interface_up_update(ifp); } +/* + * + */ +enum zebra_dplane_result kernel_address_update_ctx( + struct zebra_dplane_ctx *ctx) +{ + int ret = -1; + const struct prefix *p; + + p = dplane_ctx_get_intf_addr(ctx); + + if (dplane_ctx_get_op(ctx) == DPLANE_OP_ADDR_INSTALL) { + if (p->family == AF_INET) + ret = if_set_prefix_ctx(ctx); + else + ret = if_set_prefix6_ctx(ctx); + } else if (dplane_ctx_get_op(ctx) == DPLANE_OP_ADDR_UNINSTALL) { + if (p->family == AF_INET) + ret = if_unset_prefix_ctx(ctx); + else + ret = if_unset_prefix6_ctx(ctx); + } else { + if (IS_ZEBRA_DEBUG_DPLANE) + zlog_debug("Invalid op in interface-addr install"); + } + + return (ret == 0 ? + ZEBRA_DPLANE_REQUEST_SUCCESS : ZEBRA_DPLANE_REQUEST_FAILURE); +} + /* Set up interface's address, netmask (and broadcast? ). Solaris uses ifname:number semantics to set IP address aliases. */ -int if_set_prefix(struct interface *ifp, struct connected *ifc) +static int if_set_prefix_ctx(const struct zebra_dplane_ctx *ctx) { int ret; struct ifreq ifreq; - struct sockaddr_in addr; - struct sockaddr_in broad; - struct sockaddr_in mask; + struct sockaddr_in addr, broad, mask; struct prefix_ipv4 ifaddr; struct prefix_ipv4 *p; - p = (struct prefix_ipv4 *)ifc->address; + p = (struct prefix_ipv4 *)dplane_ctx_get_intf_addr(ctx); ifaddr = *p; - strlcpy(ifreq.ifr_name, ifp->name, sizeof(ifreq.ifr_name)); + strlcpy(ifreq.ifr_name, dplane_ctx_get_ifname(ctx), + sizeof(ifreq.ifr_name)); addr.sin_addr = p->prefix; addr.sin_family = p->family; @@ -213,7 +249,7 @@ int if_set_prefix(struct interface *ifp, struct connected *ifc) /* We need mask for make broadcast addr. */ masklen2ip(p->prefixlen, &mask.sin_addr); - if (if_is_broadcast(ifp)) { + if (dplane_ctx_intf_is_broadcast(ctx)) { apply_mask_ipv4(&ifaddr); addr.sin_addr = ifaddr.prefix; @@ -241,16 +277,17 @@ int if_set_prefix(struct interface *ifp, struct connected *ifc) /* Set up interface's address, netmask (and broadcast). Solaris uses ifname:number semantics to set IP address aliases. */ -int if_unset_prefix(struct interface *ifp, struct connected *ifc) +static int if_unset_prefix_ctx(const struct zebra_dplane_ctx *ctx) { int ret; struct ifreq ifreq; struct sockaddr_in addr; struct prefix_ipv4 *p; - p = (struct prefix_ipv4 *)ifc->address; + p = (struct prefix_ipv4 *)dplane_ctx_get_intf_addr(ctx); - strlcpy(ifreq.ifr_name, ifp->name, sizeof(ifreq.ifr_name)); + strncpy(ifreq.ifr_name, dplane_ctx_get_ifname(ctx), + sizeof(ifreq.ifr_name)); memset(&addr, 0, sizeof(struct sockaddr_in)); addr.sin_family = p->family; @@ -377,24 +414,26 @@ int if_unset_flags(struct interface *ifp, uint64_t flags) } /* Interface's address add/delete functions. */ -int if_prefix_add_ipv6(struct interface *ifp, struct connected *ifc) +static int if_set_prefix6_ctx(const struct zebra_dplane_ctx *ctx) { char addrbuf[PREFIX_STRLEN]; + prefix2str(dplane_ctx_get_intf_addr(ctx), addrbuf, sizeof(addrbuf)); + flog_warn(EC_LIB_DEVELOPMENT, "Can't set %s on interface %s", - prefix2str(ifc->address, addrbuf, sizeof(addrbuf)), - ifp->name); + addrbuf, dplane_ctx_get_ifname(ctx)); return 0; } -int if_prefix_delete_ipv6(struct interface *ifp, struct connected *ifc) +static int if_unset_prefix6_ctx(const struct zebra_dplane_ctx *ctx) { char addrbuf[PREFIX_STRLEN]; + prefix2str(dplane_ctx_get_intf_addr(ctx), addrbuf, sizeof(addrbuf)); + flog_warn(EC_LIB_DEVELOPMENT, "Can't delete %s on interface %s", - prefix2str(ifc->address, addrbuf, sizeof(addrbuf)), - ifp->name); + addrbuf, dplane_ctx_get_ifname(ctx)); return 0; } diff --git a/zebra/rt.h b/zebra/rt.h index 2c77af2aa..08b51fcc0 100644 --- a/zebra/rt.h +++ b/zebra/rt.h @@ -50,10 +50,9 @@ extern enum zebra_dplane_result kernel_lsp_update( enum zebra_dplane_result kernel_pw_update(struct zebra_dplane_ctx *ctx); -extern int kernel_address_add_ipv4(struct interface *, struct connected *); -extern int kernel_address_delete_ipv4(struct interface *, struct connected *); -extern int kernel_address_add_ipv6(struct interface *, struct connected *); -extern int kernel_address_delete_ipv6(struct interface *, struct connected *); +enum zebra_dplane_result kernel_address_update_ctx( + struct zebra_dplane_ctx *ctx); + extern int kernel_neigh_update(int cmd, int ifindex, uint32_t addr, char *lla, int llalen, ns_id_t ns_id); extern int kernel_interface_set_master(struct interface *master, diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c index af54c3b5c..d1b28227c 100644 --- a/zebra/zebra_dplane.c +++ b/zebra/zebra_dplane.c @@ -122,6 +122,33 @@ struct dplane_pw_info { }; /* + * Interface/prefix info for the dataplane + */ +struct dplane_intf_info { + + char ifname[INTERFACE_NAMSIZ]; + ifindex_t ifindex; + + uint32_t metric; + uint32_t flags; + +#define DPLANE_INTF_CONNECTED (1 << 0) /* Connected peer, p2p */ +#define DPLANE_INTF_SECONDARY (1 << 1) +#define DPLANE_INTF_BROADCAST (1 << 2) +#define DPLANE_INTF_HAS_DEST (1 << 3) +#define DPLANE_INTF_HAS_LABEL (1 << 4) + + /* Interface address/prefix */ + struct prefix prefix; + + /* Dest address, for p2p, or broadcast prefix */ + struct prefix dest_prefix; + + char *label; + char label_buf[32]; +}; + +/* * The context block used to exchange info about route updates across * the boundary between the zebra main context (and pthread) and the * dataplane layer (and pthread). @@ -152,11 +179,12 @@ struct zebra_dplane_ctx { vrf_id_t zd_vrf_id; uint32_t zd_table_id; - /* Support info for either route or LSP update */ + /* Support info for different kinds of updates */ union { struct dplane_route_info rinfo; zebra_lsp_t lsp; struct dplane_pw_info pw; + struct dplane_intf_info intf; } u; /* Namespace info, used especially for netlink kernel communication */ @@ -266,6 +294,9 @@ static struct zebra_dplane_globals { _Atomic uint32_t dg_pws_in; _Atomic uint32_t dg_pw_errors; + _Atomic uint32_t dg_intf_addrs_in; + _Atomic uint32_t dg_intf_addr_errors; + _Atomic uint32_t dg_update_yields; /* Dataplane pthread */ @@ -303,6 +334,9 @@ static enum zebra_dplane_result lsp_update_internal(zebra_lsp_t *lsp, enum dplane_op_e op); static enum zebra_dplane_result pw_update_internal(struct zebra_pw *pw, enum dplane_op_e op); +static enum zebra_dplane_result intf_addr_update_internal( + const struct interface *ifp, const struct connected *ifc, + enum dplane_op_e op); /* * Public APIs @@ -409,6 +443,16 @@ static void dplane_ctx_free(struct zebra_dplane_ctx **pctx) } break; + case DPLANE_OP_ADDR_INSTALL: + case DPLANE_OP_ADDR_UNINSTALL: + /* Maybe free label string, if allocated */ + if ((*pctx)->u.intf.label != NULL && + (*pctx)->u.intf.label != (*pctx)->u.intf.label_buf) { + free((*pctx)->u.intf.label); + (*pctx)->u.intf.label = NULL; + } + break; + case DPLANE_OP_NONE: break; } @@ -549,6 +593,14 @@ const char *dplane_op2str(enum dplane_op_e op) case DPLANE_OP_SYS_ROUTE_DELETE: ret = "SYS_ROUTE_DEL"; break; + + case DPLANE_OP_ADDR_INSTALL: + ret = "ADDR_INSTALL"; + break; + case DPLANE_OP_ADDR_UNINSTALL: + ret = "ADDR_UNINSTALL"; + break; + } return ret; @@ -868,6 +920,90 @@ dplane_ctx_get_pw_nhg(const struct zebra_dplane_ctx *ctx) return &(ctx->u.pw.nhg); } +/* Accessors for interface information */ +const char *dplane_ctx_get_ifname(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.intf.ifname; +} + +ifindex_t dplane_ctx_get_ifindex(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.intf.ifindex; +} + +uint32_t dplane_ctx_get_intf_metric(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.intf.metric; +} + +/* Is interface addr p2p? */ +bool dplane_ctx_intf_is_connected(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return (ctx->u.intf.flags & DPLANE_INTF_CONNECTED); +} + +bool dplane_ctx_intf_is_secondary(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return (ctx->u.intf.flags & DPLANE_INTF_SECONDARY); +} + +bool dplane_ctx_intf_is_broadcast(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return (ctx->u.intf.flags & DPLANE_INTF_BROADCAST); +} + +const struct prefix *dplane_ctx_get_intf_addr( + const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return &(ctx->u.intf.prefix); +} + +bool dplane_ctx_intf_has_dest(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return (ctx->u.intf.flags & DPLANE_INTF_HAS_DEST); +} + +const struct prefix *dplane_ctx_get_intf_dest( + const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + if (ctx->u.intf.flags & DPLANE_INTF_HAS_DEST) + return &(ctx->u.intf.dest_prefix); + else + return NULL; +} + +bool dplane_ctx_intf_has_label(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return (ctx->u.intf.flags & DPLANE_INTF_HAS_LABEL); +} + +const char *dplane_ctx_get_intf_label(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.intf.label; +} + /* * End of dplane context accessors */ @@ -1494,6 +1630,140 @@ done: } /* + * Enqueue interface address add for the dataplane. + */ +enum zebra_dplane_result dplane_intf_addr_set(const struct interface *ifp, + const struct connected *ifc) +{ +#if !defined(HAVE_NETLINK) && defined(HAVE_STRUCT_IFALIASREQ) + /* Extra checks for this OS path. */ + + /* Don't configure PtP addresses on broadcast ifs or reverse */ + if (!(ifp->flags & IFF_POINTOPOINT) != !CONNECTED_PEER(ifc)) { + if (IS_ZEBRA_DEBUG_KERNEL || IS_ZEBRA_DEBUG_DPLANE) + zlog_debug("Failed to set intf addr: mismatch p2p and connected"); + + return ZEBRA_DPLANE_REQUEST_FAILURE; + } + + /* Ensure that no existing installed v4 route conflicts with + * the new interface prefix. This check must be done in the + * zebra pthread context, and any route delete (if needed) + * is enqueued before the interface address programming attempt. + */ + if (ifc->address->family == AF_INET) { + struct prefix_ipv4 *p; + + p = (struct prefix_ipv4 *)ifc->address; + rib_lookup_and_pushup(p, ifp->vrf_id); + } +#endif + + return intf_addr_update_internal(ifp, ifc, DPLANE_OP_ADDR_INSTALL); +} + +/* + * Enqueue interface address remove/uninstall for the dataplane. + */ +enum zebra_dplane_result dplane_intf_addr_unset(const struct interface *ifp, + const struct connected *ifc) +{ + return intf_addr_update_internal(ifp, ifc, DPLANE_OP_ADDR_UNINSTALL); +} + +static enum zebra_dplane_result intf_addr_update_internal( + const struct interface *ifp, const struct connected *ifc, + enum dplane_op_e op) +{ + enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE; + int ret = EINVAL; + struct zebra_dplane_ctx *ctx = NULL; + struct zebra_ns *zns; + + if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) { + char addr_str[PREFIX_STRLEN]; + + prefix2str(ifc->address, addr_str, sizeof(addr_str)); + + zlog_debug("init intf ctx %s: idx %d, addr %u:%s", + dplane_op2str(op), ifp->ifindex, ifp->vrf_id, + addr_str); + } + + ctx = dplane_ctx_alloc(); + if (ctx == NULL) { + ret = ENOMEM; + goto done; + } + + ctx->zd_op = op; + ctx->zd_status = ZEBRA_DPLANE_REQUEST_SUCCESS; + ctx->zd_vrf_id = ifp->vrf_id; + + zns = zebra_ns_lookup(ifp->vrf_id); + dplane_ctx_ns_init(ctx, zns, false); + + /* Init the interface-addr-specific area */ + memset(&ctx->u.intf, 0, sizeof(ctx->u.intf)); + + strncpy(ctx->u.intf.ifname, ifp->name, sizeof(ctx->u.intf.ifname)); + ctx->u.intf.ifindex = ifp->ifindex; + ctx->u.intf.prefix = *(ifc->address); + + if (if_is_broadcast(ifp)) + ctx->u.intf.flags |= DPLANE_INTF_BROADCAST; + + if (CONNECTED_PEER(ifc)) { + ctx->u.intf.dest_prefix = *(ifc->destination); + ctx->u.intf.flags |= + (DPLANE_INTF_CONNECTED | DPLANE_INTF_HAS_DEST); + } else if (ifc->destination) { + ctx->u.intf.dest_prefix = *(ifc->destination); + ctx->u.intf.flags |= DPLANE_INTF_HAS_DEST; + } + + if (CHECK_FLAG(ifc->flags, ZEBRA_IFA_SECONDARY)) + ctx->u.intf.flags |= DPLANE_INTF_SECONDARY; + + if (ifc->label) { + size_t len; + + ctx->u.intf.flags |= DPLANE_INTF_HAS_LABEL; + + /* Use embedded buffer if it's adequate; else allocate. */ + len = strlen(ifc->label); + + if (len < sizeof(ctx->u.intf.label_buf)) { + strncpy(ctx->u.intf.label_buf, ifc->label, + sizeof(ctx->u.intf.label_buf)); + ctx->u.intf.label = ctx->u.intf.label_buf; + } else { + ctx->u.intf.label = strdup(ifc->label); + } + } + + ret = dplane_route_enqueue(ctx); + +done: + + /* Increment counter */ + atomic_fetch_add_explicit(&zdplane_info.dg_intf_addrs_in, 1, + memory_order_relaxed); + + if (ret == AOK) + result = ZEBRA_DPLANE_REQUEST_QUEUED; + else { + /* Error counter */ + atomic_fetch_add_explicit(&zdplane_info.dg_intf_addr_errors, + 1, memory_order_relaxed); + if (ctx) + dplane_ctx_free(&ctx); + } + + return result; +} + +/* * Handler for 'show dplane' */ int dplane_show_helper(struct vty *vty, bool detailed) @@ -1877,6 +2147,35 @@ kernel_dplane_route_update(struct zebra_dplane_ctx *ctx) } /* + * Handler for kernel-facing interface address updates + */ +static enum zebra_dplane_result +kernel_dplane_address_update(struct zebra_dplane_ctx *ctx) +{ + enum zebra_dplane_result res; + + + if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) { + char dest_str[PREFIX_STRLEN]; + + prefix2str(dplane_ctx_get_intf_addr(ctx), dest_str, + sizeof(dest_str)); + + zlog_debug("Dplane intf %s, idx %u, addr %s", + dplane_op2str(dplane_ctx_get_op(ctx)), + dplane_ctx_get_ifindex(ctx), dest_str); + } + + res = kernel_address_update_ctx(ctx); + + if (res != ZEBRA_DPLANE_REQUEST_SUCCESS) + atomic_fetch_add_explicit(&zdplane_info.dg_intf_addr_errors, + 1, memory_order_relaxed); + + return res; +} + +/* * Kernel provider callback */ static int kernel_dplane_process_func(struct zebra_dplane_provider *prov) @@ -1925,6 +2224,11 @@ static int kernel_dplane_process_func(struct zebra_dplane_provider *prov) res = kernel_dplane_pw_update(ctx); break; + case DPLANE_OP_ADDR_INSTALL: + case DPLANE_OP_ADDR_UNINSTALL: + res = kernel_dplane_address_update(ctx); + break; + /* Ignore system 'notifications' - the kernel already knows */ case DPLANE_OP_SYS_ROUTE_ADD: case DPLANE_OP_SYS_ROUTE_DELETE: diff --git a/zebra/zebra_dplane.h b/zebra/zebra_dplane.h index 4e089bc66..d45628fdd 100644 --- a/zebra/zebra_dplane.h +++ b/zebra/zebra_dplane.h @@ -118,6 +118,10 @@ enum dplane_op_e { /* System route notification */ DPLANE_OP_SYS_ROUTE_ADD, DPLANE_OP_SYS_ROUTE_DELETE, + + /* Interface address update */ + DPLANE_OP_ADDR_INSTALL, + DPLANE_OP_ADDR_UNINSTALL, }; /* Enable system route notifications */ @@ -234,6 +238,22 @@ const union pw_protocol_fields *dplane_ctx_get_pw_proto( const struct nexthop_group *dplane_ctx_get_pw_nhg( const struct zebra_dplane_ctx *ctx); +/* Accessors for interface information */ +const char *dplane_ctx_get_ifname(const struct zebra_dplane_ctx *ctx); +ifindex_t dplane_ctx_get_ifindex(const struct zebra_dplane_ctx *ctx); +uint32_t dplane_ctx_get_intf_metric(const struct zebra_dplane_ctx *ctx); +/* Is interface addr p2p? */ +bool dplane_ctx_intf_is_connected(const struct zebra_dplane_ctx *ctx); +bool dplane_ctx_intf_is_secondary(const struct zebra_dplane_ctx *ctx); +bool dplane_ctx_intf_is_broadcast(const struct zebra_dplane_ctx *ctx); +const struct prefix *dplane_ctx_get_intf_addr( + const struct zebra_dplane_ctx *ctx); +bool dplane_ctx_intf_has_dest(const struct zebra_dplane_ctx *ctx); +const struct prefix *dplane_ctx_get_intf_dest( + const struct zebra_dplane_ctx *ctx); +bool dplane_ctx_intf_has_label(const struct zebra_dplane_ctx *ctx); +const char *dplane_ctx_get_intf_label(const struct zebra_dplane_ctx *ctx); + /* Namespace info - esp. for netlink communication */ const struct zebra_dplane_info *dplane_ctx_get_ns( const struct zebra_dplane_ctx *ctx); @@ -275,6 +295,15 @@ enum zebra_dplane_result dplane_lsp_delete(zebra_lsp_t *lsp); enum zebra_dplane_result dplane_pw_install(struct zebra_pw *pw); enum zebra_dplane_result dplane_pw_uninstall(struct zebra_pw *pw); +/* + * Enqueue interface address changes for the dataplane. + */ +enum zebra_dplane_result dplane_intf_addr_set(const struct interface *ifp, + const struct connected *ifc); +enum zebra_dplane_result dplane_intf_addr_unset(const struct interface *ifp, + const struct connected *ifc); + + /* Retrieve the limit on the number of pending, unprocessed updates. */ uint32_t dplane_get_in_queue_limit(void); diff --git a/zebra/zebra_l2.c b/zebra/zebra_l2.c index f4b2fe479..ca37dd748 100644 --- a/zebra/zebra_l2.c +++ b/zebra/zebra_l2.c @@ -172,6 +172,7 @@ void zebra_l2_vxlanif_add_update(struct interface *ifp, { struct zebra_if *zif; struct in_addr old_vtep_ip; + uint16_t chgflags = 0; zif = ifp->info; assert(zif); @@ -183,11 +184,20 @@ void zebra_l2_vxlanif_add_update(struct interface *ifp, } old_vtep_ip = zif->l2info.vxl.vtep_ip; - if (IPV4_ADDR_SAME(&old_vtep_ip, &vxlan_info->vtep_ip)) - return; - zif->l2info.vxl.vtep_ip = vxlan_info->vtep_ip; - zebra_vxlan_if_update(ifp, ZEBRA_VXLIF_LOCAL_IP_CHANGE); + if (!IPV4_ADDR_SAME(&old_vtep_ip, &vxlan_info->vtep_ip)) { + chgflags |= ZEBRA_VXLIF_LOCAL_IP_CHANGE; + zif->l2info.vxl.vtep_ip = vxlan_info->vtep_ip; + } + + if (!IPV4_ADDR_SAME(&zif->l2info.vxl.mcast_grp, + &vxlan_info->mcast_grp)) { + chgflags |= ZEBRA_VXLIF_MCAST_GRP_CHANGE; + zif->l2info.vxl.mcast_grp = vxlan_info->mcast_grp; + } + + if (chgflags) + zebra_vxlan_if_update(ifp, chgflags); } /* diff --git a/zebra/zebra_l2.h b/zebra/zebra_l2.h index 2e3e5b4a8..33aa2e374 100644 --- a/zebra/zebra_l2.h +++ b/zebra/zebra_l2.h @@ -54,6 +54,7 @@ struct zebra_l2info_vxlan { vni_t vni; /* VNI */ struct in_addr vtep_ip; /* Local tunnel IP */ vlanid_t access_vlan; /* Access VLAN - for VLAN-aware bridge. */ + struct in_addr mcast_grp; }; struct zebra_l2info_bondslave { diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index e675ca0a7..626583a84 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -24,7 +24,6 @@ #include "if.h" #include "linklist.h" #include "log.h" -#include "log_int.h" #include "memory.h" #include "mpls.h" #include "nexthop.h" @@ -403,10 +402,13 @@ static void nexthop_set_resolved(afi_t afi, const struct nexthop *newhop, nexthop_add(&nexthop->resolved, resolved_hop); } -/* If force flag is not set, do not modify falgs at all for uninstall - the route from FIB. */ +/* + * Given a nexthop we need to properly recursively resolve + * the route. As such, do a table lookup to find and match + * if at all possible. Set the nexthop->ifindex as appropriate + */ static int nexthop_active(afi_t afi, struct route_entry *re, - struct nexthop *nexthop, bool set, + struct nexthop *nexthop, struct route_node *top) { struct prefix p; @@ -422,12 +424,10 @@ static int nexthop_active(afi_t afi, struct route_entry *re, || nexthop->type == NEXTHOP_TYPE_IPV6) nexthop->ifindex = 0; - if (set) { - UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE); - nexthops_free(nexthop->resolved); - nexthop->resolved = NULL; - re->nexthop_mtu = 0; - } + UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE); + nexthops_free(nexthop->resolved); + nexthop->resolved = NULL; + re->nexthop_mtu = 0; /* * If the kernel has sent us a route, then @@ -437,16 +437,6 @@ static int nexthop_active(afi_t afi, struct route_entry *re, re->type == ZEBRA_ROUTE_SYSTEM) return 1; - /* Skip nexthops that have been filtered out due to route-map */ - /* The nexthops are specific to this route and so the same */ - /* nexthop for a different route may not have this flag set */ - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FILTERED)) { - if (IS_ZEBRA_DEBUG_RIB_DETAILED) - zlog_debug("\t%s: Nexthop Filtered", - __PRETTY_FUNCTION__); - return 0; - } - /* * Check to see if we should trust the passed in information * for UNNUMBERED interfaces as that we won't find the GW @@ -581,17 +571,14 @@ static int nexthop_active(afi_t afi, struct route_entry *re, NEXTHOP_FLAG_RECURSIVE)) continue; - if (set) { - SET_FLAG(nexthop->flags, - NEXTHOP_FLAG_RECURSIVE); - SET_FLAG(re->status, - ROUTE_ENTRY_NEXTHOPS_CHANGED); - nexthop_set_resolved(afi, newhop, - nexthop); - } + SET_FLAG(nexthop->flags, + NEXTHOP_FLAG_RECURSIVE); + SET_FLAG(re->status, + ROUTE_ENTRY_NEXTHOPS_CHANGED); + nexthop_set_resolved(afi, newhop, nexthop); resolved = 1; } - if (resolved && set) + if (resolved) re->nexthop_mtu = match->mtu; if (!resolved && IS_ZEBRA_DEBUG_RIB_DETAILED) zlog_debug("\t%s: Recursion failed to find", @@ -607,15 +594,12 @@ static int nexthop_active(afi_t afi, struct route_entry *re, NEXTHOP_FLAG_RECURSIVE)) continue; - if (set) { - SET_FLAG(nexthop->flags, - NEXTHOP_FLAG_RECURSIVE); - nexthop_set_resolved(afi, newhop, - nexthop); - } + SET_FLAG(nexthop->flags, + NEXTHOP_FLAG_RECURSIVE); + nexthop_set_resolved(afi, newhop, nexthop); resolved = 1; } - if (resolved && set) + if (resolved) re->nexthop_mtu = match->mtu; if (!resolved && IS_ZEBRA_DEBUG_RIB_DETAILED) @@ -819,17 +803,15 @@ struct route_entry *rib_lookup_ipv4(struct prefix_ipv4 *p, vrf_id_t vrf_id) /* This function verifies reachability of one given nexthop, which can be * numbered or unnumbered, IPv4 or IPv6. The result is unconditionally stored - * in nexthop->flags field. If the 4th parameter, 'set', is non-zero, - * nexthop->ifindex will be updated appropriately as well. - * An existing route map can turn (otherwise active) nexthop into inactive, but - * not vice versa. + * in nexthop->flags field. The nexthop->ifindex will be updated + * appropriately as well. An existing route map can turn + * (otherwise active) nexthop into inactive, but not vice versa. * * The return value is the final value of 'ACTIVE' flag. */ - static unsigned nexthop_active_check(struct route_node *rn, struct route_entry *re, - struct nexthop *nexthop, bool set) + struct nexthop *nexthop) { struct interface *ifp; route_map_result_t ret = RMAP_MATCH; @@ -857,14 +839,14 @@ static unsigned nexthop_active_check(struct route_node *rn, case NEXTHOP_TYPE_IPV4: case NEXTHOP_TYPE_IPV4_IFINDEX: family = AFI_IP; - if (nexthop_active(AFI_IP, re, nexthop, set, rn)) + if (nexthop_active(AFI_IP, re, nexthop, rn)) SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); else UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); break; case NEXTHOP_TYPE_IPV6: family = AFI_IP6; - if (nexthop_active(AFI_IP6, re, nexthop, set, rn)) + if (nexthop_active(AFI_IP6, re, nexthop, rn)) SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); else UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); @@ -881,7 +863,7 @@ static unsigned nexthop_active_check(struct route_node *rn, else UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); } else { - if (nexthop_active(AFI_IP6, re, nexthop, set, rn)) + if (nexthop_active(AFI_IP6, re, nexthop, rn)) SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); else UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); @@ -946,25 +928,21 @@ static unsigned nexthop_active_check(struct route_node *rn, return CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); } -/* Iterate over all nexthops of the given RIB entry and refresh their +/* + * Iterate over all nexthops of the given RIB entry and refresh their * ACTIVE flag. re->nexthop_active_num is updated accordingly. If any * nexthop is found to toggle the ACTIVE flag, the whole re structure - * is flagged with ROUTE_ENTRY_CHANGED. The 4th 'set' argument is - * transparently passed to nexthop_active_check(). + * is flagged with ROUTE_ENTRY_CHANGED. * * Return value is the new number of active nexthops. */ - -static int nexthop_active_update(struct route_node *rn, struct route_entry *re, - bool set) +static int nexthop_active_update(struct route_node *rn, struct route_entry *re) { struct nexthop *nexthop; union g_addr prev_src; - unsigned int prev_active, new_active, old_num_nh; + unsigned int prev_active, new_active; ifindex_t prev_index; - old_num_nh = re->nexthop_active_num; - re->nexthop_active_num = 0; UNSET_FLAG(re->status, ROUTE_ENTRY_CHANGED); @@ -980,7 +958,7 @@ static int nexthop_active_update(struct route_node *rn, struct route_entry *re, * a multipath perpsective should not be a data plane * decision point. */ - new_active = nexthop_active_check(rn, re, nexthop, set); + new_active = nexthop_active_check(rn, re, nexthop); if (new_active && re->nexthop_active_num >= multipath_num) { UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); new_active = 0; @@ -996,19 +974,13 @@ static int nexthop_active_update(struct route_node *rn, struct route_entry *re, || ((nexthop->type >= NEXTHOP_TYPE_IPV6 && nexthop->type < NEXTHOP_TYPE_BLACKHOLE) && !(IPV6_ADDR_SAME(&prev_src.ipv6, - &nexthop->rmap_src.ipv6)))) { + &nexthop->rmap_src.ipv6))) + || CHECK_FLAG(re->status, ROUTE_ENTRY_LABELS_CHANGED)) { SET_FLAG(re->status, ROUTE_ENTRY_CHANGED); SET_FLAG(re->status, ROUTE_ENTRY_NEXTHOPS_CHANGED); } } - if (old_num_nh != re->nexthop_active_num) - SET_FLAG(re->status, ROUTE_ENTRY_CHANGED); - - if (CHECK_FLAG(re->status, ROUTE_ENTRY_CHANGED)) { - SET_FLAG(re->status, ROUTE_ENTRY_NEXTHOPS_CHANGED); - } - return re->nexthop_active_num; } @@ -1354,7 +1326,7 @@ static void rib_process_add_fib(struct zebra_vrf *zvrf, struct route_node *rn, /* Update real nexthop. This may actually determine if nexthop is active * or not. */ - if (!nexthop_active_update(rn, new, true)) { + if (!nexthop_group_active_nexthop_num(&new->ng)) { UNSET_FLAG(new->status, ROUTE_ENTRY_CHANGED); return; } @@ -1401,8 +1373,7 @@ static void rib_process_del_fib(struct zebra_vrf *zvrf, struct route_node *rn, * down, causing the kernel to delete routes without sending DELROUTE * notifications */ - if (!nexthop_active_update(rn, old, true) && - (RIB_KERNEL_ROUTE(old))) + if (RIB_KERNEL_ROUTE(old)) SET_FLAG(old->status, ROUTE_ENTRY_REMOVED); else UNSET_FLAG(old->status, ROUTE_ENTRY_CHANGED); @@ -1424,7 +1395,7 @@ static void rib_process_update_fib(struct zebra_vrf *zvrf, /* Update the nexthop; we could determine here that nexthop is * inactive. */ - if (nexthop_active_update(rn, new, true)) + if (nexthop_group_active_nexthop_num(&new->ng)) nh_active = 1; /* If nexthop is active, install the selected route, if @@ -1509,11 +1480,8 @@ static void rib_process_update_fib(struct zebra_vrf *zvrf, } /* Update prior route. */ - if (new != old) { - /* Set real nexthop. */ - nexthop_active_update(rn, old, true); + if (new != old) UNSET_FLAG(old->status, ROUTE_ENTRY_CHANGED); - } /* Clear changed flag. */ UNSET_FLAG(new->status, ROUTE_ENTRY_CHANGED); @@ -1643,38 +1611,30 @@ static void rib_process(struct route_node *rn) /* Skip unreachable nexthop. */ /* This first call to nexthop_active_update is merely to - * determine if - * there's any change to nexthops associated with this RIB - * entry. Now, - * rib_process() can be invoked due to an external event such as - * link - * down or due to next-hop-tracking evaluation. In the latter - * case, + * determine if there's any change to nexthops associated + * with this RIB entry. Now, rib_process() can be invoked due + * to an external event such as link down or due to + * next-hop-tracking evaluation. In the latter case, * a decision has already been made that the NHs have changed. - * So, no - * need to invoke a potentially expensive call again. Further, - * since - * the change might be in a recursive NH which is not caught in - * the nexthop_active_update() code. Thus, we might miss changes - * to - * recursive NHs. + * So, no need to invoke a potentially expensive call again. + * Further, since the change might be in a recursive NH which + * is not caught in the nexthop_active_update() code. Thus, we + * might miss changes to recursive NHs. */ - if (!CHECK_FLAG(re->status, ROUTE_ENTRY_CHANGED) - && !nexthop_active_update(rn, re, false)) { + if (CHECK_FLAG(re->status, ROUTE_ENTRY_CHANGED) + && !nexthop_active_update(rn, re)) { if (re->type == ZEBRA_ROUTE_TABLE) { /* XXX: HERE BE DRAGONS!!!!! * In all honesty, I have not yet figured out - * what this part - * does or why the ROUTE_ENTRY_CHANGED test - * above is correct + * what this part does or why the + * ROUTE_ENTRY_CHANGED test above is correct * or why we need to delete a route here, and - * also not whether - * this concerns both selected and fib route, or - * only selected - * or only fib */ - /* This entry was denied by the 'ip protocol - * table' route-map, we - * need to delete it */ + * also not whether this concerns both selected + * and fib route, or only selected + * or only fib + * + * This entry was denied by the 'ip protocol + * table' route-map, we need to delete it */ if (re != old_selected) { if (IS_ZEBRA_DEBUG_RIB) zlog_debug( @@ -1751,10 +1711,8 @@ static void rib_process(struct route_node *rn) /* Update SELECTED entry */ if (old_selected != new_selected || selected_changed) { - if (new_selected && new_selected != new_fib) { - nexthop_active_update(rn, new_selected, true); + if (new_selected && new_selected != new_fib) UNSET_FLAG(new_selected->status, ROUTE_ENTRY_CHANGED); - } if (new_selected) SET_FLAG(new_selected->flags, ZEBRA_FLAG_SELECTED); @@ -2613,8 +2571,8 @@ void _route_entry_dump(const char *func, union prefixconstptr pp, INET6_ADDRSTRLEN); break; } - zlog_debug("%s: %s %s[%u] vrf %s(%u) with flags %s%s%s", func, - (nexthop->rparent ? " NH" : "NH"), straddr, + zlog_debug("%s: %s %s[%u] vrf %s(%u) with flags %s%s%s%s%s%s", + func, (nexthop->rparent ? " NH" : "NH"), straddr, nexthop->ifindex, vrf ? vrf->name : "Unknown", nexthop->vrf_id, (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE) @@ -2624,7 +2582,16 @@ void _route_entry_dump(const char *func, union prefixconstptr pp, ? "FIB " : ""), (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE) - ? "RECURSIVE" + ? "RECURSIVE " + : ""), + (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK) + ? "ONLINK " + : ""), + (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_MATCHED) + ? "MATCHED " + : ""), + (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_DUPLICATE) + ? "DUPLICATE " : "")); } zlog_debug("%s: dump complete", func); @@ -2688,7 +2655,6 @@ void rib_lookup_and_pushup(struct prefix_ipv4 *p, vrf_id_t vrf_id) { struct route_table *table; struct route_node *rn; - unsigned changed = 0; rib_dest_t *dest; if (NULL == (table = zebra_vrf_table(AFI_IP, SAFI_UNICAST, vrf_id))) { @@ -2715,7 +2681,6 @@ void rib_lookup_and_pushup(struct prefix_ipv4 *p, vrf_id_t vrf_id) * of the rest of the RE. */ if (dest->selected_fib) { - changed = 1; if (IS_ZEBRA_DEBUG_RIB) { char buf[PREFIX_STRLEN]; @@ -2725,9 +2690,8 @@ void rib_lookup_and_pushup(struct prefix_ipv4 *p, vrf_id_t vrf_id) route_entry_dump(&rn->p, NULL, dest->selected_fib); } rib_uninstall(rn, dest->selected_fib); - } - if (changed) rib_queue_add(rn); + } } int rib_add_multipath(afi_t afi, safi_t safi, struct prefix *p, @@ -2814,6 +2778,8 @@ int rib_add_multipath(afi_t afi, safi_t safi, struct prefix *p, if (IS_ZEBRA_DEBUG_RIB_DETAILED) route_entry_dump(p, src_p, re); } + + SET_FLAG(re->status, ROUTE_ENTRY_CHANGED); rib_addnode(rn, re, 1); ret = 1; @@ -3100,6 +3066,8 @@ void rib_update_table(struct route_table *table, rib_update_event_t event) continue; if (re->type != ZEBRA_ROUTE_STATIC) { + SET_FLAG(re->status, + ROUTE_ENTRY_CHANGED); rib_queue_add(rn); continue; } @@ -3113,8 +3081,11 @@ void rib_update_table(struct route_table *table, rib_update_event_t event) * gateway, NHT will * take care. */ - if (nh) + if (nh) { + SET_FLAG(re->status, + ROUTE_ENTRY_CHANGED); rib_queue_add(rn); + } } break; @@ -3124,8 +3095,12 @@ void rib_update_table(struct route_table *table, rib_update_event_t event) * protocol in * some cases (TODO). */ - if (rnode_to_ribs(rn)) + if (rnode_to_ribs(rn)) { + RNODE_FOREACH_RE_SAFE (rn, re, next) + SET_FLAG(re->status, + ROUTE_ENTRY_CHANGED); rib_queue_add(rn); + } break; default: diff --git a/zebra/zebra_rnh.c b/zebra/zebra_rnh.c index 040043146..220a8006d 100644 --- a/zebra/zebra_rnh.c +++ b/zebra/zebra_rnh.c @@ -377,6 +377,20 @@ void zebra_deregister_rnh_pseudowire(vrf_id_t vrf_id, struct zebra_pw *pw) zebra_delete_rnh(rnh, RNH_NEXTHOP_TYPE); } +/* Clear the NEXTHOP_FLAG_RNH_FILTERED flags on all nexthops + */ +static void zebra_rnh_clear_nexthop_rnh_filters(struct route_entry *re) +{ + struct nexthop *nexthop; + + if (re) { + for (nexthop = re->ng.nexthop; nexthop; + nexthop = nexthop->next) { + UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_RNH_FILTERED); + } + } +} + /* Apply the NHT route-map for a client to the route (and nexthops) * resolving a NH. */ @@ -393,11 +407,11 @@ static int zebra_rnh_apply_nht_rmap(afi_t afi, struct zebra_vrf *zvrf, nexthop = nexthop->next) { ret = zebra_nht_route_map_check( afi, proto, &prn->p, zvrf, re, nexthop); - if (ret != RMAP_DENYMATCH) { - SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); + if (ret != RMAP_DENYMATCH) at_least_one++; /* at least one valid NH */ - } else { - UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); + else { + SET_FLAG(nexthop->flags, + NEXTHOP_FLAG_RNH_FILTERED); } } } @@ -546,6 +560,7 @@ static void zebra_rnh_notify_protocol_clients(struct zebra_vrf *zvrf, afi_t afi, * this * nexthop to see if it is filtered or not. */ + zebra_rnh_clear_nexthop_rnh_filters(re); num_resolving_nh = zebra_rnh_apply_nht_rmap( afi, zvrf, prn, re, client->proto); if (num_resolving_nh) @@ -572,6 +587,9 @@ static void zebra_rnh_notify_protocol_clients(struct zebra_vrf *zvrf, afi_t afi, send_client(rnh, client, RNH_NEXTHOP_TYPE, zvrf->vrf->vrf_id); } + + if (re) + zebra_rnh_clear_nexthop_rnh_filters(re); } static void zebra_rnh_process_pbr_tables(afi_t afi, struct route_node *nrn, @@ -631,7 +649,10 @@ static bool rnh_nexthop_valid(const struct route_entry *re, const struct nexthop *nh) { return (CHECK_FLAG(re->status, ROUTE_ENTRY_INSTALLED) - && CHECK_FLAG(nh->flags, NEXTHOP_FLAG_ACTIVE)); + && CHECK_FLAG(nh->flags, NEXTHOP_FLAG_ACTIVE) + && !CHECK_FLAG(nh->flags, NEXTHOP_FLAG_RECURSIVE) + && !CHECK_FLAG(nh->flags, NEXTHOP_FLAG_DUPLICATE) + && !CHECK_FLAG(nh->flags, NEXTHOP_FLAG_RNH_FILTERED)); } /* diff --git a/zebra/zebra_vrf.h b/zebra/zebra_vrf.h index 7113c160a..c7a64d300 100644 --- a/zebra/zebra_vrf.h +++ b/zebra/zebra_vrf.h @@ -130,6 +130,9 @@ struct zebra_vrf { /* l3-vni info */ vni_t l3vni; + /* pim mroutes installed for vxlan flooding */ + struct hash *vxlan_sg_table; + bool dup_addr_detect; int dad_time; diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index 285fe3200..a2e217130 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -58,8 +58,19 @@ DEFINE_MTYPE_STATIC(ZEBRA, ZL3VNI, "L3 VNI hash"); DEFINE_MTYPE_STATIC(ZEBRA, ZVNI_VTEP, "VNI remote VTEP"); DEFINE_MTYPE_STATIC(ZEBRA, MAC, "VNI MAC"); DEFINE_MTYPE_STATIC(ZEBRA, NEIGH, "VNI Neighbor"); +DEFINE_MTYPE_STATIC(ZEBRA, ZVXLAN_SG, "zebra VxLAN multicast group"); /* definitions */ +/* PMSI strings. */ +#define VXLAN_FLOOD_STR_NO_INFO "-" +#define VXLAN_FLOOD_STR_DEFAULT VXLAN_FLOOD_STR_NO_INFO +static const struct message zvtep_flood_str[] = { + {VXLAN_FLOOD_DISABLED, VXLAN_FLOOD_STR_NO_INFO}, + {VXLAN_FLOOD_PIM_SM, "PIM-SM"}, + {VXLAN_FLOOD_HEAD_END_REPL, "HER"}, + {0} +}; + /* static function declarations */ static int ip_prefix_send_to_client(vrf_id_t vrf_id, struct prefix *p, @@ -167,10 +178,11 @@ static int zvni_send_del_to_client(vni_t vni); static void zvni_build_hash_table(void); static int zvni_vtep_match(struct in_addr *vtep_ip, zebra_vtep_t *zvtep); static zebra_vtep_t *zvni_vtep_find(zebra_vni_t *zvni, struct in_addr *vtep_ip); -static zebra_vtep_t *zvni_vtep_add(zebra_vni_t *zvni, struct in_addr *vtep_ip); +static zebra_vtep_t *zvni_vtep_add(zebra_vni_t *zvni, struct in_addr *vtep_ip, + int flood_control); 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_install(zebra_vni_t *zvni, zebra_vtep_t *zvtep); 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); @@ -201,6 +213,17 @@ static void zebra_vxlan_dup_addr_detect_for_mac(struct zebra_vrf *zvrf, bool do_dad, bool *is_dup_detect, bool is_local); +static unsigned int zebra_vxlan_sg_hash_key_make(void *p); +static bool zebra_vxlan_sg_hash_eq(const void *p1, const void *p2); +static void zebra_vxlan_sg_do_deref(struct zebra_vrf *zvrf, + struct in_addr sip, struct in_addr mcast_grp); +static zebra_vxlan_sg_t *zebra_vxlan_sg_do_ref(struct zebra_vrf *vrf, + struct in_addr sip, struct in_addr mcast_grp); +static void zebra_vxlan_sg_deref(struct in_addr local_vtep_ip, + struct in_addr mcast_grp); +static void zebra_vxlan_sg_ref(struct in_addr local_vtep_ip, + struct in_addr mcast_grp); +static void zebra_vxlan_sg_cleanup(struct hash_backet *backet, void *arg); /* Private functions */ static int host_rb_entry_compare(const struct host_rb_entry *hle1, @@ -1858,12 +1881,16 @@ static void zvni_print(zebra_vni_t *zvni, void **ctxt) vty_out(vty, " VxLAN ifIndex: %u\n", zvni->vxlan_if->ifindex); vty_out(vty, " Local VTEP IP: %s\n", inet_ntoa(zvni->local_vtep_ip)); + vty_out(vty, " Mcast group: %s\n", + inet_ntoa(zvni->mcast_grp)); } 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, "mcastGroup", + inet_ntoa(zvni->mcast_grp)); json_object_string_add(json, "advertiseGatewayMacip", zvni->advertise_gw_macip ? "Yes" : "No"); json_object_int_add(json, "numMacs", num_macs); @@ -1878,14 +1905,19 @@ static void zvni_print(zebra_vni_t *zvni, void **ctxt) 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 { + const char *flood_str = lookup_msg(zvtep_flood_str, + zvtep->flood_control, + VXLAN_FLOOD_STR_DEFAULT); + + if (json == NULL) { + vty_out(vty, " %s flood: %s\n", + inet_ntoa(zvtep->vtep_ip), + flood_str); + } else { json_ip_str = json_object_new_string( - inet_ntoa(zvtep->vtep_ip)); + inet_ntoa(zvtep->vtep_ip)); json_object_array_add(json_vtep_list, - json_ip_str); + json_ip_str); } } if (json) @@ -3882,6 +3914,9 @@ static int zvni_del(zebra_vni_t *zvni) zvni->vxlan_if = NULL; + /* Remove references to the BUM mcast grp */ + zebra_vxlan_sg_deref(zvni->local_vtep_ip, zvni->mcast_grp); + /* Free the neighbor hash table. */ hash_free(zvni->neigh_table); zvni->neigh_table = NULL; @@ -3916,6 +3951,7 @@ static int zvni_send_add_to_client(zebra_vni_t *zvni) stream_putl(s, zvni->vni); stream_put_in_addr(s, &zvni->local_vtep_ip); stream_put(s, &zvni->vrf_id, sizeof(vrf_id_t)); /* tenant vrf */ + stream_put_in_addr(s, &zvni->mcast_grp); /* Write packet size. */ stream_putw_at(s, 0, stream_get_endp(s)); @@ -4038,7 +4074,15 @@ static void zvni_build_hash_table(void) return; } - zvni->local_vtep_ip = vxl->vtep_ip; + if (zvni->local_vtep_ip.s_addr != vxl->vtep_ip.s_addr || + zvni->mcast_grp.s_addr != vxl->mcast_grp.s_addr) { + zebra_vxlan_sg_deref(zvni->local_vtep_ip, + zvni->mcast_grp); + zebra_vxlan_sg_ref(vxl->vtep_ip, + vxl->mcast_grp); + zvni->local_vtep_ip = vxl->vtep_ip; + zvni->mcast_grp = vxl->mcast_grp; + } zvni->vxlan_if = ifp; vlan_if = zvni_map_to_svi(vxl->access_vlan, zif->brslave_info.br_if); @@ -4086,13 +4130,16 @@ static zebra_vtep_t *zvni_vtep_find(zebra_vni_t *zvni, struct in_addr *vtep_ip) /* * Add remote VTEP to VNI hash table. */ -static zebra_vtep_t *zvni_vtep_add(zebra_vni_t *zvni, struct in_addr *vtep_ip) +static zebra_vtep_t *zvni_vtep_add(zebra_vni_t *zvni, struct in_addr *vtep_ip, + int flood_control) + { zebra_vtep_t *zvtep; zvtep = XCALLOC(MTYPE_ZVNI_VTEP, sizeof(zebra_vtep_t)); zvtep->vtep_ip = *vtep_ip; + zvtep->flood_control = flood_control; if (zvni->vteps) zvni->vteps->prev = zvtep; @@ -4142,12 +4189,15 @@ static int zvni_vtep_del_all(zebra_vni_t *zvni, int uninstall) } /* - * Install remote VTEP into the kernel. + * Install remote VTEP into the kernel if the remote VTEP has asked + * for head-end-replication. */ -static int zvni_vtep_install(zebra_vni_t *zvni, struct in_addr *vtep_ip) +static int zvni_vtep_install(zebra_vni_t *zvni, zebra_vtep_t *zvtep) { - if (is_vxlan_flooding_head_end()) - return kernel_add_vtep(zvni->vni, zvni->vxlan_if, vtep_ip); + if (is_vxlan_flooding_head_end() && + (zvtep->flood_control == VXLAN_FLOOD_HEAD_END_REPL)) + return kernel_add_vtep(zvni->vni, zvni->vxlan_if, + &zvtep->vtep_ip); return 0; } @@ -4181,7 +4231,7 @@ static void zvni_handle_flooding_remote_vteps(struct hash_bucket *bucket, for (zvtep = zvni->vteps; zvtep; zvtep = zvtep->next) { if (is_vxlan_flooding_head_end()) - zvni_vtep_install(zvni, &zvtep->vtep_ip); + zvni_vtep_install(zvni, zvtep); else zvni_vtep_uninstall(zvni, &zvtep->vtep_ip); } @@ -5159,7 +5209,8 @@ static void process_remote_macip_add(vni_t vni, */ zvtep = zvni_vtep_find(zvni, &vtep_ip); if (!zvtep) { - if (zvni_vtep_add(zvni, &vtep_ip) == NULL) { + zvtep = zvni_vtep_add(zvni, &vtep_ip, VXLAN_FLOOD_DISABLED); + if (!zvtep) { flog_err( EC_ZEBRA_VTEP_ADD_FAILED, "Failed to add remote VTEP, VNI %u zvni %p upon remote MACIP ADD", @@ -5167,7 +5218,7 @@ static void process_remote_macip_add(vni_t vni, return; } - zvni_vtep_install(zvni, &vtep_ip); + zvni_vtep_install(zvni, zvtep); } sticky = !!CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_STICKY); @@ -7874,6 +7925,8 @@ void zebra_vxlan_remote_vtep_add(ZAPI_HANDLER_ARGS) zebra_vni_t *zvni; struct interface *ifp; struct zebra_if *zif; + int flood_control; + zebra_vtep_t *zvtep; if (!is_evpn_enabled()) { zlog_debug( @@ -7895,12 +7948,13 @@ void zebra_vxlan_remote_vtep_add(ZAPI_HANDLER_ARGS) STREAM_GETL(s, vni); l += 4; STREAM_GET(&vtep_ip.s_addr, s, IPV4_MAX_BYTELEN); + STREAM_GETL(s, flood_control); l += IPV4_MAX_BYTELEN; if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("Recv VTEP_ADD %s VNI %u from %s", - inet_ntoa(vtep_ip), vni, - zebra_route_string(client->proto)); + zlog_debug("Recv VTEP_ADD %s VNI %u flood %d from %s", + inet_ntoa(vtep_ip), vni, flood_control, + zebra_route_string(client->proto)); /* Locate VNI hash entry - expected to exist. */ zvni = zvni_lookup(vni); @@ -7927,19 +7981,31 @@ void zebra_vxlan_remote_vtep_add(ZAPI_HANDLER_ARGS) if (!if_is_operative(ifp) || !zif->brslave_info.br_if) continue; - /* If the remote VTEP already exists, - there's nothing more to do. */ - if (zvni_vtep_find(zvni, &vtep_ip)) - continue; - - if (zvni_vtep_add(zvni, &vtep_ip) == NULL) { - flog_err(EC_ZEBRA_VTEP_ADD_FAILED, - "Failed to add remote VTEP, VNI %u zvni %p", - vni, zvni); - continue; + zvtep = zvni_vtep_find(zvni, &vtep_ip); + if (zvtep) { + /* If the remote VTEP already exists check if + * the flood mode has changed + */ + if (zvtep->flood_control != flood_control) { + if (zvtep->flood_control + == VXLAN_FLOOD_DISABLED) + /* old mode was head-end-replication but + * is no longer; get rid of the HER fdb + * entry installed before + */ + zvni_vtep_uninstall(zvni, &vtep_ip); + zvtep->flood_control = flood_control; + zvni_vtep_install(zvni, zvtep); + } + } else { + zvtep = zvni_vtep_add(zvni, &vtep_ip, flood_control); + if (zvtep) + zvni_vtep_install(zvni, zvtep); + else + flog_err(EC_ZEBRA_VTEP_ADD_FAILED, + "Failed to add remote VTEP, VNI %u zvni %p", + vni, zvni); } - - zvni_vtep_install(zvni, &vtep_ip); } stream_failure: @@ -8476,7 +8542,14 @@ int zebra_vxlan_if_update(struct interface *ifp, uint16_t chgflags) zvni_mac_del_all(zvni, 0, 1, DEL_LOCAL_MAC); } - zvni->local_vtep_ip = vxl->vtep_ip; + if (zvni->local_vtep_ip.s_addr != vxl->vtep_ip.s_addr || + zvni->mcast_grp.s_addr != vxl->mcast_grp.s_addr) { + zebra_vxlan_sg_deref(zvni->local_vtep_ip, + zvni->mcast_grp); + zebra_vxlan_sg_ref(vxl->vtep_ip, vxl->mcast_grp); + zvni->local_vtep_ip = vxl->vtep_ip; + zvni->mcast_grp = vxl->mcast_grp; + } zvni->vxlan_if = ifp; /* Take further actions needed. @@ -8488,7 +8561,9 @@ int zebra_vxlan_if_update(struct interface *ifp, uint16_t chgflags) /* Inform BGP, if there is a change of interest. */ if (chgflags - & (ZEBRA_VXLIF_MASTER_CHANGE | ZEBRA_VXLIF_LOCAL_IP_CHANGE)) + & (ZEBRA_VXLIF_MASTER_CHANGE | + ZEBRA_VXLIF_LOCAL_IP_CHANGE | + ZEBRA_VXLIF_MCAST_GRP_CHANGE)) zvni_send_add_to_client(zvni); /* If there is a valid new master or a VLAN mapping change, @@ -8578,7 +8653,14 @@ int zebra_vxlan_if_add(struct interface *ifp) } } - zvni->local_vtep_ip = vxl->vtep_ip; + if (zvni->local_vtep_ip.s_addr != vxl->vtep_ip.s_addr || + zvni->mcast_grp.s_addr != vxl->mcast_grp.s_addr) { + zebra_vxlan_sg_deref(zvni->local_vtep_ip, + zvni->mcast_grp); + zebra_vxlan_sg_ref(vxl->vtep_ip, vxl->mcast_grp); + zvni->local_vtep_ip = vxl->vtep_ip; + zvni->mcast_grp = vxl->mcast_grp; + } zvni->vxlan_if = ifp; vlan_if = zvni_map_to_svi(vxl->access_vlan, zif->brslave_info.br_if); @@ -8589,15 +8671,24 @@ int zebra_vxlan_if_add(struct interface *ifp) listnode_add_sort(zl3vni->l2vnis, zvni); } - if (IS_ZEBRA_DEBUG_VXLAN) + if (IS_ZEBRA_DEBUG_VXLAN) { + char addr_buf1[INET_ADDRSTRLEN]; + char addr_buf2[INET_ADDRSTRLEN]; + + inet_ntop(AF_INET, &vxl->vtep_ip, + addr_buf1, INET_ADDRSTRLEN); + inet_ntop(AF_INET, &vxl->mcast_grp, + addr_buf2, INET_ADDRSTRLEN); + zlog_debug( - "Add L2-VNI %u VRF %s intf %s(%u) VLAN %u local IP %s master %u", + "Add L2-VNI %u VRF %s intf %s(%u) VLAN %u local IP %s mcast_grp %s master %u", vni, vlan_if ? vrf_id_to_name(vlan_if->vrf_id) : VRF_DEFAULT_NAME, ifp->name, ifp->ifindex, vxl->access_vlan, - inet_ntoa(vxl->vtep_ip), + addr_buf1, addr_buf2, zif->brslave_info.bridge_ifindex); + } /* If down or not mapped to a bridge, we're done. */ if (!if_is_operative(ifp) || !zif->brslave_info.br_if) @@ -8688,6 +8779,13 @@ int zebra_vxlan_process_vrf_vni_cmd(struct zebra_vrf *zvrf, vni_t vni, return -1; } + if (zvrf->l3vni != vni) { + snprintf(err, err_str_sz, + "VNI %d doesn't exist in VRF: %s", + vni, zvrf->vrf->name); + return -1; + } + if (filter && !CHECK_FLAG(zl3vni->filter, PREFIX_ROUTES_ONLY)) { snprintf(err, ERR_STR_SZ, "prefix-routes-only is not set for the vni"); @@ -9144,6 +9242,8 @@ void zebra_vxlan_init_tables(struct zebra_vrf *zvrf) return; zvrf->vni_table = hash_create(vni_hash_keymake, vni_hash_cmp, "Zebra VRF VNI Table"); + zvrf->vxlan_sg_table = hash_create(zebra_vxlan_sg_hash_key_make, + zebra_vxlan_sg_hash_eq, "Zebra VxLAN SG Table"); } /* Cleanup VNI info, but don't free the table. */ @@ -9152,6 +9252,7 @@ void zebra_vxlan_cleanup_tables(struct zebra_vrf *zvrf) if (!zvrf) return; hash_iterate(zvrf->vni_table, zvni_cleanup_all, zvrf); + hash_iterate(zvrf->vxlan_sg_table, zebra_vxlan_sg_cleanup, NULL); } /* Close all VNI handling */ @@ -9311,3 +9412,221 @@ static int zebra_vxlan_dad_mac_auto_recovery_exp(struct thread *t) return 0; } + +/************************** vxlan SG cache management ************************/ +/* Inform PIM about the mcast group */ +static int zebra_vxlan_sg_send(struct prefix_sg *sg, + char *sg_str, uint16_t cmd) +{ + struct zserv *client = NULL; + struct stream *s = NULL; + + client = zserv_find_client(ZEBRA_ROUTE_PIM, 0); + if (!client) + return 0; + + s = stream_new(ZEBRA_MAX_PACKET_SIZ); + + zclient_create_header(s, cmd, VRF_DEFAULT); + stream_putl(s, IPV4_MAX_BYTELEN); + stream_put(s, &sg->src.s_addr, IPV4_MAX_BYTELEN); + stream_put(s, &sg->grp.s_addr, IPV4_MAX_BYTELEN); + + /* Write packet size. */ + stream_putw_at(s, 0, stream_get_endp(s)); + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "Send %s %s to %s", + (cmd == ZEBRA_VXLAN_SG_ADD) ? "add" : "del", sg_str, + zebra_route_string(client->proto)); + + if (cmd == ZEBRA_VXLAN_SG_ADD) + client->vxlan_sg_add_cnt++; + else + client->vxlan_sg_del_cnt++; + + return zserv_send_message(client, s); +} + +static unsigned int zebra_vxlan_sg_hash_key_make(void *p) +{ + zebra_vxlan_sg_t *vxlan_sg = p; + + return (jhash_2words(vxlan_sg->sg.src.s_addr, + vxlan_sg->sg.grp.s_addr, 0)); +} + +static bool zebra_vxlan_sg_hash_eq(const void *p1, const void *p2) +{ + const zebra_vxlan_sg_t *sg1 = p1; + const zebra_vxlan_sg_t *sg2 = p2; + + return ((sg1->sg.src.s_addr == sg2->sg.src.s_addr) + && (sg1->sg.grp.s_addr == sg2->sg.grp.s_addr)); +} + +static zebra_vxlan_sg_t *zebra_vxlan_sg_new(struct zebra_vrf *zvrf, + struct prefix_sg *sg) +{ + zebra_vxlan_sg_t *vxlan_sg; + + vxlan_sg = XCALLOC(MTYPE_ZVXLAN_SG, sizeof(*vxlan_sg)); + + vxlan_sg->zvrf = zvrf; + vxlan_sg->sg = *sg; + prefix_sg2str(sg, vxlan_sg->sg_str); + + vxlan_sg = hash_get(zvrf->vxlan_sg_table, vxlan_sg, hash_alloc_intern); + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("vxlan SG %s created", vxlan_sg->sg_str); + + return vxlan_sg; +} + +static zebra_vxlan_sg_t *zebra_vxlan_sg_find(struct zebra_vrf *zvrf, + struct prefix_sg *sg) +{ + zebra_vxlan_sg_t lookup; + + lookup.sg = *sg; + return hash_lookup(zvrf->vxlan_sg_table, &lookup); +} + +static zebra_vxlan_sg_t *zebra_vxlan_sg_add(struct zebra_vrf *zvrf, + struct prefix_sg *sg) +{ + zebra_vxlan_sg_t *vxlan_sg; + zebra_vxlan_sg_t *parent = NULL; + struct in_addr sip; + + vxlan_sg = zebra_vxlan_sg_find(zvrf, sg); + if (vxlan_sg) + return vxlan_sg; + + /* create a *G entry for every BUM group implicitly - + * 1. The SG entry is used by pimd to setup the vxlan-origination-mroute + * 2. the XG entry is used by pimd to setup the + * vxlan-termination-mroute + */ + if (sg->src.s_addr) { + memset(&sip, 0, sizeof(sip)); + parent = zebra_vxlan_sg_do_ref(zvrf, sip, sg->grp); + if (!parent) + return NULL; + } + + vxlan_sg = zebra_vxlan_sg_new(zvrf, sg); + if (!vxlan_sg) { + if (parent) + zebra_vxlan_sg_do_deref(zvrf, sip, sg->grp); + return vxlan_sg; + } + + zebra_vxlan_sg_send(sg, vxlan_sg->sg_str, ZEBRA_VXLAN_SG_ADD); + + return vxlan_sg; +} + +static void zebra_vxlan_sg_del(zebra_vxlan_sg_t *vxlan_sg) +{ + struct in_addr sip; + struct zebra_vrf *zvrf; + + zvrf = vrf_info_lookup(VRF_DEFAULT); + if (!zvrf) + return; + + /* On SG entry deletion remove the reference to its parent XG + * entry + */ + if (vxlan_sg->sg.src.s_addr) { + memset(&sip, 0, sizeof(sip)); + zebra_vxlan_sg_do_deref(zvrf, sip, vxlan_sg->sg.grp); + } + + zebra_vxlan_sg_send(&vxlan_sg->sg, vxlan_sg->sg_str, + ZEBRA_VXLAN_SG_DEL); + + hash_release(vxlan_sg->zvrf->vxlan_sg_table, vxlan_sg); + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("VXLAN SG %s deleted", vxlan_sg->sg_str); + + XFREE(MTYPE_ZVXLAN_SG, vxlan_sg); +} + +static void zebra_vxlan_sg_do_deref(struct zebra_vrf *zvrf, + struct in_addr sip, struct in_addr mcast_grp) +{ + zebra_vxlan_sg_t *vxlan_sg; + struct prefix_sg sg; + + sg.family = AF_INET; + sg.prefixlen = IPV4_MAX_BYTELEN; + sg.src = sip; + sg.grp = mcast_grp; + vxlan_sg = zebra_vxlan_sg_find(zvrf, &sg); + if (!vxlan_sg) + return; + + if (vxlan_sg->ref_cnt) + --vxlan_sg->ref_cnt; + + if (!vxlan_sg->ref_cnt) + zebra_vxlan_sg_del(vxlan_sg); +} + +static zebra_vxlan_sg_t *zebra_vxlan_sg_do_ref(struct zebra_vrf *zvrf, + struct in_addr sip, struct in_addr mcast_grp) +{ + zebra_vxlan_sg_t *vxlan_sg; + struct prefix_sg sg; + + sg.family = AF_INET; + sg.prefixlen = IPV4_MAX_BYTELEN; + sg.src = sip; + sg.grp = mcast_grp; + vxlan_sg = zebra_vxlan_sg_add(zvrf, &sg); + if (vxlan_sg) + ++vxlan_sg->ref_cnt; + + return vxlan_sg; +} + +static void zebra_vxlan_sg_deref(struct in_addr local_vtep_ip, + struct in_addr mcast_grp) +{ + struct zebra_vrf *zvrf; + + if (!local_vtep_ip.s_addr || !mcast_grp.s_addr) + return; + + zvrf = vrf_info_lookup(VRF_DEFAULT); + if (!zvrf) + return; + + zebra_vxlan_sg_do_deref(zvrf, local_vtep_ip, mcast_grp); +} + +static void zebra_vxlan_sg_ref(struct in_addr local_vtep_ip, + struct in_addr mcast_grp) +{ + struct zebra_vrf *zvrf; + + if (!local_vtep_ip.s_addr || !mcast_grp.s_addr) + return; + + zvrf = vrf_info_lookup(VRF_DEFAULT); + if (!zvrf) + return; + zebra_vxlan_sg_do_ref(zvrf, local_vtep_ip, mcast_grp); +} + +static void zebra_vxlan_sg_cleanup(struct hash_backet *backet, void *arg) +{ + zebra_vxlan_sg_t *vxlan_sg = (zebra_vxlan_sg_t *)backet->data; + + zebra_vxlan_sg_del(vxlan_sg); +} diff --git a/zebra/zebra_vxlan.h b/zebra/zebra_vxlan.h index 2ff92970d..f752bdd69 100644 --- a/zebra/zebra_vxlan.h +++ b/zebra/zebra_vxlan.h @@ -60,9 +60,11 @@ is_vxlan_flooding_head_end(void) } /* VxLAN interface change flags of interest. */ -#define ZEBRA_VXLIF_LOCAL_IP_CHANGE 0x1 -#define ZEBRA_VXLIF_MASTER_CHANGE 0x2 -#define ZEBRA_VXLIF_VLAN_CHANGE 0x4 +#define ZEBRA_VXLIF_LOCAL_IP_CHANGE (1 << 0) +#define ZEBRA_VXLIF_MASTER_CHANGE (1 << 1) +#define ZEBRA_VXLIF_VLAN_CHANGE (1 << 2) +#define ZEBRA_VXLIF_MCAST_GRP_CHANGE (1 << 3) + #define VNI_STR_LEN 32 diff --git a/zebra/zebra_vxlan_private.h b/zebra/zebra_vxlan_private.h index 5081c08d1..9f945442b 100644 --- a/zebra/zebra_vxlan_private.h +++ b/zebra/zebra_vxlan_private.h @@ -52,6 +52,10 @@ struct zebra_vtep_t_ { /* Remote IP. */ /* NOTE: Can only be IPv4 right now. */ struct in_addr vtep_ip; + /* Flood mode (one of enum vxlan_flood_control) based on the PMSI + * tunnel type advertised by the remote VTEP + */ + int flood_control; /* Links. */ struct zebra_vtep_t_ *next; @@ -87,6 +91,9 @@ struct zebra_vni_t_ { /* Local IP */ struct in_addr local_vtep_ip; + /* PIM-SM MDT group for BUM flooding */ + struct in_addr mcast_grp; + /* tenant VRF, if any */ vrf_id_t vrf_id; @@ -427,4 +434,26 @@ struct nh_walk_ctx { } #endif +/* + * Multicast hash table. + * + * This table contains - + * 1. The (S, G) entries used for encapsulating and forwarding BUM traffic. + * S is the local VTEP-IP and G is a BUM mcast group address. + * 2. The (X, G) entries used for terminating a BUM flow. + * Multiple L2-VNIs can share the same MDT hence the need to maintain + * an aggregated table that pimd can consume without much + * re-interpretation. + */ +typedef struct zebra_vxlan_sg_ { + struct zebra_vrf *zvrf; + + struct prefix_sg sg; + char sg_str[PREFIX_SG_STR_LEN]; + + /* For SG - num of L2 VNIs using this entry for sending BUM traffic */ + /* For XG - num of SG using this as parent */ + uint32_t ref_cnt; +} zebra_vxlan_sg_t; + #endif /* _ZEBRA_VXLAN_PRIVATE_H */ diff --git a/zebra/zserv.c b/zebra/zserv.c index 80fdbefcd..df5f236c0 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -970,6 +970,8 @@ static void zebra_show_client_detail(struct vty *vty, struct zserv *client) client->v4_nh_watch_add_cnt, 0, client->v4_nh_watch_rem_cnt); vty_out(vty, "NHT v6 %-12d%-12d%-12d\n", client->v6_nh_watch_add_cnt, 0, client->v6_nh_watch_rem_cnt); + vty_out(vty, "VxLAN SG %-12d%-12d%-12d\n", client->vxlan_sg_add_cnt, + 0, client->vxlan_sg_del_cnt); vty_out(vty, "Interface Up Notifications: %d\n", client->ifup_cnt); vty_out(vty, "Interface Down Notifications: %d\n", client->ifdown_cnt); vty_out(vty, "VNI add notifications: %d\n", client->vniadd_cnt); diff --git a/zebra/zserv.h b/zebra/zserv.h index 86863d961..90fd19571 100644 --- a/zebra/zserv.h +++ b/zebra/zserv.h @@ -141,6 +141,8 @@ struct zserv { uint32_t v4_nh_watch_rem_cnt; uint32_t v6_nh_watch_add_cnt; uint32_t v6_nh_watch_rem_cnt; + uint32_t vxlan_sg_add_cnt; + uint32_t vxlan_sg_del_cnt; time_t nh_reg_time; time_t nh_dereg_time; |