From 6282e1247d3d880852b12f0ecae419899bbfa196 Mon Sep 17 00:00:00 2001 From: Chirag Shah Date: Wed, 7 Feb 2018 16:28:09 -0800 Subject: [PATCH 01/98] ospfd: fix ospf interface and neighbor json show ip ospf [vrf all] interface json and show ip ospf [vrf all] neighbor json to display objects in dictionary output rather in array list. Ticket:CM-19115,CM-19097 Signed-off-by: Chirag Shah --- ospfd/ospf_vty.c | 33 +++++++++++---------------------- 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c index 1276f5477c..d79f7724f3 100644 --- a/ospfd/ospf_vty.c +++ b/ospfd/ospf_vty.c @@ -3693,7 +3693,7 @@ static int show_ip_ospf_interface_common(struct vty *vty, struct ospf *ospf, { struct interface *ifp; struct vrf *vrf = vrf_lookup_by_id(ospf->vrf_id); - json_object *json_vrf = NULL, *json_intf_array = NULL; + json_object *json_vrf = NULL; json_object *json_interface_sub = NULL, *json_interface = NULL; if (use_json) { @@ -3701,7 +3701,7 @@ static int show_ip_ospf_interface_common(struct vty *vty, struct ospf *ospf, json_vrf = json_object_new_object(); else json_vrf = json; - json_intf_array = json_object_new_array(); + json_interface = json_object_new_object(); } if (ospf->instance) { @@ -3715,15 +3715,10 @@ static int show_ip_ospf_interface_common(struct vty *vty, struct ospf *ospf, ospf_show_vrf_name(ospf, vty, json_vrf, use_vrf); if (intf_name == NULL) { - if (use_json) - json_object_object_add(json_vrf, "interfaces", - json_intf_array); /* Show All Interfaces.*/ FOR_ALL_INTERFACES (vrf, ifp) { if (ospf_oi_count(ifp)) { if (use_json) { - json_interface = - json_object_new_object(); json_interface_sub = json_object_new_object(); } @@ -3732,14 +3727,15 @@ static int show_ip_ospf_interface_common(struct vty *vty, struct ospf *ospf, use_json); if (use_json) { - json_object_array_add(json_intf_array, - json_interface); json_object_object_add( json_interface, ifp->name, json_interface_sub); } } } + if (use_json) + json_object_object_add(json_vrf, "interfaces", + json_interface); } else { /* Interface name is specified. */ ifp = if_lookup_by_name(intf_name, ospf->vrf_id); @@ -3753,19 +3749,17 @@ static int show_ip_ospf_interface_common(struct vty *vty, struct ospf *ospf, if (use_json) { json_interface_sub = json_object_new_object(); json_interface = json_object_new_object(); - json_object_object_add(json_vrf, "interfaces", - json_intf_array); } show_ip_ospf_interface_sub( vty, ospf, ifp, json_interface_sub, use_json); if (use_json) { - json_object_array_add(json_intf_array, - json_interface); json_object_object_add(json_interface, ifp->name, json_interface_sub); + json_object_object_add(json_vrf, "interfaces", + json_interface); } } } @@ -4300,7 +4294,7 @@ static int show_ip_ospf_neighbor_common(struct vty *vty, struct ospf *ospf, { struct ospf_interface *oi; struct listnode *node; - json_object *json_vrf = NULL, *json_nbr_array = NULL; + json_object *json_vrf = NULL; json_object *json_nbr_sub = NULL; if (use_json) { @@ -4308,7 +4302,7 @@ static int show_ip_ospf_neighbor_common(struct vty *vty, struct ospf *ospf, json_vrf = json_object_new_object(); else json_vrf = json; - json_nbr_array = json_object_new_array(); + json_nbr_sub = json_object_new_object(); } if (ospf->instance) { @@ -4322,21 +4316,16 @@ static int show_ip_ospf_neighbor_common(struct vty *vty, struct ospf *ospf, ospf_show_vrf_name(ospf, vty, json_vrf, use_vrf); if (!use_json) show_ip_ospf_neighbour_header(vty); - else - json_object_object_add(json_vrf, "neighbors", - json_nbr_array); for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, node, oi)) { if (ospf_interface_neighbor_count(oi) == 0) continue; - if (use_json) { - json_nbr_sub = json_object_new_object(); - json_object_array_add(json_nbr_array, json_nbr_sub); - } show_ip_ospf_neighbor_sub(vty, oi, json_nbr_sub, use_json); } if (use_json) { + json_object_object_add(json_vrf, "neighbors", + json_nbr_sub); if (use_vrf) { if (ospf->vrf_id == VRF_DEFAULT) json_object_object_add(json, "default", From 19274fe8c7bf0ab80375344826e183b4356c9c7b Mon Sep 17 00:00:00 2001 From: Olivier Dugeon Date: Fri, 9 Feb 2018 12:13:07 +0100 Subject: [PATCH 02/98] ospfd: Fix ospfd crash - ospfd/ospf_opaque.c: Update issue #1652 by introducing a new function 'free_opaque_info_owner()' to clean list of callback owner and call this function in appropriate place where 'listdelete_and_null' is not used. - ospfd/ospf_packet.c: In case of crash, ospfd is not been able to flush LSA. In case of self Opaque LSA, when restarting, ospfd crash during the resynchronisation process with its neighbor due to an empty list of LSA to flood. Just add a control on the list count in 'ospf_ls_upd_queue_send()' to escape the function and avoid the problem. Signed-off-by: Olivier Dugeon --- ospfd/ospf_opaque.c | 36 +++++++++++++++++++++++++++++++++++- ospfd/ospf_packet.c | 4 ++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/ospfd/ospf_opaque.c b/ospfd/ospf_opaque.c index 1c586d252c..009fd997ea 100644 --- a/ospfd/ospf_opaque.c +++ b/ospfd/ospf_opaque.c @@ -75,6 +75,7 @@ static void ospf_opaque_funclist_init(void); static void ospf_opaque_funclist_term(void); static void free_opaque_info_per_type(void *val); static void free_opaque_info_per_id(void *val); +static void free_opaque_info_owner(void *val); static int ospf_opaque_lsa_install_hook(struct ospf_lsa *lsa); static int ospf_opaque_lsa_delete_hook(struct ospf_lsa *lsa); @@ -439,9 +440,11 @@ void ospf_delete_opaque_functab(u_char lsa_type, u_char opaque_type) if (functab->opaque_type == opaque_type) { /* Cleanup internal control information, if it * still remains. */ - if (functab->oipt != NULL) + if (functab->oipt != NULL) { free_opaque_info_per_type( functab->oipt); + free_opaque_info_owner(functab->oipt); + } /* Dequeue listnode entry from the list. */ listnode_delete(funclist, functab); @@ -572,6 +575,7 @@ register_opaque_info_per_type(struct ospf_opaque_functab *functab, top = ospf_lookup_by_vrf_id(new->vrf_id); if (new->area != NULL && (top = new->area->ospf) == NULL) { free_opaque_info_per_type((void *)oipt); + free_opaque_info_owner(oipt); oipt = NULL; goto out; /* This case may not exist. */ } @@ -583,6 +587,7 @@ register_opaque_info_per_type(struct ospf_opaque_functab *functab, "register_opaque_info_per_type: Unexpected LSA-type(%u)", new->data->type); free_opaque_info_per_type((void *)oipt); + free_opaque_info_owner(oipt); oipt = NULL; goto out; /* This case may not exist. */ } @@ -600,6 +605,35 @@ out: return oipt; } +/* Remove "oipt" from its owner's self-originated LSA list. */ +static void free_opaque_info_owner(void *val) +{ + struct opaque_info_per_type *oipt = (struct opaque_info_per_type *)val; + + switch (oipt->lsa_type) { + case OSPF_OPAQUE_LINK_LSA: { + struct ospf_interface *oi = + (struct ospf_interface *)(oipt->owner); + listnode_delete(oi->opaque_lsa_self, oipt); + break; + } + case OSPF_OPAQUE_AREA_LSA: { + struct ospf_area *area = (struct ospf_area *)(oipt->owner); + listnode_delete(area->opaque_lsa_self, oipt); + break; + } + case OSPF_OPAQUE_AS_LSA: { + struct ospf *top = (struct ospf *)(oipt->owner); + listnode_delete(top->opaque_lsa_self, oipt); + break; + } + default: + zlog_warn("free_opaque_info_owner: Unexpected LSA-type(%u)", + oipt->lsa_type); + break; /* This case may not exist. */ + } +} + static void free_opaque_info_per_type(void *val) { struct opaque_info_per_type *oipt = (struct opaque_info_per_type *)val; diff --git a/ospfd/ospf_packet.c b/ospfd/ospf_packet.c index 8670359610..881226683c 100644 --- a/ospfd/ospf_packet.c +++ b/ospfd/ospf_packet.c @@ -3897,6 +3897,10 @@ static void ospf_ls_upd_queue_send(struct ospf_interface *oi, zlog_debug("listcount = %d, [%s]dst %s", listcount(update), IF_NAME(oi), inet_ntoa(addr)); + /* Check that we have really something to process */ + if (listcount(update) == 0) + return; + op = ospf_ls_upd_packet_new(update, oi); /* Prepare OSPF common header. */ From 7a5e6e46da05a1d65c28ffdb2b6c9ade29887cfe Mon Sep 17 00:00:00 2001 From: dturlupov Date: Fri, 9 Feb 2018 17:10:22 +0300 Subject: [PATCH 03/98] ospfd: OSPF support the +/- in set metric <+/-metric> Signed-off-by: Dmitrii Turlupov --- ospfd/ospf_routemap.c | 47 +++++++++++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 15 deletions(-) diff --git a/ospfd/ospf_routemap.c b/ospfd/ospf_routemap.c index b7a47602d0..f2769c6f38 100644 --- a/ospfd/ospf_routemap.c +++ b/ospfd/ospf_routemap.c @@ -337,6 +337,7 @@ static struct route_map_rule_cmd route_match_tag_cmd = { }; struct ospf_metric { + enum { metric_increment, metric_decrement, metric_absolute } type; bool used; u_int32_t metric; }; @@ -356,8 +357,19 @@ static route_map_result_t route_set_metric(void *rule, struct prefix *prefix, ei = object; /* Set metric out value. */ - if (metric->used) + if (!metric->used) + return RMAP_OKAY; + if (metric->type == metric_increment) + ei->route_map_set.metric += metric->metric; + if (metric->type == metric_decrement) + ei->route_map_set.metric -= metric->metric; + if (metric->type == metric_absolute) ei->route_map_set.metric = metric->metric; + + if ((signed int)ei->route_map_set.metric < 1) + ei->route_map_set.metric = -1; + if (ei->route_map_set.metric > OSPF_LS_INFINITY) + ei->route_map_set.metric = OSPF_LS_INFINITY; } return RMAP_OKAY; } @@ -370,23 +382,28 @@ static void *route_set_metric_compile(const char *arg) metric = XCALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(u_int32_t)); metric->used = false; - /* OSPF doesn't support the +/- in - set metric <+/-metric> check - Ignore the +/- component */ - if (!all_digit(arg)) { - if ((arg[0] == '+' || arg[0] == '-') && all_digit(arg + 1)) { - zlog_warn("OSPF does not support 'set metric +/-'"); - arg++; - } else { - if (strmatch(arg, "+rtt") || strmatch(arg, "-rtt")) - zlog_warn( - "OSPF does not support 'set metric +rtt / -rtt'"); + if (all_digit(arg)) + metric->type = metric_absolute; - return metric; - } + if (strmatch(arg, "+rtt") || strmatch(arg, "-rtt")) { + zlog_warn("OSPF does not support 'set metric +rtt / -rtt'"); + return metric; } + + if ((arg[0] == '+') && all_digit(arg + 1)) { + metric->type = metric_increment; + arg++; + } + + if ((arg[0] == '-') && all_digit(arg + 1)) { + metric->type = metric_decrement; + arg++; + } + metric->metric = strtoul(arg, NULL, 10); - metric->used = true; + + if (metric->metric) + metric->used = true; return metric; } From c48d9f5f8545c4af3e4a2a4a58999cf62b705486 Mon Sep 17 00:00:00 2001 From: Mitesh Kanjariya Date: Tue, 6 Feb 2018 14:28:22 -0800 Subject: [PATCH 04/98] zebra, bgp: Support type-5 routes with asymmetric routing Asymmetric routing is an ideal choice when all VLANs are cfged on all leafs. It simplifies the routing configuration and eliminates potential need for advertising subnet routes. However, we need to reach the Internet or global destinations or to do subnet-based routing between PODs or DCs. This requires EVPN type-5 routes but those routes require L3 VNI configuration. This task is to support EVPN type-5 routes for prefix-based routing in conjunction with asymmetric routing within the POD/DC. It is done by providing an option to use the L3 VNI only for prefix routes, so that type-2 routes (host routes) will only use the L2 VNI. Signed-off-by: Mitesh Kanjariya --- bgpd/bgp_evpn.c | 66 +++++++++++++++++++++++++++++-------- bgpd/bgp_evpn.h | 3 +- bgpd/bgp_evpn_private.h | 21 +++++++++--- bgpd/bgp_evpn_vty.c | 8 +++++ bgpd/bgp_zebra.c | 10 ++++-- bgpd/bgpd.h | 1 + zebra/zebra_vrf.c | 5 ++- zebra/zebra_vty.c | 28 +++++++++++----- zebra/zebra_vxlan.c | 40 ++++++++++++++++------ zebra/zebra_vxlan.h | 3 +- zebra/zebra_vxlan_private.h | 3 ++ 11 files changed, 147 insertions(+), 41 deletions(-) diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index c7d5f8d111..756449cf1b 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -666,10 +666,11 @@ static void build_evpn_route_extcomm(struct bgpevpn *vpn, struct attr *attr, for (ALL_LIST_ELEMENTS(vpn->export_rtl, node, nnode, ecom)) attr->ecommunity = ecommunity_merge(attr->ecommunity, ecom); - /* Add the export RTs for L3VNI - currently only supported for IPV4 host - * routes + /* + * only attach l3-vni export rts for ipv4 address family and if we are + * advertising both the labels in type-2 routes */ - if (afi == AFI_IP) { + if (afi == AFI_IP && CHECK_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS)) { vrf_export_rtl = bgpevpn_get_vrf_export_rtl(vpn); if (vrf_export_rtl && !list_isempty(vrf_export_rtl)) { for (ALL_LIST_ELEMENTS(vrf_export_rtl, node, nnode, @@ -690,7 +691,12 @@ static void build_evpn_route_extcomm(struct bgpevpn *vpn, struct attr *attr, ecommunity_merge(attr->ecommunity, &ecom_sticky); } - if (afi == AFI_IP && !is_zero_mac(&attr->rmac)) { + /* + * only attach l3-vni rmac for ipv4 address family and if we are + * advertising both the labels in type-2 routes + */ + if (afi == AFI_IP && !is_zero_mac(&attr->rmac) && + CHECK_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS)) { memset(&ecom_rmac, 0, sizeof(ecom_rmac)); encode_rmac_extcomm(&eval_rmac, &attr->rmac); ecom_rmac.size = 1; @@ -1189,8 +1195,13 @@ static int update_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, /* The VNI goes into the 'label' field of the route */ vni2label(vpn->vni, &label[0]); - /* Type-2 routes may carry a second VNI - the L3-VNI */ - if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) { + + /* Type-2 routes may carry a second VNI - the L3-VNI. + * Only attach second label if we are advertising two labels for + * type-2 routes. + */ + if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE && + CHECK_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS)) { vni_t l3vni; l3vni = bgpevpn_get_l3vni(vpn); @@ -1209,6 +1220,24 @@ static int update_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, && !CHECK_FLAG(tmp_ri->flags, BGP_INFO_REMOVED)) route_change = 0; else { + /* + * The attributes have changed, type-2 routes needs to + * be advertised with right labels. + */ + vni2label(vpn->vni, &label[0]); + if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE && + CHECK_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS)) { + vni_t l3vni; + + l3vni = bgpevpn_get_l3vni(vpn); + if (l3vni) { + vni2label(l3vni, &label[1]); + num_labels++; + } + } + memcpy(&tmp_ri->extra->label, label, sizeof(label)); + tmp_ri->extra->num_labels = num_labels; + /* The attribute has changed. */ /* Add (or update) attribute to hash. */ attr_new = bgp_attr_intern(attr); @@ -4214,7 +4243,8 @@ static void link_l2vni_hash_to_l3vni(struct hash_backet *backet, int bgp_evpn_local_l3vni_add(vni_t l3vni, vrf_id_t vrf_id, struct ethaddr *rmac, - struct in_addr originator_ip) + struct in_addr originator_ip, + int filter) { struct bgp *bgp_vrf = NULL; /* bgp VRF instance */ struct bgp *bgp_def = NULL; /* default bgp instance */ @@ -4266,6 +4296,10 @@ int bgp_evpn_local_l3vni_add(vni_t l3vni, /* set the originator ip */ bgp_vrf->originator_ip = originator_ip; + /* set the right filter - are we using l3vni only for prefix routes? */ + if (filter) + SET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_L3VNI_PREFIX_ROUTES_ONLY); + /* auto derive RD/RT */ if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD)) evpn_auto_rt_import_add_for_vrf(bgp_vrf); @@ -4279,9 +4313,12 @@ int bgp_evpn_local_l3vni_add(vni_t l3vni, link_l2vni_hash_to_l3vni, bgp_vrf); - /* updates all corresponding local mac-ip routes */ - for (ALL_LIST_ELEMENTS_RO(bgp_vrf->l2vnis, node, vpn)) - update_routes_for_vni(bgp_def, vpn); + /* Only update all corresponding type-2 routes if we are advertising two + * labels along with type-2 routes + */ + if (!filter) + for (ALL_LIST_ELEMENTS_RO(bgp_vrf->l2vnis, node, vpn)) + update_routes_for_vni(bgp_def, vpn); /* advertise type-5 routes if needed */ update_advertise_vrf_routes(bgp_vrf); @@ -4340,9 +4377,12 @@ int bgp_evpn_local_l3vni_del(vni_t l3vni, } /* update all corresponding local mac-ip routes */ - for (ALL_LIST_ELEMENTS_RO(bgp_vrf->l2vnis, node, vpn)) - update_routes_for_vni(bgp_def, vpn); - + if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_L3VNI_PREFIX_ROUTES_ONLY)) { + for (ALL_LIST_ELEMENTS_RO(bgp_vrf->l2vnis, node, vpn)) { + UNSET_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS); + update_routes_for_vni(bgp_def, vpn); + } + } /* Delete the instance if it was autocreated */ if (CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_AUTO)) diff --git a/bgpd/bgp_evpn.h b/bgpd/bgp_evpn.h index a8dcbc112b..d8d92618f6 100644 --- a/bgpd/bgp_evpn.h +++ b/bgpd/bgp_evpn.h @@ -91,7 +91,8 @@ extern int bgp_evpn_local_macip_add(struct bgp *bgp, vni_t vni, u_char flags); extern int bgp_evpn_local_l3vni_add(vni_t vni, vrf_id_t vrf_id, struct ethaddr *rmac, - struct in_addr originator_ip); + struct in_addr originator_ip, + int filter); 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, diff --git a/bgpd/bgp_evpn_private.h b/bgpd/bgp_evpn_private.h index cc0ec82344..1dbc1f25f0 100644 --- a/bgpd/bgp_evpn_private.h +++ b/bgpd/bgp_evpn_private.h @@ -61,6 +61,8 @@ struct bgpevpn { #define VNI_FLAG_RD_CFGD 0x4 /* RD is user configured. */ #define VNI_FLAG_IMPRT_CFGD 0x8 /* Import RT is user configured */ #define VNI_FLAG_EXPRT_CFGD 0x10 /* Export RT is user configured */ +#define VNI_FLAG_USE_TWO_LABELS 0x20 /* Attach both L2-VNI and L3-VNI if + needed for this VPN */ /* Flag to indicate if we are advertising the g/w mac ip for this VNI*/ u_int8_t advertise_gw_macip; @@ -179,9 +181,13 @@ static inline void bgpevpn_unlink_from_l3vni(struct bgpevpn *vpn) struct bgp *bgp_vrf = NULL; bgp_vrf = bgp_lookup_by_vrf_id(vpn->tenant_vrf_id); - if (!bgp_vrf || !bgp_vrf->l2vnis) + if (!bgp_vrf) return; - listnode_delete(bgp_vrf->l2vnis, vpn); + + UNSET_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS); + + if (bgp_vrf->l2vnis) + listnode_delete(bgp_vrf->l2vnis, vpn); } static inline void bgpevpn_link_to_l3vni(struct bgpevpn *vpn) @@ -189,9 +195,16 @@ static inline void bgpevpn_link_to_l3vni(struct bgpevpn *vpn) struct bgp *bgp_vrf = NULL; bgp_vrf = bgp_lookup_by_vrf_id(vpn->tenant_vrf_id); - if (!bgp_vrf || !bgp_vrf->l2vnis) + if (!bgp_vrf) return; - listnode_add_sort(bgp_vrf->l2vnis, vpn); + + /* check if we are advertising two labels for this vpn */ + if (!CHECK_FLAG(bgp_vrf->vrf_flags, + BGP_VRF_L3VNI_PREFIX_ROUTES_ONLY)) + SET_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS); + + if (bgp_vrf->l2vnis) + listnode_add_sort(bgp_vrf->l2vnis, vpn); } static inline int is_vni_configured(struct bgpevpn *vpn) diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c index bd42ccdecf..e9e0c8df1b 100644 --- a/bgpd/bgp_evpn_vty.c +++ b/bgpd/bgp_evpn_vty.c @@ -3897,6 +3897,10 @@ DEFUN (show_bgp_vrf_l3vni_info, vty_out(vty, " L3-VNI: %u\n", bgp->l3vni); vty_out(vty, " Rmac: %s\n", prefix_mac2str(&bgp->rmac, buf, sizeof(buf))); + vty_out(vty, " VNI Filter: %s\n", + CHECK_FLAG(bgp->vrf_flags, + BGP_VRF_L3VNI_PREFIX_ROUTES_ONLY) ? + "prefix-routes-only" : "none"); vty_out(vty, " L2-VNI List:\n"); vty_out(vty, " "); for (ALL_LIST_ELEMENTS_RO(bgp->l2vnis, node, vpn)) @@ -3922,6 +3926,10 @@ DEFUN (show_bgp_vrf_l3vni_info, json_object_string_add(json, "rmac", prefix_mac2str(&bgp->rmac, buf, sizeof(buf))); + json_object_string_add(json, "vniFilter", + CHECK_FLAG(bgp->vrf_flags, + BGP_VRF_L3VNI_PREFIX_ROUTES_ONLY) + ? "prefix-routes-only" : "none"); /* list of l2vnis */ for (ALL_LIST_ELEMENTS_RO(bgp->l2vnis, node, vpn)) json_object_array_add(json_vnis, diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index e0bd74a206..aaf3ba1252 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -1731,6 +1731,7 @@ static void bgp_zebra_connected(struct zclient *zclient) static int bgp_zebra_process_local_l3vni(int cmd, struct zclient *zclient, zebra_size_t length, vrf_id_t vrf_id) { + int filter = 0; char buf[ETHER_ADDR_STRLEN]; vni_t l3vni = 0; struct ethaddr rmac; @@ -1744,17 +1745,20 @@ static int bgp_zebra_process_local_l3vni(int cmd, struct zclient *zclient, if (cmd == ZEBRA_L3VNI_ADD) { stream_get(&rmac, s, sizeof(struct ethaddr)); originator_ip.s_addr = stream_get_ipv4(s); + stream_get(&filter, s, sizeof(int)); } if (BGP_DEBUG(zebra, ZEBRA)) - zlog_debug("Rx L3-VNI %s VRF %s VNI %u RMAC %s", + zlog_debug("Rx L3-VNI %s VRF %s VNI %u RMAC %s filter %s", (cmd == ZEBRA_L3VNI_ADD) ? "add" : "del", vrf_id_to_name(vrf_id), l3vni, - prefix_mac2str(&rmac, buf, sizeof(buf))); + prefix_mac2str(&rmac, buf, sizeof(buf)), + filter ? "prefix-routes-only" : "none"); if (cmd == ZEBRA_L3VNI_ADD) - bgp_evpn_local_l3vni_add(l3vni, vrf_id, &rmac, originator_ip); + bgp_evpn_local_l3vni_add(l3vni, vrf_id, &rmac, originator_ip, + filter); else bgp_evpn_local_l3vni_del(l3vni, vrf_id); diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index c4ac4b0adb..afb7126ec5 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -435,6 +435,7 @@ struct bgp { #define BGP_VRF_IMPORT_RT_CFGD (1 << 3) #define BGP_VRF_EXPORT_RT_CFGD (1 << 4) #define BGP_VRF_RD_CFGD (1 << 5) +#define BGP_VRF_L3VNI_PREFIX_ROUTES_ONLY (1 << 6) /* unique ID for auto derivation of RD for this vrf */ uint16_t vrf_rd_id; diff --git a/zebra/zebra_vrf.c b/zebra/zebra_vrf.c index b9b3048486..65fa7ecf80 100644 --- a/zebra/zebra_vrf.c +++ b/zebra/zebra_vrf.c @@ -598,7 +598,10 @@ static int vrf_config_write(struct vty *vty) if (vrf_is_user_cfged(vrf)) { vty_out(vty, "vrf %s\n", zvrf_name(zvrf)); if (zvrf->l3vni) - vty_out(vty, " vni %u\n", zvrf->l3vni); + vty_out(vty, " vni %u%s\n", + zvrf->l3vni, + is_l3vni_for_prefix_routes_only(zvrf->l3vni) ? + " prefix-routes-only" :""); vty_out(vty, "!\n"); } diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index 93397afa79..c2a620c30d 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -2338,20 +2338,26 @@ DEFUN (show_vrf, DEFUN (default_vrf_vni_mapping, default_vrf_vni_mapping_cmd, - "vni " CMD_VNI_RANGE, + "vni " CMD_VNI_RANGE "[prefix-routes-only]", "VNI corresponding to the DEFAULT VRF\n" - "VNI-ID\n") + "VNI-ID\n" + "Prefix routes only \n") { int ret = 0; char err[ERR_STR_SZ]; struct zebra_vrf *zvrf = NULL; vni_t vni = strtoul(argv[1]->arg, NULL, 10); + int filter = 0; zvrf = vrf_info_lookup(VRF_DEFAULT); if (!zvrf) return CMD_WARNING; - ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ, 1); + if (argc == 3) + filter = 1; + + ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ, + filter, 1); if (ret != 0) { vty_out(vty, "%s\n", err); return CMD_WARNING; @@ -2376,7 +2382,7 @@ DEFUN (no_default_vrf_vni_mapping, if (!zvrf) return CMD_WARNING; - ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ, 0); + ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ, 0, 0); if (ret != 0) { vty_out(vty, "%s\n", err); return CMD_WARNING; @@ -2387,11 +2393,13 @@ DEFUN (no_default_vrf_vni_mapping, DEFUN (vrf_vni_mapping, vrf_vni_mapping_cmd, - "vni " CMD_VNI_RANGE, + "vni " CMD_VNI_RANGE "[prefix-routes-only]", "VNI corresponding to tenant VRF\n" - "VNI-ID\n") + "VNI-ID\n" + "prefix-routes-only\n") { int ret = 0; + int filter = 0; ZEBRA_DECLVAR_CONTEXT(vrf, zvrf); vni_t vni = strtoul(argv[1]->arg, NULL, 10); @@ -2400,9 +2408,13 @@ DEFUN (vrf_vni_mapping, assert(vrf); assert(zvrf); + if (argc == 3) + filter = 1; + /* Mark as having FRR configuration */ vrf_set_user_cfged(vrf); - ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ, 1); + ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ, + filter, 1); if (ret != 0) { vty_out(vty, "%s\n", err); return CMD_WARNING; @@ -2427,7 +2439,7 @@ DEFUN (no_vrf_vni_mapping, assert(vrf); assert(zvrf); - ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ, 0); + ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ, 0, 0); if (ret != 0) { vty_out(vty, "%s\n", err); return CMD_WARNING; diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index c9cc556a44..20b9e94288 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -928,6 +928,9 @@ static void zl3vni_print(zebra_l3vni_t *zl3vni, void **ctx) zl3vni_svi_if_name(zl3vni)); vty_out(vty, " State: %s\n", zl3vni_state2str(zl3vni)); + vty_out(vty, " VNI Filter: %s\n", + CHECK_FLAG(zl3vni->filter, PREFIX_ROUTES_ONLY) ? + "prefix-routes-only" : "none"); vty_out(vty, " Router MAC: %s\n", zl3vni_rmac2str(zl3vni, buf, sizeof(buf))); vty_out(vty, " L2 VNIs: "); @@ -951,6 +954,10 @@ static void zl3vni_print(zebra_l3vni_t *zl3vni, void **ctx) json_object_string_add(json, "routerMac", zl3vni_rmac2str(zl3vni, buf, sizeof(buf))); + json_object_string_add(json, "vniFilter", + CHECK_FLAG(zl3vni->filter, + PREFIX_ROUTES_ONLY) ? + "prefix-routes-only" : "none"); for (ALL_LIST_ELEMENTS(zl3vni->l2vnis, node, nnode, zvni)) { json_object_array_add(json_vni_list, json_object_new_int(zvni->vni)); @@ -3613,15 +3620,19 @@ static int zl3vni_send_add_to_client(zebra_l3vni_t *zl3vni) stream_putl(s, zl3vni->vni); stream_put(s, &rmac, sizeof(struct ethaddr)); stream_put_in_addr(s, &zl3vni->local_vtep_ip); + stream_put(s, &zl3vni->filter, sizeof(int)); /* Write packet size. */ stream_putw_at(s, 0, stream_get_endp(s)); if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("Send L3_VNI_ADD %u VRF %s RMAC %s local-ip %s to %s", + zlog_debug( + "Send L3_VNI_ADD %u VRF %s RMAC %s local-ip %s filter %s to %s", zl3vni->vni, vrf_id_to_name(zl3vni_vrf_id(zl3vni)), prefix_mac2str(&rmac, buf, sizeof(buf)), inet_ntoa(zl3vni->local_vtep_ip), + CHECK_FLAG(zl3vni->filter, PREFIX_ROUTES_ONLY) ? + "prefix-routes-only" : "none", zebra_route_string(client->proto)); client->l3vniadd_cnt++; @@ -3666,10 +3677,6 @@ static void zebra_vxlan_process_l3vni_oper_up(zebra_l3vni_t *zl3vni) if (!zl3vni) return; - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("L3-VNI %u is UP - send add to BGP", - zl3vni->vni); - /* send l3vni add to BGP */ zl3vni_send_add_to_client(zl3vni); } @@ -3679,10 +3686,6 @@ static void zebra_vxlan_process_l3vni_oper_down(zebra_l3vni_t *zl3vni) if (!zl3vni) return; - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("L3-VNI %u is Down - Send del to BGP", - zl3vni->vni); - /* send l3-vni del to BGP*/ zl3vni_send_del_to_client(zl3vni); } @@ -3835,6 +3838,17 @@ static int zebra_vxlan_readd_remote_rmac(zebra_l3vni_t *zl3vni, /* Public functions */ +int is_l3vni_for_prefix_routes_only(vni_t vni) +{ + zebra_l3vni_t *zl3vni = NULL; + + zl3vni = zl3vni_lookup(vni); + if (!zl3vni) + return 0; + + return CHECK_FLAG(zl3vni->filter, PREFIX_ROUTES_ONLY) ? 1 : 0; +} + /* handle evpn route in vrf table */ void zebra_vxlan_evpn_vrf_route_add(vrf_id_t vrf_id, struct ethaddr *rmac, @@ -6432,7 +6446,7 @@ int zebra_vxlan_if_add(struct interface *ifp) int zebra_vxlan_process_vrf_vni_cmd(struct zebra_vrf *zvrf, vni_t vni, char *err, int err_str_sz, - int add) + int filter, int add) { zebra_l3vni_t *zl3vni = NULL; struct zebra_vrf *zvrf_default = NULL; @@ -6477,6 +6491,12 @@ int zebra_vxlan_process_vrf_vni_cmd(struct zebra_vrf *zvrf, /* associate the vrf with vni */ zvrf->l3vni = vni; + /* set the filter in l3vni to denote if we are using l3vni only + * for prefix routes + */ + if (filter) + SET_FLAG(zl3vni->filter, PREFIX_ROUTES_ONLY); + /* associate with vxlan-intf; * we need to associate with the vxlan-intf first */ diff --git a/zebra/zebra_vxlan.h b/zebra/zebra_vxlan.h index d9801a8b60..df9b07db60 100644 --- a/zebra/zebra_vxlan.h +++ b/zebra/zebra_vxlan.h @@ -52,6 +52,7 @@ is_evpn_enabled() #define VNI_STR_LEN 32 +extern int is_l3vni_for_prefix_routes_only(vni_t vni); extern ifindex_t get_l3vni_svi_ifindex(vrf_id_t vrf_id); extern int zebra_vxlan_vrf_delete(struct zebra_vrf *zvrf); extern int zebra_vxlan_vrf_enable(struct zebra_vrf *zvrf); @@ -154,7 +155,7 @@ extern int zebra_vxlan_advertise_all_vni(struct zserv *client, struct zebra_vrf *zvrf); extern int zebra_vxlan_process_vrf_vni_cmd(struct zebra_vrf *zvrf, vni_t vni, char *err, - int err_str_sz, int add); + int err_str_sz, int filter, int add); extern void zebra_vxlan_init_tables(struct zebra_vrf *zvrf); extern void zebra_vxlan_close_tables(struct zebra_vrf *); extern void zebra_vxlan_cleanup_tables(struct zebra_vrf *); diff --git a/zebra/zebra_vxlan_private.h b/zebra/zebra_vxlan_private.h index 8d34b3e2f1..e8de25cefd 100644 --- a/zebra/zebra_vxlan_private.h +++ b/zebra/zebra_vxlan_private.h @@ -101,6 +101,9 @@ struct zebra_l3vni_t_ { /* vrf_id */ vrf_id_t vrf_id; + uint32_t filter; +#define PREFIX_ROUTES_ONLY (1 << 0) /* l3-vni used for prefix routes only */ + /* Local IP */ struct in_addr local_vtep_ip; From f89a449b5f109668bfcbc823db855244dfa7e170 Mon Sep 17 00:00:00 2001 From: Chirag Shah Date: Sun, 11 Feb 2018 20:17:11 -0800 Subject: [PATCH 05/98] ospf6d: Fix distance command Reset prevoiusly configured distance command options if user enters new (different) parameters. Ticket:CM-19635 Reviewed By: Testing Done: R1(config-ospf6)# distance ospf6 intra-area 55 external 55 R1#show running-config ospf6d router ospf6 distance ospf6 intra-area 55 external 55 R1(config-ospf6)# distance ospf6 inter-area 55 R1#show running-config ospf6d router ospf6 distance ospf6 inter-area 55 Signed-off-by: Chirag Shah --- ospf6d/ospf6_top.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ospf6d/ospf6_top.c b/ospf6d/ospf6_top.c index 749873bcf8..450be5d448 100644 --- a/ospf6d/ospf6_top.c +++ b/ospf6d/ospf6_top.c @@ -521,6 +521,10 @@ DEFUN (ospf6_distance_ospf6, VTY_DECLVAR_CONTEXT(ospf6, o); int idx = 0; + o->distance_intra = 0; + o->distance_inter = 0; + o->distance_external = 0; + if (argv_find(argv, argc, "intra-area", &idx)) o->distance_intra = atoi(argv[idx + 1]->arg); idx = 0; From 926b88f00d0391d1356753fcbdb1983bff2ef79d Mon Sep 17 00:00:00 2001 From: Chirag Shah Date: Sun, 11 Feb 2018 20:27:43 -0800 Subject: [PATCH 06/98] ospfd: Fix distance command Reset prevoiusly configured distance command options if user enters new (different) parameters. Ticket:CM-19635 Testing Done: R1(config-router)# distance ospf intra-area 45 external 45 R1# show running-config ospfd router ospf distance ospf intra-area 45 external 45 R1(config-router)# distance ospf inter-area 45 R1# show running-config ospfd router ospf distance ospf inter-area 45 Signed-off-by: Chirag Shah --- ospfd/ospf_vty.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c index 1276f5477c..09350b45a8 100644 --- a/ospfd/ospf_vty.c +++ b/ospfd/ospf_vty.c @@ -8558,6 +8558,10 @@ DEFUN (ospf_distance_ospf, VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); int idx = 0; + ospf->distance_intra = 0; + ospf->distance_inter = 0; + ospf->distance_external = 0; + if (argv_find(argv, argc, "intra-area", &idx)) ospf->distance_intra = atoi(argv[idx + 1]->arg); idx = 0; From 53c84f7800eebf1406ff9c5eb749900e4b425082 Mon Sep 17 00:00:00 2001 From: Mitesh Kanjariya Date: Fri, 9 Feb 2018 01:09:20 -0800 Subject: [PATCH 07/98] bgpd: Policy to control which RIB routes are injected into EVPN FRR/CL provides the means for injecting regular (IPv4) routes from the BGP RIB into EVPN as type-5 routes. This needs to be enhanced to allow selective injection. This can be achieved by adding a route-map option for the "advertise ipv4/ipv6 unicast" command. Signed-off-by: Mitesh Kanjariya --- bgpd/bgp_evpn.c | 13 +++++++++ bgpd/bgp_evpn_vty.c | 70 ++++++++++++++++++++++++++++++++++++--------- bgpd/bgp_routemap.c | 13 +++++++++ bgpd/bgpd.h | 3 ++ 4 files changed, 86 insertions(+), 13 deletions(-) diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index c7d5f8d111..ec8e2907a6 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -3275,6 +3275,19 @@ void bgp_evpn_advertise_type5_routes(struct bgp *bgp_vrf, */ for (ri = rn->info; ri; ri = ri->next) { if (CHECK_FLAG(ri->flags, BGP_INFO_SELECTED)) { + + /* apply the route-map */ + if (bgp_vrf->adv_cmd_rmap[afi][safi].map) { + int ret = 0; + + ret = + route_map_apply( + bgp_vrf->adv_cmd_rmap[afi][safi].map, + &rn->p, RMAP_BGP, ri); + if (ret == RMAP_DENYMATCH) + continue; + } + bgp_evpn_advertise_type5_route(bgp_vrf, &rn->p, ri->attr, afi, safi); diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c index bd42ccdecf..e1e11e44ac 100644 --- a/bgpd/bgp_evpn_vty.c +++ b/bgpd/bgp_evpn_vty.c @@ -2723,19 +2723,34 @@ DEFUN (no_bgp_evpn_advertise_vni_subnet, DEFUN (bgp_evpn_advertise_type5, bgp_evpn_advertise_type5_cmd, - "advertise " BGP_AFI_CMD_STR "" BGP_SAFI_CMD_STR, + "advertise " BGP_AFI_CMD_STR "" BGP_SAFI_CMD_STR " [route-map WORD]", "Advertise prefix routes\n" BGP_AFI_HELP_STR - BGP_SAFI_HELP_STR) + BGP_SAFI_HELP_STR + "route-map for filtering specific routes\n" + "Name of the route map\n") { struct bgp *bgp_vrf = VTY_GET_CONTEXT(bgp); /* bgp vrf instance */ int idx_afi = 0; int idx_safi = 0; + int idx_rmap = 0; afi_t afi = 0; safi_t safi = 0; + int ret = 0; + int rmap_changed = 0; argv_find_and_parse_afi(argv, argc, &idx_afi, &afi); argv_find_and_parse_safi(argv, argc, &idx_safi, &safi); + ret = argv_find(argv, argc, "route-map", &idx_rmap); + if (ret) { + if (!bgp_vrf->adv_cmd_rmap[afi][safi].name) + rmap_changed = 1; + else if (strcmp(argv[idx_rmap + 1]->arg, + bgp_vrf->adv_cmd_rmap[afi][safi].name) != 0) + rmap_changed = 1; + } else if (bgp_vrf->adv_cmd_rmap[afi][safi].name) { + rmap_changed = 1; + } if (!(afi == AFI_IP) || (afi == AFI_IP6)) { vty_out(vty, @@ -2754,24 +2769,44 @@ DEFUN (bgp_evpn_advertise_type5, /* if we are already advertising ipv4 prefix as type-5 * nothing to do */ - if (!CHECK_FLAG(bgp_vrf->vrf_flags, - BGP_VRF_ADVERTISE_IPV4_IN_EVPN)) { - SET_FLAG(bgp_vrf->vrf_flags, - BGP_VRF_ADVERTISE_IPV4_IN_EVPN); - bgp_evpn_advertise_type5_routes(bgp_vrf, afi, safi); - } + if (!rmap_changed && CHECK_FLAG(bgp_vrf->vrf_flags, + BGP_VRF_ADVERTISE_IPV4_IN_EVPN)) + return CMD_WARNING; + SET_FLAG(bgp_vrf->vrf_flags, + BGP_VRF_ADVERTISE_IPV4_IN_EVPN); } else { /* if we are already advertising ipv6 prefix as type-5 * nothing to do */ - if (!CHECK_FLAG(bgp_vrf->vrf_flags, - BGP_VRF_ADVERTISE_IPV6_IN_EVPN)) { - SET_FLAG(bgp_vrf->vrf_flags, - BGP_VRF_ADVERTISE_IPV6_IN_EVPN); - bgp_evpn_advertise_type5_routes(bgp_vrf, afi, safi); + if (!rmap_changed && CHECK_FLAG(bgp_vrf->vrf_flags, + BGP_VRF_ADVERTISE_IPV6_IN_EVPN)) + return CMD_WARNING; + SET_FLAG(bgp_vrf->vrf_flags, + BGP_VRF_ADVERTISE_IPV6_IN_EVPN); + } + + if (rmap_changed) { + bgp_evpn_withdraw_type5_routes(bgp_vrf, afi, safi); + if (bgp_vrf->adv_cmd_rmap[afi][safi].name) { + XFREE(MTYPE_ROUTE_MAP_NAME, + bgp_vrf->adv_cmd_rmap[afi][safi].name); + bgp_vrf->adv_cmd_rmap[afi][safi].name = NULL; + bgp_vrf->adv_cmd_rmap[afi][safi].map = NULL; } } + + /* set the route-map for advertise command */ + if (ret && argv[idx_rmap + 1]->arg) { + bgp_vrf->adv_cmd_rmap[afi][safi].name = + XSTRDUP(MTYPE_ROUTE_MAP_NAME, + argv[idx_rmap + 1]->arg); + bgp_vrf->adv_cmd_rmap[afi][safi].map = + route_map_lookup_by_name(argv[idx_rmap + 1]->arg); + } + + /* advertise type-5 routes */ + bgp_evpn_advertise_type5_routes(bgp_vrf, afi, safi); return CMD_SUCCESS; } @@ -2827,6 +2862,15 @@ DEFUN (no_bgp_evpn_advertise_type5, BGP_VRF_ADVERTISE_IPV6_IN_EVPN); } } + + /* clear the route-map information for advertise ipv4/ipv6 unicast */ + if (bgp_vrf->adv_cmd_rmap[afi][safi].name) { + XFREE(MTYPE_ROUTE_MAP_NAME, + bgp_vrf->adv_cmd_rmap[afi][safi].name); + bgp_vrf->adv_cmd_rmap[afi][safi].name = NULL; + bgp_vrf->adv_cmd_rmap[afi][safi].map = NULL; + } + return CMD_SUCCESS; } diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index 06e53e2daf..4d5624d3b0 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -3076,6 +3076,19 @@ static void bgp_route_map_process_update(struct bgp *bgp, const char *rmap_name, } } } + + /* for type5 command route-maps */ + FOREACH_AFI_SAFI (afi, safi) { + if (bgp->adv_cmd_rmap[afi][safi].name && + strcmp(rmap_name, bgp->adv_cmd_rmap[afi][safi].name) == 0) { + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug( + "Processing route_map %s update on advertise type5 route command", + rmap_name); + bgp_evpn_withdraw_type5_routes(bgp, afi, safi); + bgp_evpn_advertise_type5_routes(bgp, afi, safi); + } + } } static int bgp_route_map_process_update_cb(char *rmap_name) diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 51709f9ed0..220b6d989e 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -452,6 +452,9 @@ struct bgp { /* list of corresponding l2vnis (struct bgpevpn) */ struct list *l2vnis; + /* route map for advertise ipv4/ipv6 unicast (type-5 routes) */ + struct bgp_rmap adv_cmd_rmap[AFI_MAX][SAFI_MAX]; + QOBJ_FIELDS }; DECLARE_QOBJ_TYPE(bgp) From cf29dab3b545f4d8c2286599d2f74426f365911f Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Tue, 13 Feb 2018 23:34:52 -0500 Subject: [PATCH 08/98] ospf6d: Fix a possible deref by null found in SA There exists a possibility that rtr_lsa may be null. Add an assert that shows we actually expect it to be non-null at this point in time going forward. Signed-off-by: Donald Sharp --- ospf6d/ospf6_spf.c | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/ospf6d/ospf6_spf.c b/ospf6d/ospf6_spf.c index 17ce1771e2..29ba1bcec7 100644 --- a/ospf6d/ospf6_spf.c +++ b/ospf6d/ospf6_spf.c @@ -1029,18 +1029,21 @@ struct ospf6_lsa *ospf6_create_single_router_lsa(struct ospf6_area *area, /* Fill Larger LSA Payload */ end = ospf6_lsdb_head(lsdb, 2, type, adv_router, &rtr_lsa); - if (rtr_lsa) { - if (!OSPF6_LSA_IS_MAXAGE(rtr_lsa)) { - /* Append first Link State ID LSA */ - lsa_header = (struct ospf6_lsa_header *)rtr_lsa->header; - memcpy(new_header, lsa_header, - ntohs(lsa_header->length)); - /* Assign new lsa length as aggregated length. */ - ((struct ospf6_lsa_header *)new_header)->length = - htons(total_lsa_length); - new_header += ntohs(lsa_header->length); - num_lsa--; - } + + /* + * We assume at this point in time that rtr_lsa is + * a valid pointer. + */ + assert(rtr_lsa); + if (!OSPF6_LSA_IS_MAXAGE(rtr_lsa)) { + /* Append first Link State ID LSA */ + lsa_header = (struct ospf6_lsa_header *)rtr_lsa->header; + memcpy(new_header, lsa_header, ntohs(lsa_header->length)); + /* Assign new lsa length as aggregated length. */ + ((struct ospf6_lsa_header *)new_header)->length = + htons(total_lsa_length); + new_header += ntohs(lsa_header->length); + num_lsa--; } /* Print LSA Name */ From 9b50aa1fd3f38eb34d77b9ae7e848b35bc814bfc Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Tue, 13 Feb 2018 23:37:08 -0500 Subject: [PATCH 09/98] ospfd: Fix some new SA issues found by coverity Fix a || && mixup. Add an assert for area to show we expect it to be non-null going forward. When memory is allocated if it fails we abort then no need to check for null. Signed-off-by: Donald Sharp --- ospfd/ospf_ext.c | 2 +- ospfd/ospf_flood.c | 1 + ospfd/ospf_sr.c | 10 +--------- 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/ospfd/ospf_ext.c b/ospfd/ospf_ext.c index d42476b6d8..d7faf4b0de 100644 --- a/ospfd/ospf_ext.c +++ b/ospfd/ospf_ext.c @@ -175,7 +175,7 @@ void ospf_ext_term(void) { if ((OspfEXT.scope != OSPF_OPAQUE_AREA_LSA) - || (OspfEXT.scope != OSPF_OPAQUE_AS_LSA)) + && (OspfEXT.scope != OSPF_OPAQUE_AS_LSA)) zlog_warn( "EXT: Unable to unregister Extended Prefix " "Opaque LSA functions: Wrong scope!"); diff --git a/ospfd/ospf_flood.c b/ospfd/ospf_flood.c index 36b6d5143d..7ad9cf9f2f 100644 --- a/ospfd/ospf_flood.c +++ b/ospfd/ospf_flood.c @@ -563,6 +563,7 @@ int ospf_flood_through_area(struct ospf_area *area, struct ospf_neighbor *inbr, struct ospf_interface *oi; int lsa_ack_flag = 0; + assert(area); /* All other types are specific to a single area (Area A). The eligible interfaces are all those interfaces attaching to the Area A. If Area A is the backbone, this includes all the virtual diff --git a/ospfd/ospf_sr.c b/ospfd/ospf_sr.c index c6649a7a04..9827eac71b 100644 --- a/ospfd/ospf_sr.c +++ b/ospfd/ospf_sr.c @@ -149,14 +149,6 @@ static struct sr_node *sr_node_new(struct in_addr *rid) new->ext_link->del = del_sr_link; new->ext_prefix->del = del_sr_pref; - /* Check if list are correctly created */ - if (new->ext_link == NULL || new->ext_prefix == NULL) { - list_delete_original(new->ext_link); - list_delete_original(new->ext_prefix); - XFREE(MTYPE_OSPF_SR_PARAMS, new); - return NULL; - } - IPV4_ADDR_COPY(&new->adv_router, rid); new->neighbor = NULL; new->instance = 0; @@ -440,7 +432,7 @@ static struct ospf_path *get_nexthop_by_addr(struct ospf *top, struct route_node *rn; /* Sanity Check */ - if ((top == NULL) && (top->new_table)) + if (top == NULL) return NULL; if (IS_DEBUG_OSPF_SR) From 6447dbb3726089659be4c891be8b2db39031791a Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Tue, 13 Feb 2018 23:39:09 -0500 Subject: [PATCH 10/98] zebra: Clean up some SA issues found by new code 1) Add asserts in a couple of spots to show we never expect prefix to be bad. 2) Fix some bfd code where out_ctxt will always be NULL. Signed-off-by: Donald Sharp --- zebra/zebra_ptm.c | 9 +++++++-- zebra/zebra_vty.c | 12 ++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/zebra/zebra_ptm.c b/zebra/zebra_ptm.c index 7f5fd472f1..187c2594ad 100644 --- a/zebra/zebra_ptm.c +++ b/zebra/zebra_ptm.c @@ -1007,8 +1007,13 @@ int zebra_ptm_bfd_client_register(struct zserv *client, return 0; stream_failure: - if (out_ctxt) - ptm_lib_cleanup_msg(ptm_hdl, out_ctxt); + /* + * IF we ever add more STREAM_GETXXX functions after the out_ctxt + * is allocated then we need to add this code back in + * + * if (out_ctxt) + * ptm_lib_cleanup_msg(ptm_hdl, out_ctxt); + */ return 0; } diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index f5ad9c1c8a..269244f768 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -458,6 +458,12 @@ DEFPY(ip_route_blackhole_vrf, VTY_DECLVAR_CONTEXT(vrf, vrf); struct zebra_vrf *zvrf = vrf->info; + /* + * Coverity is complaining that prefix could + * be dereferenced, but we know that prefix will + * valid. Add an assert to make it happy + */ + assert(prefix); return zebra_static_route_leak(vty, zvrf, zvrf, AFI_IP, SAFI_UNICAST, no, prefix, mask_str, NULL, NULL, NULL, flag, @@ -2014,6 +2020,12 @@ DEFPY(ipv6_route_blackhole_vrf, VTY_DECLVAR_CONTEXT(vrf, vrf); struct zebra_vrf *zvrf = vrf->info; + /* + * Coverity is complaining that prefix could + * be dereferenced, but we know that prefix will + * valid. Add an assert to make it happy + */ + assert(prefix); return zebra_static_route_leak(vty, zvrf, zvrf, AFI_IP6, SAFI_UNICAST, no, prefix_str, NULL, from_str, NULL, NULL, flag, From 7d061b3cb120c35c5b580bbbcfb1ec9602f58956 Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Wed, 14 Feb 2018 01:11:09 -0500 Subject: [PATCH 11/98] lib, sharpd, zebra: Update the zapi_vrf_label call to add afi Add the ability to pass in an afi to zebra. zebra_vrf keeps track of the afi/label tuple and then does the right thing before we call down. AF_MPLS does not care about v4 or v6 it just knows label and what device to use for lookup. Signed-off-by: Donald Sharp --- lib/zclient.c | 3 ++- lib/zclient.h | 8 +++++++- sharpd/sharp_vty.c | 7 +++++-- sharpd/sharp_zebra.c | 4 ++-- sharpd/sharp_zebra.h | 2 +- zebra/zebra_vrf.h | 2 +- zebra/zserv.c | 12 ++++++++---- 7 files changed, 26 insertions(+), 12 deletions(-) diff --git a/lib/zclient.c b/lib/zclient.c index 8e8b50b15e..714888a3f3 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -363,7 +363,7 @@ static int zebra_hello_send(struct zclient *zclient) return 0; } -void zclient_send_vrf_label(struct zclient *zclient, vrf_id_t vrf_id, +void zclient_send_vrf_label(struct zclient *zclient, vrf_id_t vrf_id, afi_t afi, mpls_label_t label, enum lsp_types_t ltype) { struct stream *s; @@ -373,6 +373,7 @@ void zclient_send_vrf_label(struct zclient *zclient, vrf_id_t vrf_id, zclient_create_header(s, ZEBRA_VRF_LABEL, vrf_id); stream_putl(s, label); + stream_putc(s, afi); stream_putc(s, ltype); stream_putw_at(s, 0, stream_get_endp(s)); zclient_send_message(zclient); diff --git a/lib/zclient.h b/lib/zclient.h index 344b45fb54..d8a70c6cf3 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -390,9 +390,15 @@ extern void redist_del_instance(struct redist_proto *, u_short); * label for lookup. If you pass in MPLS_LABEL_NONE * we will cause a delete action and remove this label pop * operation. + * + * The underlying AF_MPLS doesn't care about afi's + * but we can make the zebra_vrf keep track of what + * we have installed and play some special games + * to get them both installed. */ extern void zclient_send_vrf_label(struct zclient *zclient, vrf_id_t vrf_id, - mpls_label_t label, enum lsp_types_t ltype); + afi_t afi, mpls_label_t label, + enum lsp_types_t ltype); extern void zclient_send_reg_requests(struct zclient *, vrf_id_t); extern void zclient_send_dereg_requests(struct zclient *, vrf_id_t); diff --git a/sharpd/sharp_vty.c b/sharpd/sharp_vty.c index 4f7d61b22f..0e7d1f2c29 100644 --- a/sharpd/sharp_vty.c +++ b/sharpd/sharp_vty.c @@ -80,14 +80,17 @@ DEFPY (install_routes, } DEFPY(vrf_label, vrf_label_cmd, - "sharp label vrf NAME$name label (0-100000)$label", + "sharp label vrf NAME$name label (0-100000)$label", "Sharp Routing Protocol\n" "Give a vrf a label\n" + "Pop and forward for IPv4\n" + "Pop and forward for IPv6\n" VRF_CMD_HELP_STR "The label to use, 0 specifies remove the label installed from previous\n" "Specified range to use\n") { struct vrf *vrf; + afi_t afi = (ipv4) ? AFI_IP : AFI_IP6; if (strcmp(name, "default") == 0) vrf = vrf_lookup_by_id(VRF_DEFAULT); @@ -102,7 +105,7 @@ DEFPY(vrf_label, vrf_label_cmd, if (label == 0) label = MPLS_LABEL_NONE; - vrf_label_add(vrf->vrf_id, label); + vrf_label_add(vrf->vrf_id, afi, label); return CMD_SUCCESS; } diff --git a/sharpd/sharp_zebra.c b/sharpd/sharp_zebra.c index f771e53f0c..78e8cf0adc 100644 --- a/sharpd/sharp_zebra.c +++ b/sharpd/sharp_zebra.c @@ -152,9 +152,9 @@ static void zebra_connected(struct zclient *zclient) zclient_send_reg_requests(zclient, VRF_DEFAULT); } -void vrf_label_add(vrf_id_t vrf_id, mpls_label_t label) +void vrf_label_add(vrf_id_t vrf_id, afi_t afi, mpls_label_t label) { - zclient_send_vrf_label(zclient, vrf_id, label, ZEBRA_LSP_SHARP); + zclient_send_vrf_label(zclient, vrf_id, afi, label, ZEBRA_LSP_SHARP); } void route_add(struct prefix *p, struct nexthop *nh) diff --git a/sharpd/sharp_zebra.h b/sharpd/sharp_zebra.h index 281c67ff94..0bba443bd4 100644 --- a/sharpd/sharp_zebra.h +++ b/sharpd/sharp_zebra.h @@ -24,7 +24,7 @@ extern void sharp_zebra_init(void); -extern void vrf_label_add(vrf_id_t vrf_id, mpls_label_t label); +extern void vrf_label_add(vrf_id_t vrf_id, afi_t afi, mpls_label_t label); extern void route_add(struct prefix *p, struct nexthop *nh); extern void route_delete(struct prefix *p); #endif diff --git a/zebra/zebra_vrf.h b/zebra/zebra_vrf.h index bfeb4b386c..d3a5316b9d 100644 --- a/zebra/zebra_vrf.h +++ b/zebra/zebra_vrf.h @@ -80,7 +80,7 @@ struct zebra_vrf { struct zebra_ns *zns; /* MPLS Label to handle L3VPN <-> vrf popping */ - mpls_label_t label; + mpls_label_t label[AFI_MAX]; /* MPLS static LSP config table */ struct hash *slsp_table; diff --git a/zebra/zserv.c b/zebra/zserv.c index 704b861960..ab8d8b51bc 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -2486,23 +2486,27 @@ stream_failure: return 1; } + static void zread_vrf_label(struct zserv *client, struct zebra_vrf *zvrf) { struct interface *ifp; mpls_label_t nlabel; + afi_t afi; struct stream *s; struct zebra_vrf *def_zvrf; enum lsp_types_t ltype; s = client->ibuf; STREAM_GETL(s, nlabel); - if (nlabel == zvrf->label) { + STREAM_GETC(s, afi); + if (nlabel == zvrf->label[afi]) { /* * Nothing to do here move along */ return; } + STREAM_GETC(s, ltype); if (zvrf->vrf->vrf_id != VRF_DEFAULT) @@ -2518,15 +2522,15 @@ static void zread_vrf_label(struct zserv *client, def_zvrf = zebra_vrf_lookup_by_id(VRF_DEFAULT); - if (zvrf->label != MPLS_LABEL_NONE) - mpls_lsp_uninstall(def_zvrf, ltype, zvrf->label, + if (zvrf->label[afi] != MPLS_LABEL_NONE) + mpls_lsp_uninstall(def_zvrf, ltype, zvrf->label[afi], NEXTHOP_TYPE_IFINDEX, NULL, ifp->ifindex); if (nlabel != MPLS_LABEL_NONE) mpls_lsp_install(def_zvrf, ltype, nlabel, MPLS_LABEL_IMPLICIT_NULL, NEXTHOP_TYPE_IFINDEX, NULL, ifp->ifindex); - zvrf->label = nlabel; + zvrf->label[afi] = nlabel; stream_failure: return; } From d6927cf390119c731be022acb878f76c3e106521 Mon Sep 17 00:00:00 2001 From: Chirag Shah Date: Mon, 12 Feb 2018 13:22:04 -0800 Subject: [PATCH 12/98] ospf6d: router-id change notify to restart ospf6d Notify user to store config and restart ospf6d as part of router-id change cli if any of the area active. Store zebra router-id under ospf6, when static router-id removed restore zebra router-id, ask to restart ospf6d. Signed-off-by: Chirag Shah --- ospf6d/ospf6_area.h | 2 ++ ospf6d/ospf6_neighbor.c | 4 ++++ ospf6d/ospf6_top.c | 29 +++++++++++++++++++++++++++-- ospf6d/ospf6_top.h | 2 ++ ospf6d/ospf6_zebra.c | 7 +++---- 5 files changed, 38 insertions(+), 6 deletions(-) diff --git a/ospf6d/ospf6_area.h b/ospf6d/ospf6_area.h index b7cd9b4b09..e162d21cd2 100644 --- a/ospf6d/ospf6_area.h +++ b/ospf6d/ospf6_area.h @@ -99,6 +99,8 @@ struct ospf6_area { /* Time stamps. */ struct timeval ts_spf; /* SPF calculation time stamp. */ + + uint32_t full_nbrs; /* Fully adjacent neighbors. */ }; #define OSPF6_AREA_ENABLE 0x01 diff --git a/ospf6d/ospf6_neighbor.c b/ospf6d/ospf6_neighbor.c index 35d0b0a646..05bc254951 100644 --- a/ospf6d/ospf6_neighbor.c +++ b/ospf6d/ospf6_neighbor.c @@ -193,7 +193,11 @@ static void ospf6_neighbor_state_change(u_char next_state, if (prev_state == OSPF6_NEIGHBOR_LOADING && next_state == OSPF6_NEIGHBOR_FULL) { OSPF6_AS_EXTERN_LSA_SCHEDULE(on->ospf6_if); + on->ospf6_if->area->full_nbrs++; } + + if (prev_state == OSPF6_NEIGHBOR_FULL) + on->ospf6_if->area->full_nbrs--; } if ((prev_state == OSPF6_NEIGHBOR_EXCHANGE diff --git a/ospf6d/ospf6_top.c b/ospf6d/ospf6_top.c index db39420548..c17b8918ec 100644 --- a/ospf6d/ospf6_top.c +++ b/ospf6d/ospf6_top.c @@ -335,6 +335,8 @@ DEFUN(ospf6_router_id, int ret; const char *router_id_str; u_int32_t router_id; + struct ospf6_area *oa; + struct listnode *node; argv_find(argv, argc, "A.B.C.D", &idx); router_id_str = argv[idx]->arg; @@ -346,8 +348,17 @@ DEFUN(ospf6_router_id, } o->router_id_static = router_id; - if (o->router_id == 0) - o->router_id = router_id; + + for (ALL_LIST_ELEMENTS_RO(o->area_list, node, oa)) { + if (oa->full_nbrs) { + vty_out(vty, + "For this router-id change to take effect," + " save config and restart ospf6d\n"); + return CMD_SUCCESS; + } + } + + o->router_id = router_id; return CMD_SUCCESS; } @@ -360,8 +371,22 @@ DEFUN(no_ospf6_router_id, V4NOTATION_STR) { VTY_DECLVAR_CONTEXT(ospf6, o); + struct ospf6_area *oa; + struct listnode *node; + o->router_id_static = 0; + + for (ALL_LIST_ELEMENTS_RO(o->area_list, node, oa)) { + if (oa->full_nbrs) { + vty_out(vty, + "For this router-id change to take effect," + " save config and restart ospf6d\n"); + return CMD_SUCCESS; + } + } o->router_id = 0; + if (o->router_id_zebra.s_addr) + o->router_id = (uint32_t)o->router_id_zebra.s_addr; return CMD_SUCCESS; } diff --git a/ospf6d/ospf6_top.h b/ospf6d/ospf6_top.h index 8f99cc33f4..d7a3766b80 100644 --- a/ospf6d/ospf6_top.h +++ b/ospf6d/ospf6_top.h @@ -32,6 +32,8 @@ struct ospf6 { /* static router id */ u_int32_t router_id_static; + struct in_addr router_id_zebra; + /* start time */ struct timeval starttime; diff --git a/ospf6d/ospf6_zebra.c b/ospf6d/ospf6_zebra.c index cc87c499ee..4fb959b952 100644 --- a/ospf6d/ospf6_zebra.c +++ b/ospf6d/ospf6_zebra.c @@ -46,8 +46,6 @@ unsigned char conf_debug_ospf6_zebra = 0; /* information about zebra. */ struct zclient *zclient = NULL; -struct in_addr router_id_zebra; - /* Router-id update message from zebra. */ static int ospf6_router_id_update_zebra(int command, struct zclient *zclient, zebra_size_t length, vrf_id_t vrf_id) @@ -56,13 +54,14 @@ static int ospf6_router_id_update_zebra(int command, struct zclient *zclient, struct ospf6 *o = ospf6; zebra_router_id_update_read(zclient->ibuf, &router_id); - router_id_zebra = router_id.u.prefix4; if (o == NULL) return 0; + o->router_id_zebra = router_id.u.prefix4; + if (o->router_id == 0) - o->router_id = (u_int32_t)router_id_zebra.s_addr; + o->router_id = (uint32_t)o->router_id_zebra.s_addr; return 0; } From 8fd9db586f17d7034ad2ec1fd972e503c70349cb Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Thu, 15 Feb 2018 13:52:57 -0500 Subject: [PATCH 13/98] zebra: Ensure unconfiguration works properly for vrf labels If you were to configure a v4 and v6 vrf pop and forward label that both happened to be the same, unconfiguring one would remove them both. This fixes that issue by noticing if we should remove it or not based upon v4 or v6 having the same label or not. Signed-off-by: Donald Sharp --- zebra/zserv.c | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/zebra/zserv.c b/zebra/zserv.c index ab8d8b51bc..b3b1fa79e9 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -2522,9 +2522,29 @@ static void zread_vrf_label(struct zserv *client, def_zvrf = zebra_vrf_lookup_by_id(VRF_DEFAULT); - if (zvrf->label[afi] != MPLS_LABEL_NONE) - mpls_lsp_uninstall(def_zvrf, ltype, zvrf->label[afi], - NEXTHOP_TYPE_IFINDEX, NULL, ifp->ifindex); + if (zvrf->label[afi] != MPLS_LABEL_NONE) { + afi_t scrubber; + bool really_remove; + + really_remove = true; + for (scrubber = AFI_IP; scrubber < AFI_MAX ; scrubber++) { + if (scrubber == afi) + continue; + + if (zvrf->label[scrubber] == MPLS_LABEL_NONE) + continue; + + if (zvrf->label[afi] == zvrf->label[scrubber]) { + really_remove = false; + break; + } + } + + if (really_remove) + mpls_lsp_uninstall(def_zvrf, ltype, zvrf->label[afi], + NEXTHOP_TYPE_IFINDEX, NULL, + ifp->ifindex); + } if (nlabel != MPLS_LABEL_NONE) mpls_lsp_install(def_zvrf, ltype, nlabel, MPLS_LABEL_IMPLICIT_NULL, From 9f64bf393c27f01ab8951c3d45c3a27bcc5b0cf1 Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Thu, 15 Feb 2018 13:35:10 -0500 Subject: [PATCH 14/98] sharpd: Add ability to build from tarball Since sharpd is only typically built with a development build this was not noticed. Add the necessary headers to build this thingie(tm). Signed-off-by: Donald Sharp --- sharpd/subdir.am | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sharpd/subdir.am b/sharpd/subdir.am index da7d68e0bc..490a2ba787 100644 --- a/sharpd/subdir.am +++ b/sharpd/subdir.am @@ -13,6 +13,11 @@ sharpd_libsharp_a_SOURCES = \ sharpd/sharp_vty.c \ # end +noinst_HEADERS += \ + sharpd/sharp_vty.h \ + sharpd/sharp_zebra.h \ + # end + sharpd/sharp_vty_clippy.c: $(CLIPPY_DEPS) sharpd/sharp_vty.$(OBJEXT): sharpd/sharp_vty_clippy.c From 827ed7076d4761e2c3cd8fb91053b738b0231898 Mon Sep 17 00:00:00 2001 From: Daniel Walton Date: Thu, 15 Feb 2018 20:55:43 +0000 Subject: [PATCH 15/98] bgpd: "no neighbor 10.13.0.12 peer-group ibgp" does not remove peer Signed-off-by: Daniel Walton This worked for unnumbered peers but not for numbered peers. This is before the fix: router bgp 100 coalesce-time 1000 neighbor FOO peer-group neighbor FOO remote-as external neighbor swp1 interface peer-group FOO neighbor 1.1.1.1 peer-group FOO ! line vty exec-timeout 0 0 ! end cel-redxp-10# wr Note: this version of vtysh never writes vtysh.conf Building Configuration... Integrated configuration saved to /etc/frr/frr.conf [OK] cel-redxp-10# conf t cel-redxp-10(config)# router bgp cel-redxp-10(config-router)# no neighbor swp1 interface peer-group FOO cel-redxp-10(config-router)# no neighbor 1.1.1.1 peer-group FOO cel-redxp-10(config-router)# do show run Building configuration... Current configuration: ! frr version 4.1-dev frr defaults datacenter hostname cel-redxp-10 ! service integrated-vtysh-config ! password cn321 ! log syslog ! router bgp 100 coalesce-time 1000 neighbor FOO peer-group neighbor FOO remote-as external neighbor 1.1.1.1 remote-as external ! address-family ipv4 unicast no neighbor 1.1.1.1 activate exit-address-family ! line vty exec-timeout 0 0 ! end cel-redxp-10(config-router)# After the fix "no neighbor 1.1.1.1 peer-group FOO" removes the 1.1.1.1 neighbor. --- bgpd/bgp_vty.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 7ee641d6cf..c7140b2f1f 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -3395,7 +3395,7 @@ DEFUN (no_neighbor_set_peer_group, return CMD_WARNING_CONFIG_FAILED; } - ret = peer_group_unbind(bgp, peer, group); + ret = peer_delete(peer); return bgp_vty_return(vty, ret); } From e2063df358308bf505db0561fbae344185e8bd8d Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Fri, 16 Feb 2018 11:00:01 +0100 Subject: [PATCH 16/98] bgpd: prevent from configuring vrf-policy when in BGP VRF instance Under a BGP VRF instance, prevent from entering in vrf-policy mode. This mode is reserved for non VRF instances that want to handle several VRF at the same time. Signed-off-by: Philippe Guibert --- bgpd/rfapi/bgp_rfapi_cfg.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/bgpd/rfapi/bgp_rfapi_cfg.c b/bgpd/rfapi/bgp_rfapi_cfg.c index 15e09c639e..f28b8a2ced 100644 --- a/bgpd/rfapi/bgp_rfapi_cfg.c +++ b/bgpd/rfapi/bgp_rfapi_cfg.c @@ -2977,6 +2977,11 @@ DEFUN_NOSH (vnc_vrf_policy, struct rfapi_nve_group_cfg *rfg; VTY_DECLVAR_CONTEXT(bgp, bgp); + if (bgp->inst_type == BGP_INSTANCE_TYPE_VRF) { + vty_out(vty, "Can't configure vrf-policy within a BGP VRF instance\n"); + return CMD_WARNING_CONFIG_FAILED; + } + /* Search for name */ rfg = bgp_rfapi_cfg_match_byname(bgp, argv[1]->arg, RFAPI_GROUP_CFG_VRF); @@ -3007,6 +3012,10 @@ DEFUN (vnc_no_vrf_policy, { VTY_DECLVAR_CONTEXT(bgp, bgp); + /* silently return */ + if (bgp->inst_type == BGP_INSTANCE_TYPE_VRF) + return CMD_SUCCESS; + return bgp_rfapi_delete_named_nve_group(vty, bgp, argv[2]->arg, RFAPI_GROUP_CFG_VRF); } From 5a9825aac66ab221ed8baec6b26f21936de85005 Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Sat, 17 Feb 2018 14:52:44 -0500 Subject: [PATCH 17/98] isisd: Remove impossible check The circuit->area value is always true in every code path to isis_circuit_af_set( isis_vty.c ). Therefore was_enabled will always be true. If was_enabled ever became false then the area->ip_circuits and area->ipv6_circuits lines would segfault. Signed-off-by: Donald Sharp --- isisd/isis_circuit.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/isisd/isis_circuit.c b/isisd/isis_circuit.c index 98087942a6..20ce0f1fad 100644 --- a/isisd/isis_circuit.c +++ b/isisd/isis_circuit.c @@ -1168,7 +1168,6 @@ void isis_circuit_af_set(struct isis_circuit *circuit, bool ip_router, struct isis_area *area = circuit->area; bool change = circuit->ip_router != ip_router || circuit->ipv6_router != ipv6_router; - bool was_enabled = !!circuit->area; area->ip_circuits += ip_router - circuit->ip_router; area->ipv6_circuits += ipv6_router - circuit->ipv6_router; @@ -1182,8 +1181,6 @@ void isis_circuit_af_set(struct isis_circuit *circuit, bool ip_router, if (!ip_router && !ipv6_router) isis_csm_state_change(ISIS_DISABLE, circuit, area); - else if (!was_enabled) - isis_csm_state_change(ISIS_ENABLE, circuit, area); else lsp_regenerate_schedule(circuit->area, circuit->is_type, 0); } From 100cb793cce2ad3cad41e9b8c4d16b496dbf102b Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Sat, 17 Feb 2018 14:59:32 -0500 Subject: [PATCH 18/98] bgpd: Fix value set but never used The value 'pnt' was being set but never used. If we need this in the future it will be a simple thing to add back in. Signed-off-by: Donald Sharp --- bgpd/bgp_evpn_vty.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c index e1e11e44ac..1373afec4e 100644 --- a/bgpd/bgp_evpn_vty.c +++ b/bgpd/bgp_evpn_vty.c @@ -83,7 +83,7 @@ static void display_vrf_import_rt(struct vty *vty, case ECOMMUNITY_ENCODE_AS: eas.as = (*pnt++ << 8); eas.as |= (*pnt++); - pnt = ptr_get_be32(pnt, &eas.val); + ptr_get_be32(pnt, &eas.val); snprintf(rt_buf, RT_ADDRSTRLEN, "%u:%u", eas.as, eas.val); @@ -195,7 +195,7 @@ static void display_import_rt(struct vty *vty, struct irt_node *irt, case ECOMMUNITY_ENCODE_AS: eas.as = (*pnt++ << 8); eas.as |= (*pnt++); - pnt = ptr_get_be32(pnt, &eas.val); + ptr_get_be32(pnt, &eas.val); snprintf(rt_buf, RT_ADDRSTRLEN, "%u:%u", eas.as, eas.val); From d922605d28fc929d962bcc586bced0453543cc3a Mon Sep 17 00:00:00 2001 From: Olivier Dugeon Date: Mon, 19 Feb 2018 10:17:41 +0100 Subject: [PATCH 19/98] OSPFD: Fix Segment Routing Lan Adjacency TLVs - Lan Adjacency TLVs was incorrectly formatted due to an error in TLV size computation. Add new macro to fix this issue - Update SR link nexthop when it corresponds to an LAN Adj SID. The nexthop is set to the router id in the TLVi (as per draft), but we need the neighbor IP address to set the corresponding MPLS LFIB entry Signed-off-by: Olivier Dugeon --- ospfd/ospf_ext.c | 29 ++++++++++++++++++----------- ospfd/ospf_sr.c | 6 ++++++ ospfd/ospf_sr.h | 5 ++--- 3 files changed, 26 insertions(+), 14 deletions(-) diff --git a/ospfd/ospf_ext.c b/ospfd/ospf_ext.c index d7faf4b0de..95835cfd0b 100644 --- a/ospfd/ospf_ext.c +++ b/ospfd/ospf_ext.c @@ -334,10 +334,12 @@ static void set_prefix_sid(struct ext_itf *exti, uint8_t algorithm, /* Set Label or Index value */ if (value_type == SID_LABEL) { - TLV_LEN(exti->node_sid) = htons(SID_LABEL_SIZE); + TLV_LEN(exti->node_sid) = + htons(SID_LABEL_SIZE(EXT_SUBTLV_PREFIX_SID_SIZE)); exti->node_sid.value = htonl(SET_LABEL(value)); } else { - TLV_LEN(exti->node_sid) = htons(SID_INDEX_SIZE); + TLV_LEN(exti->node_sid) = + htons(SID_INDEX_SIZE(EXT_SUBTLV_PREFIX_SID_SIZE)); exti->node_sid.value = htonl(value); } @@ -370,11 +372,13 @@ static void set_adj_sid(struct ext_itf *exti, bool backup, uint32_t value, /* Adjust Length, Flags and Value depending on the type of Label */ if (value_type == SID_LABEL) { SET_FLAG(flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG); - TLV_LEN(exti->adj_sid[index]) = htons(SID_LABEL_SIZE); + TLV_LEN(exti->adj_sid[index]) = + htons(SID_LABEL_SIZE(EXT_SUBTLV_ADJ_SID_SIZE)); exti->adj_sid[index].value = htonl(SET_LABEL(value)); } else { UNSET_FLAG(flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG); - TLV_LEN(exti->adj_sid[index]) = htons(SID_INDEX_SIZE); + TLV_LEN(exti->adj_sid[index]) = + htons(SID_INDEX_SIZE(EXT_SUBTLV_ADJ_SID_SIZE)); exti->adj_sid[index].value = htonl(value); } @@ -402,7 +406,7 @@ static void set_lan_adj_sid(struct ext_itf *exti, bool backup, uint32_t value, } /* Set Header */ - TLV_TYPE(exti->lan_sid[index]) = htons(EXT_SUBTLV_ADJ_SID); + TLV_TYPE(exti->lan_sid[index]) = htons(EXT_SUBTLV_LAN_ADJ_SID); /* Only Local ADJ-SID is supported for the moment */ SET_FLAG(flags, EXT_SUBTLV_LINK_ADJ_SID_LFLG); @@ -410,11 +414,13 @@ static void set_lan_adj_sid(struct ext_itf *exti, bool backup, uint32_t value, /* Adjust Length, Flags and Value depending on the type of Label */ if (value_type == SID_LABEL) { SET_FLAG(flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG); - TLV_LEN(exti->lan_sid[index]) = htons(SID_LABEL_SIZE); + TLV_LEN(exti->lan_sid[index]) = + htons(SID_LABEL_SIZE(EXT_SUBTLV_PREFIX_RANGE_SIZE)); exti->lan_sid[index].value = htonl(SET_LABEL(value)); } else { UNSET_FLAG(flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG); - TLV_LEN(exti->lan_sid[index]) = htons(SID_INDEX_SIZE); + TLV_LEN(exti->lan_sid[index]) = + htons(SID_INDEX_SIZE(EXT_SUBTLV_PREFIX_RANGE_SIZE)); exti->lan_sid[index].value = htonl(value); } @@ -906,7 +912,7 @@ static void ospf_ext_link_lsa_body_set(struct stream *s, struct ext_itf *exti) /* Build LSA body for an Extended Link TLV with Adj. SID */ build_tlv_header(s, &exti->link.header); stream_put(s, TLV_DATA(&exti->link.header), EXT_TLV_LINK_SIZE); - /* then add Ajacency SubTLVs */ + /* then add Adjacency SubTLVs */ build_tlv(s, &exti->adj_sid[1].header); build_tlv(s, &exti->adj_sid[0].header); @@ -921,8 +927,8 @@ static void ospf_ext_link_lsa_body_set(struct stream *s, struct ext_itf *exti) /* Build LSA body for an Extended Link TLV with LAN SID */ build_tlv_header(s, &exti->link.header); - stream_put(s, &exti->link.header, EXT_TLV_LINK_SIZE); - /* then add LAN-Ajacency SubTLVs */ + stream_put(s, TLV_DATA(&exti->link.header), EXT_TLV_LINK_SIZE); + /* then add LAN-Adjacency SubTLVs */ build_tlv(s, &exti->lan_sid[1].header); build_tlv(s, &exti->lan_sid[0].header); } @@ -1671,8 +1677,9 @@ static uint16_t show_vty_ext_link_lan_adj_sid(struct vty *vty, vty_out(vty, " LAN-Adj-SID Sub-TLV: Length %u\n\tFlags: " "0x%x\n\tMT-ID:0x%x\n\tWeight: 0x%x\n\tNeighbor ID: " - "%s\n\tLabel: %u\n", + "%s\n\t%s: %u\n", ntohs(top->header.length), top->flags, top->mtid, top->weight, + inet_ntoa(top->neighbor_id), CHECK_FLAG(top->flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG) ? "Label" : "Index", CHECK_FLAG(top->flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG) diff --git a/ospfd/ospf_sr.c b/ospfd/ospf_sr.c index 9827eac71b..1560977ae8 100644 --- a/ospfd/ospf_sr.c +++ b/ospfd/ospf_sr.c @@ -491,6 +491,12 @@ static int compute_link_nhlfe(struct sr_link *srl) srl->nhlfe[0].ifindex = nh->oi->ifp->ifindex; srl->nhlfe[1].ifindex = nh->oi->ifp->ifindex; + /* Update neighbor address for LAN_ADJ_SID */ + if (srl->type == LAN_ADJ_SID) { + IPV4_ADDR_COPY(&srl->nhlfe[0].nexthop, &nh->src); + IPV4_ADDR_COPY(&srl->nhlfe[1].nexthop, &nh->src); + } + /* Set Input & Output Label */ if (CHECK_FLAG(srl->flags[0], EXT_SUBTLV_LINK_ADJ_SID_VFLG)) srl->nhlfe[0].label_in = srl->sid[0]; diff --git a/ospfd/ospf_sr.h b/ospfd/ospf_sr.h index cb7d0833ec..4d3f5f441a 100644 --- a/ospfd/ospf_sr.h +++ b/ospfd/ospf_sr.h @@ -49,11 +49,10 @@ /* Segment Routing TLVs as per draft-ietf-ospf-segment-routing-extensions-19 */ /* Segment ID could be a Label (3 bytes) or an Index (4 bytes) */ -#define SID_BASE_SIZE 4 #define SID_LABEL 3 -#define SID_LABEL_SIZE (SID_BASE_SIZE + SID_LABEL) +#define SID_LABEL_SIZE(U) (U - 1) #define SID_INDEX 4 -#define SID_INDEX_SIZE (SID_BASE_SIZE + SID_INDEX) +#define SID_INDEX_SIZE(U) (U) /* SID/Label Sub TLV - section 2.1 */ #define SUBTLV_SID_LABEL 1 From fa7129639684378ad852b934efeb6246ef82800e Mon Sep 17 00:00:00 2001 From: Renato Westphal Date: Wed, 20 Sep 2017 00:05:25 -0300 Subject: [PATCH 20/98] zebra: implement recursive MPLS labels When a BGP-labeled route is resolved into an LDP-labeled IGP route, zebra would install it with no labels in the kernel. This patch implements recursive MPLS labels, i.e. make zebra install all labels from the route's nexthop chain (the labels from the top-level nexthop being installed in the top of the MPLS label stack). Multiple recursion levels are supported. Signed-off-by: Renato Westphal --- zebra/rt_netlink.c | 191 ++++++++++++++++++++++----------------------- 1 file changed, 94 insertions(+), 97 deletions(-) diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index d778990141..a80ab9d834 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -840,6 +840,7 @@ static void _netlink_route_build_singlepath(const char *routedesc, int bytelen, { struct mpls_label_stack *nh_label; mpls_lse_t out_lse[MPLS_MAX_LABELS]; + int num_labels = 0; char label_buf[256]; /* @@ -849,56 +850,54 @@ static void _netlink_route_build_singlepath(const char *routedesc, int bytelen, * you fix this assumption */ label_buf[0] = '\0'; - /* outgoing label - either as NEWDST (in the case of LSR) or as ENCAP - * (in the case of LER) - */ - nh_label = nexthop->nh_label; - if (rtmsg->rtm_family == AF_MPLS) { - assert(nh_label); - assert(nh_label->num_labels == 1); - } - if (nh_label && nh_label->num_labels) { - int i, num_labels = 0; - u_int32_t bos; + assert(nexthop); + for (struct nexthop *nh = nexthop; nh; nh = nh->rparent) { char label_buf1[20]; - for (i = 0; i < nh_label->num_labels; i++) { - if (nh_label->label[i] != MPLS_LABEL_IMPLICIT_NULL) { - bos = ((i == (nh_label->num_labels - 1)) ? 1 - : 0); - out_lse[i] = mpls_lse_encode(nh_label->label[i], - 0, 0, bos); - if (IS_ZEBRA_DEBUG_KERNEL) { - if (!num_labels) - sprintf(label_buf, "label %u", - nh_label->label[i]); - else { - sprintf(label_buf1, "/%u", - nh_label->label[i]); - strlcat(label_buf, label_buf1, - sizeof(label_buf)); - } - } - num_labels++; - } - } - if (num_labels) { - if (rtmsg->rtm_family == AF_MPLS) - addattr_l(nlmsg, req_size, RTA_NEWDST, &out_lse, - num_labels * sizeof(mpls_lse_t)); - else { - struct rtattr *nest; - u_int16_t encap = LWTUNNEL_ENCAP_MPLS; + nh_label = nh->nh_label; + if (!nh_label || !nh_label->num_labels) + continue; - addattr_l(nlmsg, req_size, RTA_ENCAP_TYPE, - &encap, sizeof(u_int16_t)); - nest = addattr_nest(nlmsg, req_size, RTA_ENCAP); - addattr_l(nlmsg, req_size, MPLS_IPTUNNEL_DST, - &out_lse, - num_labels * sizeof(mpls_lse_t)); - addattr_nest_end(nlmsg, nest); + for (int i = 0; i < nh_label->num_labels; i++) { + if (nh_label->label[i] == MPLS_LABEL_IMPLICIT_NULL) + continue; + + if (IS_ZEBRA_DEBUG_KERNEL) { + if (!num_labels) + sprintf(label_buf, "label %u", + nh_label->label[i]); + else { + sprintf(label_buf1, "/%u", + nh_label->label[i]); + strlcat(label_buf, label_buf1, + sizeof(label_buf)); + } } + + out_lse[num_labels] = + mpls_lse_encode(nh_label->label[i], 0, 0, 0); + num_labels++; + } + } + + if (num_labels) { + /* Set the BoS bit */ + out_lse[num_labels - 1] |= htonl(1 << MPLS_LS_S_SHIFT); + + if (rtmsg->rtm_family == AF_MPLS) + addattr_l(nlmsg, req_size, RTA_NEWDST, &out_lse, + num_labels * sizeof(mpls_lse_t)); + else { + struct rtattr *nest; + u_int16_t encap = LWTUNNEL_ENCAP_MPLS; + + addattr_l(nlmsg, req_size, RTA_ENCAP_TYPE, &encap, + sizeof(u_int16_t)); + nest = addattr_nest(nlmsg, req_size, RTA_ENCAP); + addattr_l(nlmsg, req_size, MPLS_IPTUNNEL_DST, &out_lse, + num_labels * sizeof(mpls_lse_t)); + addattr_nest_end(nlmsg, nest); } } @@ -1045,6 +1044,7 @@ static void _netlink_route_build_multipath(const char *routedesc, int bytelen, { struct mpls_label_stack *nh_label; mpls_lse_t out_lse[MPLS_MAX_LABELS]; + int num_labels = 0; char label_buf[256]; rtnh->rtnh_len = sizeof(*rtnh); @@ -1059,63 +1059,60 @@ static void _netlink_route_build_multipath(const char *routedesc, int bytelen, * you fix this assumption */ label_buf[0] = '\0'; - /* outgoing label - either as NEWDST (in the case of LSR) or as ENCAP - * (in the case of LER) - */ - nh_label = nexthop->nh_label; - if (rtmsg->rtm_family == AF_MPLS) { - assert(nh_label); - assert(nh_label->num_labels == 1); - } - if (nh_label && nh_label->num_labels) { - int i, num_labels = 0; - u_int32_t bos; + assert(nexthop); + for (struct nexthop *nh = nexthop; nh; nh = nh->rparent) { char label_buf1[20]; - for (i = 0; i < nh_label->num_labels; i++) { - if (nh_label->label[i] != MPLS_LABEL_IMPLICIT_NULL) { - bos = ((i == (nh_label->num_labels - 1)) ? 1 - : 0); - out_lse[i] = mpls_lse_encode(nh_label->label[i], - 0, 0, bos); - if (IS_ZEBRA_DEBUG_KERNEL) { - if (!num_labels) - sprintf(label_buf, "label %u", - nh_label->label[i]); - else { - sprintf(label_buf1, "/%u", - nh_label->label[i]); - strlcat(label_buf, label_buf1, - sizeof(label_buf)); - } - } - num_labels++; - } - } - if (num_labels) { - if (rtmsg->rtm_family == AF_MPLS) { - rta_addattr_l(rta, NL_PKT_BUF_SIZE, RTA_NEWDST, - &out_lse, - num_labels * sizeof(mpls_lse_t)); - rtnh->rtnh_len += RTA_LENGTH( - num_labels * sizeof(mpls_lse_t)); - } else { - struct rtattr *nest; - u_int16_t encap = LWTUNNEL_ENCAP_MPLS; - int len = rta->rta_len; + nh_label = nh->nh_label; + if (!nh_label || !nh_label->num_labels) + continue; - rta_addattr_l(rta, NL_PKT_BUF_SIZE, - RTA_ENCAP_TYPE, &encap, - sizeof(u_int16_t)); - nest = rta_nest(rta, NL_PKT_BUF_SIZE, - RTA_ENCAP); - rta_addattr_l(rta, NL_PKT_BUF_SIZE, - MPLS_IPTUNNEL_DST, &out_lse, - num_labels * sizeof(mpls_lse_t)); - rta_nest_end(rta, nest); - rtnh->rtnh_len += rta->rta_len - len; + for (int i = 0; i < nh_label->num_labels; i++) { + if (nh_label->label[i] == MPLS_LABEL_IMPLICIT_NULL) + continue; + + if (IS_ZEBRA_DEBUG_KERNEL) { + if (!num_labels) + sprintf(label_buf, "label %u", + nh_label->label[i]); + else { + sprintf(label_buf1, "/%u", + nh_label->label[i]); + strlcat(label_buf, label_buf1, + sizeof(label_buf)); + } } + + out_lse[num_labels] = + mpls_lse_encode(nh_label->label[i], 0, 0, 0); + num_labels++; + } + } + + if (num_labels) { + /* Set the BoS bit */ + out_lse[num_labels - 1] |= htonl(1 << MPLS_LS_S_SHIFT); + + if (rtmsg->rtm_family == AF_MPLS) { + rta_addattr_l(rta, NL_PKT_BUF_SIZE, RTA_NEWDST, + &out_lse, + num_labels * sizeof(mpls_lse_t)); + rtnh->rtnh_len += + RTA_LENGTH(num_labels * sizeof(mpls_lse_t)); + } else { + struct rtattr *nest; + u_int16_t encap = LWTUNNEL_ENCAP_MPLS; + int len = rta->rta_len; + + rta_addattr_l(rta, NL_PKT_BUF_SIZE, RTA_ENCAP_TYPE, + &encap, sizeof(u_int16_t)); + nest = rta_nest(rta, NL_PKT_BUF_SIZE, RTA_ENCAP); + rta_addattr_l(rta, NL_PKT_BUF_SIZE, MPLS_IPTUNNEL_DST, + &out_lse, + num_labels * sizeof(mpls_lse_t)); + rta_nest_end(rta, nest); + rtnh->rtnh_len += rta->rta_len - len; } } From 4d9ad5dcd0244b4035d8f88f7a3478a450c74283 Mon Sep 17 00:00:00 2001 From: Mladen Sablic Date: Mon, 12 Feb 2018 23:41:33 +0100 Subject: [PATCH 21/98] pimd: Multicast traceroute client and router This commit is the implementation of weak multicast traceroute. It consists of IGMP module dealing with mtrace type IGMP messages and client program mtrace/mtracebis for initiating mtrace queries. Signed-off-by: Mladen Sablic --- configure.ac | 1 + .../backports/ubuntu14.04/debian/frr.install | 2 + debianpkg/frr.install | 2 + doc/Makefile.am | 2 + doc/mtracebis.8.in | 25 + lib/prefix.h | 1 + pimd/.gitignore | 1 + pimd/COMMANDS | 3 + pimd/mtracebis.c | 371 +++++++++ pimd/mtracebis_netlink.c | 700 +++++++++++++++++ pimd/mtracebis_netlink.h | 130 ++++ pimd/mtracebis_routeget.c | 98 +++ pimd/mtracebis_routeget.h | 31 + pimd/pim_cmd.c | 25 + pimd/pim_cmd.h | 1 + pimd/pim_igmp.c | 11 + pimd/pim_igmp.h | 2 + pimd/pim_igmp_mtrace.c | 727 ++++++++++++++++++ pimd/pim_igmp_mtrace.h | 103 +++ pimd/pim_oil.c | 4 +- pimd/pim_oil.h | 2 + pimd/pim_vty.c | 5 + pimd/pimd.h | 4 + pimd/subdir.am | 11 + vtysh/vtysh.c | 14 + 25 files changed, 2274 insertions(+), 2 deletions(-) create mode 100644 doc/mtracebis.8.in create mode 100644 pimd/mtracebis.c create mode 100644 pimd/mtracebis_netlink.c create mode 100644 pimd/mtracebis_netlink.h create mode 100644 pimd/mtracebis_routeget.c create mode 100644 pimd/mtracebis_routeget.h create mode 100644 pimd/pim_igmp_mtrace.c create mode 100644 pimd/pim_igmp_mtrace.h diff --git a/configure.ac b/configure.ac index 6bd2d44fe8..0d6c99acfa 100755 --- a/configure.ac +++ b/configure.ac @@ -1886,6 +1886,7 @@ AC_CONFIG_FILES([Makefile doc/eigrpd.8 doc/ripngd.8 doc/pimd.8 + doc/mtracebis.8 doc/nhrpd.8 doc/vtysh.1 doc/watchfrr.8 diff --git a/debianpkg/backports/ubuntu14.04/debian/frr.install b/debianpkg/backports/ubuntu14.04/debian/frr.install index adce915e1f..8e83bdf6df 100644 --- a/debianpkg/backports/ubuntu14.04/debian/frr.install +++ b/debianpkg/backports/ubuntu14.04/debian/frr.install @@ -1,5 +1,6 @@ etc/frr/ usr/bin/vtysh +usr/bin/mtracebis usr/include/frr/ usr/lib/ tools/frr etc/init.d/ @@ -15,6 +16,7 @@ usr/share/man/man8/ripngd.8 usr/share/man/man8/zebra.8 usr/share/man/man8/isisd.8 usr/share/man/man8/watchfrr.8 +usr/share/man/man8/mtracebis.8 usr/share/snmp/mibs/ tools/etc/* etc/ tools/*.service lib/systemd/system diff --git a/debianpkg/frr.install b/debianpkg/frr.install index 2d86009dba..1aee4d540e 100644 --- a/debianpkg/frr.install +++ b/debianpkg/frr.install @@ -1,5 +1,6 @@ etc/frr/ usr/bin/vtysh +usr/bin/mtracebis usr/include/frr/ usr/lib/ tools/frr usr/lib/frr @@ -16,6 +17,7 @@ usr/share/man/man8/zebra.8 usr/share/man/man8/isisd.8 usr/share/man/man8/watchfrr.8 usr/share/man/man8/frr-args.8 +usr/share/man/man8/mtracebis.8 usr/share/snmp/mibs/ tools/etc/* etc/ tools/*.service lib/systemd/system diff --git a/doc/Makefile.am b/doc/Makefile.am index 7aaa36556f..9c81202db9 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -104,6 +104,7 @@ man_MANS = frr.1 frr-args.8 if PIMD man_MANS += pimd.8 +man_MANS += mtracebis.8 endif if BGPD @@ -169,6 +170,7 @@ EXTRA_DIST = BGP-TypeCode draft-zebra-00.ms draft-zebra-00.txt \ ripd.8.in \ ripngd.8.in \ pimd.8.in \ + mtracebis.8.in \ nhrpd.8.in \ vtysh.1.in \ watchfrr.8.in \ diff --git a/doc/mtracebis.8.in b/doc/mtracebis.8.in new file mode 100644 index 0000000000..3abc717b94 --- /dev/null +++ b/doc/mtracebis.8.in @@ -0,0 +1,25 @@ +.\" This file was originally generated by help2man 1.44.1. +.TH MTRACEBIS "8" "February 2018" "mtracebis 0.1" "System Administration Utilities" +.SH NAME +mtracebis \- a program to initiate multicast traceroute "mtrace" queries +.SH SYNOPSIS +mtracebis + +.SH DESCRIPTION +.B mtracebis +is a program used to test multicast connectivity in a multicast and multicast +traceroute enabled IP network. +.PP +Initial version of the program requires multicast source IP address and +initiates a weak traceroute across the network. This tests whether the +interfaces towards the source are multicast enabled. First query sent is a +full query, capable of crossing the network all the way to the source. If this +fails, hop-by-hop queries are initiated. +.PP +Hop-by-hop queries start by requesting only a response from the nearest router. +Following that, next query is extended to the next two routers, and so on... +until a set of routers is tested for connectivity. +.SH SEE ALSO +See the project homepage at <@PACKAGE_URL@>. +.SH AUTHORS +Copyright 2018 Mladen Sablic diff --git a/lib/prefix.h b/lib/prefix.h index bcc2230607..5bf7d498c1 100644 --- a/lib/prefix.h +++ b/lib/prefix.h @@ -239,6 +239,7 @@ static inline void ipv4_addr_copy(struct in_addr *dst, #define IPV4_NET127(a) ((((u_int32_t) (a)) & 0xff000000) == 0x7f000000) #define IPV4_LINKLOCAL(a) ((((u_int32_t) (a)) & 0xffff0000) == 0xa9fe0000) #define IPV4_CLASS_DE(a) ((((u_int32_t) (a)) & 0xe0000000) == 0xe0000000) +#define IPV4_MC_LINKLOCAL(a) ((((u_int32_t) (a)) & 0xffffff00) == 0xe0000000) /* Max bit/byte length of IPv6 address. */ #define IPV6_MAX_BYTELEN 16 diff --git a/pimd/.gitignore b/pimd/.gitignore index e23216b058..1f56cfaecd 100644 --- a/pimd/.gitignore +++ b/pimd/.gitignore @@ -2,6 +2,7 @@ Makefile.in libpim.a pimd +mtracebis test_igmpv3_join tags TAGS diff --git a/pimd/COMMANDS b/pimd/COMMANDS index c545eca56e..6f2e020bd8 100644 --- a/pimd/COMMANDS +++ b/pimd/COMMANDS @@ -59,6 +59,7 @@ debug commands: clear ip pim interfaces Reset PIM interfaces clear ip pim oil Rescan PIM OIL (output interface list) debug igmp IGMP protocol activity + debug mtrace Mtrace protocol activity debug mroute PIM interaction with kernel MFC cache debug pim PIM protocol activity debug pim zebra ZEBRA protocol activity @@ -76,4 +77,6 @@ statistics commands: pimd: show memory pim PIM memory statistics +vtysh: + mtrace Multicast traceroute -x- diff --git a/pimd/mtracebis.c b/pimd/mtracebis.c new file mode 100644 index 0000000000..2032cdcfbe --- /dev/null +++ b/pimd/mtracebis.c @@ -0,0 +1,371 @@ +/* + * Multicast Traceroute for FRRouting + * Copyright (C) 2018 Mladen Sablic + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifdef __linux__ + +#include "pim_igmp_mtrace.h" + +#include "checksum.h" +#include "mtracebis_routeget.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MTRACEBIS_VERSION "0.1" +#define MTRACE_TIMEOUT (5) + +#define IP_HDR_LEN (sizeof(struct ip)) +#define IP_RA_LEN (4) +#define MTRACE_BUF_LEN (MTRACE_HDR_SIZE + (MTRACE_MAX_HOPS * MTRACE_RSP_SIZE)) +#define IP_AND_MTRACE_BUF_LEN (IP_HDR_LEN + IP_RA_LEN + MTRACE_BUF_LEN) + +static const char *progname; +static void usage(void) +{ + fprintf(stderr, "Usage : %s \n", progname); +} +static void version(void) +{ + fprintf(stderr, "%s %s\n", progname, MTRACEBIS_VERSION); +} + +static int send_query(int fd, struct in_addr to_addr, + struct igmp_mtrace *mtrace) +{ + struct sockaddr_in to; + socklen_t tolen; + int sent; + + memset(&to, 0, sizeof(to)); + to.sin_family = AF_INET; + to.sin_addr = to_addr; + tolen = sizeof(to); + + sent = sendto(fd, (char *)mtrace, sizeof(*mtrace), MSG_DONTWAIT, + (struct sockaddr *)&to, tolen); + + if (sent < 1) + return -1; + return 0; +} + +static void print_query(struct igmp_mtrace *mtrace) +{ + char src_str[INET_ADDRSTRLEN]; + char dst_str[INET_ADDRSTRLEN]; + char grp_str[INET_ADDRSTRLEN]; + + printf("* Mtrace from %s to %s via group %s\n", + inet_ntop(AF_INET, &mtrace->src_addr, src_str, sizeof(src_str)), + inet_ntop(AF_INET, &mtrace->dst_addr, dst_str, sizeof(dst_str)), + inet_ntop(AF_INET, &mtrace->grp_addr, grp_str, sizeof(grp_str))); +} + +static int recv_response(int fd, long msec, int *hops) +{ + int recvd; + char mtrace_buf[IP_AND_MTRACE_BUF_LEN]; + struct ip *ip; + struct igmp_mtrace *mtrace; + int mtrace_len; + int responses; + int i; + u_short sum; + + recvd = recvfrom(fd, mtrace_buf, IP_AND_MTRACE_BUF_LEN, 0, NULL, 0); + + if (recvd < 1) { + fprintf(stderr, "recvfrom error: %s\n", strerror(errno)); + return -1; + } + + if (recvd < (int)sizeof(struct ip)) { + fprintf(stderr, "no ip header\n"); + return -1; + } + + ip = (struct ip *)mtrace_buf; + + if (ip->ip_v != 4) { + fprintf(stderr, "IP not version 4\n"); + return -1; + } + + sum = ip->ip_sum; + ip->ip_sum = 0; + + if (sum != in_cksum(ip, ip->ip_hl * 4)) + return -1; + + mtrace = (struct igmp_mtrace *)(mtrace_buf + (4 * ip->ip_hl)); + + mtrace_len = ntohs(ip->ip_len) - ip->ip_hl * 4; + + if (mtrace_len < (int)MTRACE_HDR_SIZE) + return -1; + + sum = mtrace->checksum; + mtrace->checksum = 0; + if (sum != in_cksum(mtrace, mtrace_len)) { + fprintf(stderr, "mtrace checksum wrong\n"); + return -1; + } + + if (mtrace->type != PIM_IGMP_MTRACE_RESPONSE) + return -1; + + + responses = mtrace_len - sizeof(struct igmp_mtrace); + responses /= sizeof(struct igmp_mtrace_rsp); + + printf("%ld ms received responses from %d hops.\n", msec, responses); + + if (hops) + *hops = responses; + + for (i = 0; i < responses; i++) { + struct igmp_mtrace_rsp *rsp = &mtrace->rsp[i]; + + if (rsp->fwd_code != 0) + printf("-%d fwd. code 0x%2x.\n", i, rsp->fwd_code); + } + + return 0; +} + +static int wait_for_response(int fd, int *hops) +{ + fd_set readfds; + struct timeval timeout; + int ret = -1; + long msec, rmsec, tmsec; + + FD_ZERO(&readfds); + FD_SET(fd, &readfds); + + memset(&timeout, 0, sizeof(timeout)); + + timeout.tv_sec = MTRACE_TIMEOUT; + + tmsec = timeout.tv_sec * 1000 + timeout.tv_usec / 1000; + do { + ret = select(fd + 1, &readfds, NULL, NULL, &timeout); + if (ret <= 0) + return ret; + rmsec = timeout.tv_sec * 1000 + timeout.tv_usec / 1000; + msec = tmsec - rmsec; + } while (recv_response(fd, msec, hops) != 0); + + return ret; +} + +int main(int argc, char *const argv[]) +{ + struct in_addr mc_source; + struct in_addr iface_addr; + struct in_addr gw_addr; + struct in_addr mtrace_addr; + struct igmp_mtrace mtrace; + int hops = 255; + int rhops; + int maxhops = 255; + int perhop = 3; + int ifindex; + int unicast = 1; + int ttl = 64; + int fd = -1; + int ret = -1; + int c; + int i, j; + char ifname[IF_NAMESIZE]; + char ip_str[INET_ADDRSTRLEN]; + + mtrace_addr.s_addr = inet_addr("224.0.1.32"); + + uid_t uid = getuid(); + + if (uid != 0) { + printf("must run as root\n"); + exit(EXIT_FAILURE); + } + + if (argc <= 0) + progname = "mtracebis"; + else + progname = argv[0]; + + if (argc != 2) { + usage(); + exit(EXIT_FAILURE); + } + + while (1) { + static struct option long_options[] = { + {"help", no_argument, 0, 'h'}, + {"version", no_argument, 0, 'v'}, + {0, 0, 0, 0} }; + int option_index = 0; + + c = getopt_long(argc, argv, "vh", long_options, &option_index); + + if (c == -1) + break; + + switch (c) { + case 'h': + usage(); + exit(0); + case 'v': + version(); + exit(0); + default: + usage(); + exit(EXIT_FAILURE); + } + } + if (inet_pton(AF_INET, argv[1], &mc_source) != 1) { + usage(); + fprintf(stderr, "%s: %s not a valid IPv4 address\n", argv[0], + argv[1]); + exit(EXIT_FAILURE); + } + + ifindex = routeget(mc_source, &iface_addr, &gw_addr); + if (ifindex < 0) { + fprintf(stderr, "%s: failed to get route to source %s\n", + argv[0], argv[1]); + exit(EXIT_FAILURE); + } + + if (if_indextoname(ifindex, ifname) == NULL) { + fprintf(stderr, "%s: if_indextoname error: %s\n", argv[0], + strerror(errno)); + exit(EXIT_FAILURE); + } + + /* zero mtrace struct */ + memset((char *)&mtrace, 0, sizeof(mtrace)); + + /* set up query */ + mtrace.type = PIM_IGMP_MTRACE_QUERY_REQUEST; + mtrace.hops = hops; + mtrace.checksum = 0; + mtrace.grp_addr.s_addr = 0; + mtrace.src_addr = mc_source; + mtrace.dst_addr = iface_addr; + mtrace.rsp_addr = unicast ? iface_addr : mtrace_addr; + mtrace.rsp_ttl = ttl; + mtrace.qry_id = 0xffffff & time(NULL); + + mtrace.checksum = in_cksum(&mtrace, sizeof(mtrace)); + + fd = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP); + + if (fd < 1) { + fprintf(stderr, "%s: socket error: %s\n", argv[0], + strerror(errno)); + exit(EXIT_FAILURE); + } + + ret = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, ifname, + strlen(ifname)); + + if (ret < 0) { + fprintf(stderr, "%s: setsockopt error: %s\n", argv[0], + strerror(errno)); + ret = EXIT_FAILURE; + goto close_fd; + } + + print_query(&mtrace); + if (send_query(fd, gw_addr, &mtrace) < 0) { + fprintf(stderr, "%s: sendto error: %s\n", argv[0], + strerror(errno)); + ret = EXIT_FAILURE; + goto close_fd; + } + printf("Querying full reverse path...\n"); + ret = wait_for_response(fd, NULL); + if (ret > 0) { + ret = 0; + goto close_fd; + } + if (ret < 0) { + fprintf(stderr, "%s: select error: %s\n", argv[0], + strerror(errno)); + ret = EXIT_FAILURE; + goto close_fd; + } + printf(" * "); + printf("switching to hop-by-hop:\n"); + printf("%3d ? (%s)\n", 0, + inet_ntop(AF_INET, &mtrace.dst_addr, ip_str, sizeof(ip_str))); + for (i = 1; i < maxhops; i++) { + printf("%3d ", -i); + mtrace.hops = i; + for (j = 0; j < perhop; j++) { + mtrace.qry_id++; + mtrace.checksum = 0; + mtrace.checksum = in_cksum(&mtrace, sizeof(mtrace)); + if (send_query(fd, gw_addr, &mtrace) < 0) { + fprintf(stderr, "%s: sendto error: %s\n", + argv[0], strerror(errno)); + ret = EXIT_FAILURE; + goto close_fd; + } + ret = wait_for_response(fd, &rhops); + if (ret > 0) { + if (i > rhops) { + ret = 0; + goto close_fd; + } + break; + } + printf(" *"); + } + if (ret <= 0) + printf("\n"); + } + ret = 0; +close_fd: + close(fd); + exit(ret); +} + +#else /* __linux__ */ + +#include +#include + +int main(int argc, char *argv[]) +{ + printf("%s implemented only for GNU/Linux\n", argv[0]); + exit(0); +} + +#endif /* __linux__ */ diff --git a/pimd/mtracebis_netlink.c b/pimd/mtracebis_netlink.c new file mode 100644 index 0000000000..42b80b218b --- /dev/null +++ b/pimd/mtracebis_netlink.c @@ -0,0 +1,700 @@ +/* + * libnetlink.c RTnetlink service routines. + * + * 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. + * + * Authors: Alexey Kuznetsov, + * + */ + +#ifdef __linux__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mtracebis_netlink.h" + +int rcvbuf = 1024 * 1024; + +void rtnl_close(struct rtnl_handle *rth) +{ + if (rth->fd >= 0) { + close(rth->fd); + rth->fd = -1; + } +} + +int rtnl_open_byproto(struct rtnl_handle *rth, unsigned subscriptions, + int protocol) +{ + socklen_t addr_len; + int sndbuf = 32768; + + memset(rth, 0, sizeof(*rth)); + + rth->fd = socket(AF_NETLINK, SOCK_RAW, protocol); + if (rth->fd < 0) { + perror("Cannot open netlink socket"); + return -1; + } + + if (setsockopt(rth->fd,SOL_SOCKET,SO_SNDBUF,&sndbuf,sizeof(sndbuf)) < 0) { + perror("SO_SNDBUF"); + return -1; + } + + if (setsockopt(rth->fd,SOL_SOCKET,SO_RCVBUF,&rcvbuf,sizeof(rcvbuf)) < 0) { + perror("SO_RCVBUF"); + return -1; + } + + memset(&rth->local, 0, sizeof(rth->local)); + rth->local.nl_family = AF_NETLINK; + rth->local.nl_groups = subscriptions; + + if (bind(rth->fd, (struct sockaddr*)&rth->local, sizeof(rth->local)) < 0) { + perror("Cannot bind netlink socket"); + return -1; + } + addr_len = sizeof(rth->local); + if (getsockname(rth->fd, (struct sockaddr*)&rth->local, &addr_len) < 0) { + perror("Cannot getsockname"); + return -1; + } + if (addr_len != sizeof(rth->local)) { + fprintf(stderr, "Wrong address length %d\n", addr_len); + return -1; + } + if (rth->local.nl_family != AF_NETLINK) { + fprintf(stderr, "Wrong address family %d\n", rth->local.nl_family); + return -1; + } + rth->seq = time(NULL); + return 0; +} + +int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions) +{ + return rtnl_open_byproto(rth, subscriptions, NETLINK_ROUTE); +} + +int rtnl_wilddump_request(struct rtnl_handle *rth, int family, int type) +{ + struct { + struct nlmsghdr nlh; + struct rtgenmsg g; + } req; + + memset(&req, 0, sizeof(req)); + req.nlh.nlmsg_len = sizeof(req); + req.nlh.nlmsg_type = type; + req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST; + req.nlh.nlmsg_pid = 0; + req.nlh.nlmsg_seq = rth->dump = ++rth->seq; + req.g.rtgen_family = family; + + return send(rth->fd, (void*)&req, sizeof(req), 0); +} + +int rtnl_send(struct rtnl_handle *rth, const char *buf, int len) +{ + return send(rth->fd, buf, len, 0); +} + +int rtnl_send_check(struct rtnl_handle *rth, const char *buf, int len) +{ + struct nlmsghdr *h; + int status; + char resp[1024]; + + status = send(rth->fd, buf, len, 0); + if (status < 0) + return status; + + /* Check for immediate errors */ + status = recv(rth->fd, resp, sizeof(resp), MSG_DONTWAIT|MSG_PEEK); + if (status < 0) { + if (errno == EAGAIN) + return 0; + return -1; + } + + for (h = (struct nlmsghdr *)resp; NLMSG_OK(h, (uint32_t)status); + h = NLMSG_NEXT(h, status)) { + if (h->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h); + if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) + fprintf(stderr, "ERROR truncated\n"); + else + errno = -err->error; + return -1; + } + } + + return 0; +} + +int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len) +{ + struct nlmsghdr nlh; + struct sockaddr_nl nladdr; + struct iovec iov[2] = { + { .iov_base = &nlh, .iov_len = sizeof(nlh) }, + { .iov_base = req, .iov_len = len } + }; + struct msghdr msg = { + .msg_name = &nladdr, + .msg_namelen = sizeof(nladdr), + .msg_iov = iov, + .msg_iovlen = 2, + }; + + memset(&nladdr, 0, sizeof(nladdr)); + nladdr.nl_family = AF_NETLINK; + + nlh.nlmsg_len = NLMSG_LENGTH(len); + nlh.nlmsg_type = type; + nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST; + nlh.nlmsg_pid = 0; + nlh.nlmsg_seq = rth->dump = ++rth->seq; + + return sendmsg(rth->fd, &msg, 0); +} + +int rtnl_dump_filter_l(struct rtnl_handle *rth, + const struct rtnl_dump_filter_arg *arg) +{ + struct sockaddr_nl nladdr; + struct iovec iov; + struct msghdr msg = { + .msg_name = &nladdr, + .msg_namelen = sizeof(nladdr), + .msg_iov = &iov, + .msg_iovlen = 1, + }; + char buf[16384]; + + iov.iov_base = buf; + while (1) { + int status; + const struct rtnl_dump_filter_arg *a; + int found_done = 0; + int msglen = 0; + + iov.iov_len = sizeof(buf); + status = recvmsg(rth->fd, &msg, 0); + + if (status < 0) { + if (errno == EINTR || errno == EAGAIN) + continue; + fprintf(stderr, "netlink receive error %s (%d)\n", + strerror(errno), errno); + return -1; + } + + if (status == 0) { + fprintf(stderr, "EOF on netlink\n"); + return -1; + } + + for (a = arg; a->filter; a++) { + struct nlmsghdr *h = (struct nlmsghdr*)buf; + msglen = status; + + while (NLMSG_OK(h, (uint32_t)msglen)) { + int err; + + if (nladdr.nl_pid != 0 || + h->nlmsg_pid != rth->local.nl_pid || + h->nlmsg_seq != rth->dump) { + if (a->junk) { + err = a->junk(&nladdr, h, + a->arg2); + if (err < 0) + return err; + } + goto skip_it; + } + + if (h->nlmsg_type == NLMSG_DONE) { + found_done = 1; + break; /* process next filter */ + } + if (h->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h); + if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) { + fprintf(stderr, + "ERROR truncated\n"); + } else { + errno = -err->error; + perror("RTNETLINK answers"); + } + return -1; + } + err = a->filter(&nladdr, h, a->arg1); + if (err < 0) + return err; + +skip_it: + h = NLMSG_NEXT(h, msglen); + } + } + + if (found_done) + return 0; + + if (msg.msg_flags & MSG_TRUNC) { + fprintf(stderr, "Message truncated\n"); + continue; + } + if (msglen) { + fprintf(stderr, "!!!Remnant of size %d\n", msglen); + exit(1); + } + } +} + +int rtnl_dump_filter(struct rtnl_handle *rth, + rtnl_filter_t filter, + void *arg1, + rtnl_filter_t junk, + void *arg2) +{ + const struct rtnl_dump_filter_arg a[2] = { + { .filter = filter, .arg1 = arg1, .junk = junk, .arg2 = arg2 }, + { .filter = NULL, .arg1 = NULL, .junk = NULL, .arg2 = NULL } + }; + + return rtnl_dump_filter_l(rth, a); +} + +int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer, + unsigned groups, struct nlmsghdr *answer, + rtnl_filter_t junk, + void *jarg) +{ + int status; + unsigned seq; + struct nlmsghdr *h; + struct sockaddr_nl nladdr; + struct iovec iov = { + .iov_base = (void*) n, + .iov_len = n->nlmsg_len + }; + struct msghdr msg = { + .msg_name = &nladdr, + .msg_namelen = sizeof(nladdr), + .msg_iov = &iov, + .msg_iovlen = 1, + }; + char buf[16384]; + + memset(&nladdr, 0, sizeof(nladdr)); + nladdr.nl_family = AF_NETLINK; + nladdr.nl_pid = peer; + nladdr.nl_groups = groups; + + n->nlmsg_seq = seq = ++rtnl->seq; + + if (answer == NULL) + n->nlmsg_flags |= NLM_F_ACK; + + status = sendmsg(rtnl->fd, &msg, 0); + + if (status < 0) { + perror("Cannot talk to rtnetlink"); + return -1; + } + + memset(buf,0,sizeof(buf)); + + iov.iov_base = buf; + + while (1) { + iov.iov_len = sizeof(buf); + status = recvmsg(rtnl->fd, &msg, 0); + + if (status < 0) { + if (errno == EINTR || errno == EAGAIN) + continue; + fprintf(stderr, "netlink receive error %s (%d)\n", + strerror(errno), errno); + return -1; + } + if (status == 0) { + fprintf(stderr, "EOF on netlink\n"); + return -1; + } + if (msg.msg_namelen != sizeof(nladdr)) { + fprintf(stderr, "sender address length == %d\n", msg.msg_namelen); + exit(1); + } + for (h = (struct nlmsghdr*)buf; status >= (int)sizeof(*h); ) { + int err; + int len = h->nlmsg_len; + int l = len - sizeof(*h); + + if (l<0 || len>status) { + if (msg.msg_flags & MSG_TRUNC) { + fprintf(stderr, "Truncated message\n"); + return -1; + } + fprintf(stderr, "!!!malformed message: len=%d\n", len); + exit(1); + } + + if ((int)nladdr.nl_pid != peer || + h->nlmsg_pid != rtnl->local.nl_pid || + h->nlmsg_seq != seq) { + if (junk) { + err = junk(&nladdr, h, jarg); + if (err < 0) + return err; + } + /* Don't forget to skip that message. */ + status -= NLMSG_ALIGN(len); + h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len)); + continue; + } + + if (h->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h); + if (l < (int)sizeof(struct nlmsgerr)) { + fprintf(stderr, "ERROR truncated\n"); + } else { + errno = -err->error; + if (errno == 0) { + if (answer) + memcpy(answer, h, h->nlmsg_len); + return 0; + } + perror("RTNETLINK answers"); + } + return -1; + } + if (answer) { + memcpy(answer, h, h->nlmsg_len); + return 0; + } + + fprintf(stderr, "Unexpected reply!!!\n"); + + status -= NLMSG_ALIGN(len); + h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len)); + } + if (msg.msg_flags & MSG_TRUNC) { + fprintf(stderr, "Message truncated\n"); + continue; + } + if (status) { + fprintf(stderr, "!!!Remnant of size %d\n", status); + exit(1); + } + } +} + +int rtnl_listen(struct rtnl_handle *rtnl, + rtnl_filter_t handler, + void *jarg) +{ + int status; + struct nlmsghdr *h; + struct sockaddr_nl nladdr; + struct iovec iov; + struct msghdr msg = { + .msg_name = &nladdr, + .msg_namelen = sizeof(nladdr), + .msg_iov = &iov, + .msg_iovlen = 1, + }; + char buf[8192]; + + memset(&nladdr, 0, sizeof(nladdr)); + nladdr.nl_family = AF_NETLINK; + nladdr.nl_pid = 0; + nladdr.nl_groups = 0; + + iov.iov_base = buf; + while (1) { + iov.iov_len = sizeof(buf); + status = recvmsg(rtnl->fd, &msg, 0); + + if (status < 0) { + if (errno == EINTR || errno == EAGAIN) + continue; + fprintf(stderr, "netlink receive error %s (%d)\n", + strerror(errno), errno); + if (errno == ENOBUFS) + continue; + return -1; + } + if (status == 0) { + fprintf(stderr, "EOF on netlink\n"); + return -1; + } + if (msg.msg_namelen != sizeof(nladdr)) { + fprintf(stderr, "Sender address length == %d\n", msg.msg_namelen); + exit(1); + } + for (h =(struct nlmsghdr*)buf; status >= (int)sizeof(*h); ) { + int err; + int len = h->nlmsg_len; + int l = len - sizeof(*h); + + if (l<0 || len>status) { + if (msg.msg_flags & MSG_TRUNC) { + fprintf(stderr, "Truncated message\n"); + return -1; + } + fprintf(stderr, "!!!malformed message: len=%d\n", len); + exit(1); + } + + err = handler(&nladdr, h, jarg); + if (err < 0) + return err; + + status -= NLMSG_ALIGN(len); + h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len)); + } + if (msg.msg_flags & MSG_TRUNC) { + fprintf(stderr, "Message truncated\n"); + continue; + } + if (status) { + fprintf(stderr, "!!!Remnant of size %d\n", status); + exit(1); + } + } +} + +int rtnl_from_file(FILE *rtnl, rtnl_filter_t handler, + void *jarg) +{ + int status; + struct sockaddr_nl nladdr; + char buf[8192]; + struct nlmsghdr *h = (void*)buf; + + memset(&nladdr, 0, sizeof(nladdr)); + nladdr.nl_family = AF_NETLINK; + nladdr.nl_pid = 0; + nladdr.nl_groups = 0; + + while (1) { + int err, len; + int l; + + status = fread(&buf, 1, sizeof(*h), rtnl); + + if (status < 0) { + if (errno == EINTR) + continue; + perror("rtnl_from_file: fread"); + return -1; + } + if (status == 0) + return 0; + + len = h->nlmsg_len; + l = len - sizeof(*h); + + if (l<0 || len>(int)sizeof(buf)) { + fprintf(stderr, "!!!malformed message: len=%d @%lu\n", + len, ftell(rtnl)); + return -1; + } + + status = fread(NLMSG_DATA(h), 1, NLMSG_ALIGN(l), rtnl); + + if (status < 0) { + perror("rtnl_from_file: fread"); + return -1; + } + if (status < l) { + fprintf(stderr, "rtnl-from_file: truncated message\n"); + return -1; + } + + err = handler(&nladdr, h, jarg); + if (err < 0) + return err; + } +} + +int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data) +{ + int len = RTA_LENGTH(4); + struct rtattr *rta; + if ((int)(NLMSG_ALIGN(n->nlmsg_len) + len) > maxlen) { + fprintf(stderr,"addattr32: Error! max allowed bound %d exceeded\n",maxlen); + return -1; + } + rta = NLMSG_TAIL(n); + rta->rta_type = type; + rta->rta_len = len; + memcpy(RTA_DATA(rta), &data, 4); + n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len; + return 0; +} + +int addattr_l(struct nlmsghdr *n, int maxlen, int type, const void *data, + int alen) +{ + int len = RTA_LENGTH(alen); + struct rtattr *rta; + + if ((int)(NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len)) > maxlen) { + fprintf(stderr, "addattr_l ERROR: message exceeded bound of %d\n",maxlen); + return -1; + } + rta = NLMSG_TAIL(n); + rta->rta_type = type; + rta->rta_len = len; + + if (data) + memcpy(RTA_DATA(rta), data, alen); + else + assert(alen == 0); + + n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len); + return 0; +} + +int addraw_l(struct nlmsghdr *n, int maxlen, const void *data, int len) +{ + if ((int)(NLMSG_ALIGN(n->nlmsg_len) + NLMSG_ALIGN(len)) > maxlen) { + fprintf(stderr, "addraw_l ERROR: message exceeded bound of %d\n",maxlen); + return -1; + } + + memcpy(NLMSG_TAIL(n), data, len); + memset((uint8_t *) NLMSG_TAIL(n) + len, 0, NLMSG_ALIGN(len) - len); + n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + NLMSG_ALIGN(len); + return 0; +} + +struct rtattr *addattr_nest(struct nlmsghdr *n, int maxlen, int type) +{ + struct rtattr *nest = NLMSG_TAIL(n); + + addattr_l(n, maxlen, type, NULL, 0); + return nest; +} + +int addattr_nest_end(struct nlmsghdr *n, struct rtattr *nest) +{ + nest->rta_len = (uint8_t *)NLMSG_TAIL(n) - (uint8_t *)nest; + return n->nlmsg_len; +} + +struct rtattr *addattr_nest_compat(struct nlmsghdr *n, int maxlen, int type, + const void *data, int len) +{ + struct rtattr *start = NLMSG_TAIL(n); + + addattr_l(n, maxlen, type, data, len); + addattr_nest(n, maxlen, type); + return start; +} + +int addattr_nest_compat_end(struct nlmsghdr *n, struct rtattr *start) +{ + struct rtattr *nest = start + NLMSG_ALIGN(start->rta_len); + + start->rta_len = (uint8_t *)NLMSG_TAIL(n) - (uint8_t *)start; + addattr_nest_end(n, nest); + return n->nlmsg_len; +} + +int rta_addattr32(struct rtattr *rta, int maxlen, int type, __u32 data) +{ + int len = RTA_LENGTH(4); + struct rtattr *subrta; + + if ((int)(RTA_ALIGN(rta->rta_len) + len) > maxlen) { + fprintf(stderr,"rta_addattr32: Error! max allowed bound %d exceeded\n",maxlen); + return -1; + } + subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len)); + subrta->rta_type = type; + subrta->rta_len = len; + memcpy(RTA_DATA(subrta), &data, 4); + rta->rta_len = NLMSG_ALIGN(rta->rta_len) + len; + return 0; +} + +int rta_addattr_l(struct rtattr *rta, int maxlen, int type, + const void *data, int alen) +{ + struct rtattr *subrta; + int len = RTA_LENGTH(alen); + + if ((int)(RTA_ALIGN(rta->rta_len) + RTA_ALIGN(len)) > maxlen) { + fprintf(stderr,"rta_addattr_l: Error! max allowed bound %d exceeded\n",maxlen); + return -1; + } + subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len)); + subrta->rta_type = type; + subrta->rta_len = len; + memcpy(RTA_DATA(subrta), data, alen); + rta->rta_len = NLMSG_ALIGN(rta->rta_len) + RTA_ALIGN(len); + return 0; +} + +int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len) +{ + memset(tb, 0, sizeof(struct rtattr *) * (max + 1)); + while (RTA_OK(rta, len)) { + if ((rta->rta_type <= max) && (!tb[rta->rta_type])) + tb[rta->rta_type] = rta; + rta = RTA_NEXT(rta,len); + } + if (len) + fprintf(stderr, "!!!Deficit %d, rta_len=%d\n", len, rta->rta_len); + return 0; +} + +int parse_rtattr_byindex(struct rtattr *tb[], int max, struct rtattr *rta, int len) +{ + int i = 0; + + memset(tb, 0, sizeof(struct rtattr *) * max); + while (RTA_OK(rta, len)) { + if (rta->rta_type <= max && i < max) + tb[i++] = rta; + rta = RTA_NEXT(rta,len); + } + if (len) + fprintf(stderr, "!!!Deficit %d, rta_len=%d\n", len, rta->rta_len); + return i; +} + +int __parse_rtattr_nested_compat(struct rtattr *tb[], int max, struct rtattr *rta, + int len) +{ + if ((int)RTA_PAYLOAD(rta) < len) + return -1; + if (RTA_PAYLOAD(rta) >= RTA_ALIGN(len) + sizeof(struct rtattr)) { + rta = (struct rtattr *)(uint8_t *)RTA_DATA(rta)+RTA_ALIGN(len); + return parse_rtattr_nested(tb, max, rta); + } + memset(tb, 0, sizeof(struct rtattr *) * (max + 1)); + return 0; +} + +#endif /* __linux__ */ diff --git a/pimd/mtracebis_netlink.h b/pimd/mtracebis_netlink.h new file mode 100644 index 0000000000..7a60ead975 --- /dev/null +++ b/pimd/mtracebis_netlink.h @@ -0,0 +1,130 @@ +/* + * libnetlink.c RTnetlink service routines. + * + * 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. + * + * Authors: Alexey Kuznetsov, + * + */ + +#ifdef __linux__ + +#ifndef __LIBNETLINK_H__ +#define __LIBNETLINK_H__ 1 + +#include +#include +#include +#include +#include +#include + +struct rtnl_handle +{ + int fd; + struct sockaddr_nl local; + struct sockaddr_nl peer; + __u32 seq; + __u32 dump; +}; + +extern int rcvbuf; + +extern int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions); +extern int rtnl_open_byproto(struct rtnl_handle *rth, unsigned subscriptions, int protocol); +extern void rtnl_close(struct rtnl_handle *rth); +extern int rtnl_wilddump_request(struct rtnl_handle *rth, int fam, int type); +extern int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len); + +typedef int (*rtnl_filter_t)(const struct sockaddr_nl *, + struct nlmsghdr *n, void *); + +struct rtnl_dump_filter_arg +{ + rtnl_filter_t filter; + void *arg1; + rtnl_filter_t junk; + void *arg2; +}; + +extern int rtnl_dump_filter_l(struct rtnl_handle *rth, + const struct rtnl_dump_filter_arg *arg); +extern int rtnl_dump_filter(struct rtnl_handle *rth, rtnl_filter_t filter, + void *arg1, + rtnl_filter_t junk, + void *arg2); + +extern int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer, + unsigned groups, struct nlmsghdr *answer, + rtnl_filter_t junk, + void *jarg); +extern int rtnl_send(struct rtnl_handle *rth, const char *buf, int); +extern int rtnl_send_check(struct rtnl_handle *rth, const char *buf, int); + +extern int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data); +extern int addattr_l(struct nlmsghdr *n, int maxlen, int type, const void *data, int alen); +extern int addraw_l(struct nlmsghdr *n, int maxlen, const void *data, int len); +extern struct rtattr *addattr_nest(struct nlmsghdr *n, int maxlen, int type); +extern int addattr_nest_end(struct nlmsghdr *n, struct rtattr *nest); +extern struct rtattr *addattr_nest_compat(struct nlmsghdr *n, int maxlen, int type, const void *data, int len); +extern int addattr_nest_compat_end(struct nlmsghdr *n, struct rtattr *nest); +extern int rta_addattr32(struct rtattr *rta, int maxlen, int type, __u32 data); +extern int rta_addattr_l(struct rtattr *rta, int maxlen, int type, const void *data, int alen); + +extern int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len); +extern int parse_rtattr_byindex(struct rtattr *tb[], int max, struct rtattr *rta, int len); +extern int __parse_rtattr_nested_compat(struct rtattr *tb[], int max, struct rtattr *rta, int len); + +#define parse_rtattr_nested(tb, max, rta) \ + (parse_rtattr((tb), (max), RTA_DATA(rta), RTA_PAYLOAD(rta))) + +#define parse_rtattr_nested_compat(tb, max, rta, data, len) \ +({ data = RTA_PAYLOAD(rta) >= len ? RTA_DATA(rta) : NULL; \ + __parse_rtattr_nested_compat(tb, max, rta, len); }) + +extern int rtnl_listen(struct rtnl_handle *, rtnl_filter_t handler, + void *jarg); +extern int rtnl_from_file(FILE *, rtnl_filter_t handler, + void *jarg); + +#define NLMSG_TAIL(nmsg) \ + ((struct rtattr *) (((uint8_t *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len))) + +#ifndef IFA_RTA +#define IFA_RTA(r) \ + ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg)))) +#endif +#ifndef IFA_PAYLOAD +#define IFA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifaddrmsg)) +#endif + +#ifndef IFLA_RTA +#define IFLA_RTA(r) \ + ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifinfomsg)))) +#endif +#ifndef IFLA_PAYLOAD +#define IFLA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifinfomsg)) +#endif + +#ifndef NDA_RTA +#define NDA_RTA(r) \ + ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndmsg)))) +#endif +#ifndef NDA_PAYLOAD +#define NDA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ndmsg)) +#endif + +#ifndef NDTA_RTA +#define NDTA_RTA(r) \ + ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndtmsg)))) +#endif +#ifndef NDTA_PAYLOAD +#define NDTA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ndtmsg)) +#endif + +#endif /* __LIBNETLINK_H__ */ + +#endif /* __linux__ */ diff --git a/pimd/mtracebis_routeget.c b/pimd/mtracebis_routeget.c new file mode 100644 index 0000000000..d75aaa3708 --- /dev/null +++ b/pimd/mtracebis_routeget.c @@ -0,0 +1,98 @@ +/* + * Multicast Traceroute for FRRouting + * Copyright (C) 2018 Mladen Sablic + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifdef __linux__ + +#include +#include +#include +#include +#include +#include +#include + +#include "mtracebis_netlink.h" +#include "mtracebis_routeget.h" + +static int find_dst(struct nlmsghdr *n, struct in_addr *src, struct in_addr *gw) +{ + struct rtmsg *r = NLMSG_DATA(n); + int len = n->nlmsg_len; + struct rtattr *tb[RTA_MAX + 1]; + + len -= NLMSG_LENGTH(sizeof(*r)); + if (len < 0) { + fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); + return -1; + } + + parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len); + if (tb[RTA_PREFSRC]) + src->s_addr = *(uint32_t *)RTA_DATA(tb[RTA_PREFSRC]); + if (tb[RTA_GATEWAY]) + gw->s_addr = *(uint32_t *)RTA_DATA(tb[RTA_GATEWAY]); + if (tb[RTA_OIF]) + return *(int *)RTA_DATA(tb[RTA_OIF]); + return 0; +} + +int routeget(struct in_addr dst, struct in_addr *src, struct in_addr *gw) +{ + struct { + struct nlmsghdr n; + struct rtmsg r; + char buf[1024]; + } req; + int ret; + struct rtnl_handle rth = {.fd = -1}; + + memset(&req, 0, sizeof(req)); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); + req.n.nlmsg_flags = NLM_F_REQUEST; + req.n.nlmsg_type = RTM_GETROUTE; + req.r.rtm_family = AF_INET; + req.r.rtm_table = 0; + req.r.rtm_protocol = 0; + req.r.rtm_scope = 0; + req.r.rtm_type = 0; + req.r.rtm_src_len = 0; + req.r.rtm_dst_len = 0; + req.r.rtm_tos = 0; + + addattr_l(&req.n, sizeof(req), RTA_DST, &dst.s_addr, 4); + req.r.rtm_dst_len = 32; + + ret = rtnl_open(&rth, 0); + + if (ret < 0) + return ret; + + if (rtnl_talk(&rth, &req.n, 0, 0, &req.n, NULL, NULL) < 0) { + ret = -1; + goto close_rth; + } + + ret = find_dst(&req.n, src, gw); +close_rth: + rtnl_close(&rth); + return ret; +} + +#endif /* __linux__ */ diff --git a/pimd/mtracebis_routeget.h b/pimd/mtracebis_routeget.h new file mode 100644 index 0000000000..18ecf6ebfa --- /dev/null +++ b/pimd/mtracebis_routeget.h @@ -0,0 +1,31 @@ +/* + * Multicast Traceroute for FRRouting + * Copyright (C) 2018 Mladen Sablic + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifdef __linux__ + +#ifndef ROUTEGET_H +#define ROUTEGET_H + +#include + +int routeget(struct in_addr dst, struct in_addr *src, struct in_addr *gw); + +#endif /* ROUTEGET */ + +#endif /* __linux__ */ diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index 11aeeddf93..fc07b706a9 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -7311,6 +7311,27 @@ DEFUN (no_debug_msdp_packets, ALIAS(no_debug_msdp_packets, undebug_msdp_packets_cmd, "undebug msdp packets", UNDEBUG_STR DEBUG_MSDP_STR DEBUG_MSDP_PACKETS_STR) +DEFUN (debug_mtrace, + debug_mtrace_cmd, + "debug mtrace", + DEBUG_STR + DEBUG_MTRACE_STR) +{ + PIM_DO_DEBUG_MTRACE; + return CMD_SUCCESS; +} + +DEFUN (no_debug_mtrace, + no_debug_mtrace_cmd, + "no debug mtrace", + NO_STR + DEBUG_STR + DEBUG_MTRACE_STR) +{ + PIM_DONT_DEBUG_MTRACE; + return CMD_SUCCESS; +} + DEFUN_NOSH (show_debugging_pim, show_debugging_pim_cmd, "show debugging [pim]", @@ -8721,6 +8742,8 @@ void pim_cmd_init(void) install_element(ENABLE_NODE, &debug_msdp_packets_cmd); install_element(ENABLE_NODE, &no_debug_msdp_packets_cmd); install_element(ENABLE_NODE, &undebug_msdp_packets_cmd); + install_element(ENABLE_NODE, &debug_mtrace_cmd); + install_element(ENABLE_NODE, &no_debug_mtrace_cmd); install_element(CONFIG_NODE, &debug_igmp_cmd); install_element(CONFIG_NODE, &no_debug_igmp_cmd); @@ -8763,6 +8786,8 @@ void pim_cmd_init(void) install_element(CONFIG_NODE, &debug_msdp_packets_cmd); install_element(CONFIG_NODE, &no_debug_msdp_packets_cmd); install_element(CONFIG_NODE, &undebug_msdp_packets_cmd); + install_element(CONFIG_NODE, &debug_mtrace_cmd); + install_element(CONFIG_NODE, &no_debug_mtrace_cmd); install_element(CONFIG_NODE, &ip_msdp_mesh_group_member_cmd); install_element(VRF_NODE, &ip_msdp_mesh_group_member_cmd); diff --git a/pimd/pim_cmd.h b/pimd/pim_cmd.h index 8867514876..b58da30bdd 100644 --- a/pimd/pim_cmd.h +++ b/pimd/pim_cmd.h @@ -63,6 +63,7 @@ #define DEBUG_MSDP_EVENTS_STR "MSDP protocol events\n" #define DEBUG_MSDP_INTERNAL_STR "MSDP protocol internal\n" #define DEBUG_MSDP_PACKETS_STR "MSDP protocol packets\n" +#define DEBUG_MTRACE_STR "Mtrace protocol activity\n" void pim_cmd_init(void); diff --git a/pimd/pim_igmp.c b/pimd/pim_igmp.c index 7524119e52..0522420364 100644 --- a/pimd/pim_igmp.c +++ b/pimd/pim_igmp.c @@ -29,6 +29,7 @@ #include "pim_igmp.h" #include "pim_igmpv2.h" #include "pim_igmpv3.h" +#include "pim_igmp_mtrace.h" #include "pim_iface.h" #include "pim_sock.h" #include "pim_mroute.h" @@ -504,6 +505,16 @@ int pim_igmp_packet(struct igmp_sock *igmp, char *buf, size_t len) case PIM_IGMP_V2_LEAVE_GROUP: return igmp_v2_recv_leave(igmp, ip_hdr->ip_src, from_str, igmp_msg, igmp_msg_len); + + case PIM_IGMP_MTRACE_RESPONSE: + return igmp_mtrace_recv_response(igmp, ip_hdr, ip_hdr->ip_src, + from_str, igmp_msg, + igmp_msg_len); + break; + case PIM_IGMP_MTRACE_QUERY_REQUEST: + return igmp_mtrace_recv_qry_req(igmp, ip_hdr, ip_hdr->ip_src, + from_str, igmp_msg, + igmp_msg_len); } zlog_warn("Ignoring unsupported IGMP message type: %d", msg_type); diff --git a/pimd/pim_igmp.h b/pimd/pim_igmp.h index 275f25f63f..962c50e76a 100644 --- a/pimd/pim_igmp.h +++ b/pimd/pim_igmp.h @@ -37,6 +37,8 @@ #define PIM_IGMP_V1_MEMBERSHIP_REPORT (0x12) #define PIM_IGMP_V2_MEMBERSHIP_REPORT (0x16) #define PIM_IGMP_V2_LEAVE_GROUP (0x17) +#define PIM_IGMP_MTRACE_RESPONSE (0x1E) +#define PIM_IGMP_MTRACE_QUERY_REQUEST (0x1F) #define PIM_IGMP_V3_MEMBERSHIP_REPORT (0x22) #define IGMP_V3_REPORT_HEADER_SIZE (8) diff --git a/pimd/pim_igmp_mtrace.c b/pimd/pim_igmp_mtrace.c new file mode 100644 index 0000000000..629e10cb0c --- /dev/null +++ b/pimd/pim_igmp_mtrace.c @@ -0,0 +1,727 @@ +/* + * Multicast traceroute for FRRouting + * Copyright (C) 2017 Mladen Sablic + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "pimd.h" +#include "pim_util.h" +#include "pim_sock.h" +#include "pim_rp.h" +#include "pim_oil.h" +#include "pim_ifchannel.h" +#include "pim_macro.h" +#include "pim_igmp_mtrace.h" + +static void mtrace_rsp_init(struct igmp_mtrace_rsp *mtrace_rspp) +{ + mtrace_rspp->arrival = 0; + mtrace_rspp->incoming.s_addr = 0; + mtrace_rspp->outgoing.s_addr = 0; + mtrace_rspp->prev_hop.s_addr = 0; + mtrace_rspp->in_count = MTRACE_UNKNOWN_COUNT; + mtrace_rspp->out_count = MTRACE_UNKNOWN_COUNT; + mtrace_rspp->total = MTRACE_UNKNOWN_COUNT; + mtrace_rspp->rtg_proto = 0; + mtrace_rspp->fwd_ttl = 0; + mtrace_rspp->mbz = 0; + mtrace_rspp->s = 0; + mtrace_rspp->src_mask = 0; + mtrace_rspp->fwd_code = MTRACE_FWD_CODE_NO_ERROR; +} + +static void mtrace_rsp_debug(uint32_t qry_id, int rsp, + struct igmp_mtrace_rsp *mrspp) +{ + char inc_str[INET_ADDRSTRLEN]; + char out_str[INET_ADDRSTRLEN]; + char prv_str[INET_ADDRSTRLEN]; + + zlog_debug( + "Rx mt(%d) qid=%ud arr=%x in=%s out=%s prev=%s proto=%d fwd=%d", + rsp, ntohl(qry_id), mrspp->arrival, + inet_ntop(AF_INET, &(mrspp->incoming), inc_str, + sizeof(inc_str)), + inet_ntop(AF_INET, &(mrspp->outgoing), out_str, + sizeof(out_str)), + inet_ntop(AF_INET, &(mrspp->prev_hop), prv_str, + sizeof(prv_str)), + mrspp->rtg_proto, mrspp->fwd_code); +} + +static void mtrace_debug(struct pim_interface *pim_ifp, + struct igmp_mtrace *mtracep, int mtrace_len) +{ + char inc_str[INET_ADDRSTRLEN]; + char grp_str[INET_ADDRSTRLEN]; + char src_str[INET_ADDRSTRLEN]; + char dst_str[INET_ADDRSTRLEN]; + char rsp_str[INET_ADDRSTRLEN]; + + zlog_debug( + "Rx mtrace packet incoming on %s: " + "hops=%d type=%d size=%d, grp=%s, src=%s," + " dst=%s rsp=%s ttl=%d qid=%ud", + inet_ntop(AF_INET, &(pim_ifp->primary_address), inc_str, + sizeof(inc_str)), + mtracep->hops, mtracep->type, mtrace_len, + inet_ntop(AF_INET, &(mtracep->grp_addr), grp_str, + sizeof(grp_str)), + inet_ntop(AF_INET, &(mtracep->src_addr), src_str, + sizeof(src_str)), + inet_ntop(AF_INET, &(mtracep->dst_addr), dst_str, + sizeof(dst_str)), + inet_ntop(AF_INET, &(mtracep->rsp_addr), rsp_str, + sizeof(rsp_str)), + mtracep->rsp_ttl, ntohl(mtracep->qry_id)); + if (mtrace_len > (int)sizeof(struct igmp_mtrace)) { + + int i; + + int responses = mtrace_len - sizeof(struct igmp_mtrace); + + if ((responses % sizeof(struct igmp_mtrace_rsp)) != 0) + if (PIM_DEBUG_MTRACE) + zlog_debug( + "Mtrace response block of wrong" + " length"); + + responses = responses / sizeof(struct igmp_mtrace_rsp); + + for (i = 0; i < responses; i++) + mtrace_rsp_debug(mtracep->qry_id, i, &mtracep->rsp[i]); + } +} + +/* 5.1 Query Arrival Time */ +static uint32_t query_arrival_time(void) +{ + struct timeval tv; + uint32_t qat; + + char m_qat[] = "Query arrival time lookup failed: errno=%d: %s"; + + if (gettimeofday(&tv, NULL) < 0) { + if (PIM_DEBUG_MTRACE) + zlog_warn(m_qat, errno, safe_strerror(errno)); + return 0; + } + /* not sure second offset correct, as I get different value */ + qat = ((tv.tv_sec + 32384) << 16) + ((tv.tv_usec << 10) / 15625); + + return qat; +} + +static int mtrace_send_packet(struct interface *ifp, + struct igmp_mtrace *mtracep, + size_t mtrace_buf_len, struct in_addr dst_addr, + struct in_addr group_addr) +{ + struct sockaddr_in to; + struct pim_interface *pim_ifp; + socklen_t tolen; + ssize_t sent; + int ret; + int fd; + char pim_str[INET_ADDRSTRLEN]; + char rsp_str[INET_ADDRSTRLEN]; + u_char ttl; + + pim_ifp = ifp->info; + + memset(&to, 0, sizeof(to)); + to.sin_family = AF_INET; + to.sin_addr = dst_addr; + tolen = sizeof(to); + + if (PIM_DEBUG_MTRACE) + zlog_debug("Sending mtrace packet to %s on %s", + inet_ntop(AF_INET, &mtracep->rsp_addr, rsp_str, + sizeof(rsp_str)), + inet_ntop(AF_INET, &pim_ifp->primary_address, + pim_str, sizeof(pim_str))); + + fd = pim_socket_raw(IPPROTO_IGMP); + + if (fd < 0) + return -1; + + ret = pim_socket_bind(fd, ifp); + + if (ret < 0) { + ret = -1; + goto close_fd; + } + + if (IPV4_CLASS_DE(ntohl(dst_addr.s_addr))) { + if (IPV4_MC_LINKLOCAL(ntohl(dst_addr.s_addr))) { + ttl = 1; + } else { + if (mtracep->type == PIM_IGMP_MTRACE_RESPONSE) + ttl = mtracep->rsp_ttl; + else + ttl = 64; + } + ret = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, + sizeof(ttl)); + + if (ret < 0) { + if (PIM_DEBUG_MTRACE) + zlog_warn("Failed to set socket multicast TTL"); + ret = -1; + goto close_fd; + } + } + + sent = sendto(fd, (char *)mtracep, mtrace_buf_len, MSG_DONTWAIT, + (struct sockaddr *)&to, tolen); + + if (sent != (ssize_t)mtrace_buf_len) { + char dst_str[INET_ADDRSTRLEN]; + char group_str[INET_ADDRSTRLEN]; + + pim_inet4_dump("", dst_addr, dst_str, sizeof(dst_str)); + pim_inet4_dump("", group_addr, group_str, + sizeof(group_str)); + if (sent < 0) { + if (PIM_DEBUG_MTRACE) + zlog_warn( + "Send mtrace request failed for %s on" + "%s: group=%s msg_size=%zd: errno=%d: " + " %s", + dst_str, ifp->name, group_str, + mtrace_buf_len, errno, + safe_strerror(errno)); + } else { + if (PIM_DEBUG_MTRACE) + zlog_warn( + "Send mtrace request failed for %s on" + " %s: group=%s msg_size=%zd: sent=%zd", + dst_str, ifp->name, group_str, + mtrace_buf_len, sent); + } + ret = -1; + goto close_fd; + } + ret = 0; +close_fd: + close(fd); + return ret; +} + +static int mtrace_un_forward_packet(struct pim_instance *pim, struct ip *ip_hdr, + struct interface *interface) +{ + struct pim_nexthop nexthop; + struct sockaddr_in to; + struct interface *if_out; + socklen_t tolen; + int ret; + int fd; + int sent; + uint16_t checksum; + + checksum = ip_hdr->ip_sum; + + ip_hdr->ip_sum = 0; + + if (checksum != in_cksum(ip_hdr, ip_hdr->ip_hl * 4)) + return -1; + + if (ip_hdr->ip_ttl-- <= 1) + return -1; + + ip_hdr->ip_sum = in_cksum(ip_hdr, ip_hdr->ip_hl * 4); + + fd = pim_socket_raw(IPPROTO_RAW); + + if (fd < 0) + return -1; + + pim_socket_ip_hdr(fd); + + if (interface == NULL) { + ret = pim_nexthop_lookup(pim, &nexthop, ip_hdr->ip_dst, 0); + + if (ret != 0) { + if (PIM_DEBUG_MTRACE) + zlog_warn( + "Dropping mtrace packet, " + "no route to destination"); + return -1; + } + + if_out = nexthop.interface; + } else { + if_out = interface; + } + + ret = pim_socket_bind(fd, if_out); + + if (ret < 0) { + close(fd); + return -1; + } + + memset(&to, 0, sizeof(to)); + to.sin_family = AF_INET; + to.sin_addr = ip_hdr->ip_dst; + tolen = sizeof(to); + + sent = sendto(fd, ip_hdr, ntohs(ip_hdr->ip_len), 0, + (struct sockaddr *)&to, tolen); + + close(fd); + + if (sent < 0) { + if (PIM_DEBUG_MTRACE) + zlog_warn( + "Failed to forward mtrace packet:" + " sendto errno=%d, %s", + errno, safe_strerror(errno)); + return -1; + } + + if (PIM_DEBUG_MTRACE) { + zlog_debug("Fwd mtrace packet len=%u to %s ttl=%u", + ntohs(ip_hdr->ip_len), inet_ntoa(ip_hdr->ip_dst), + ip_hdr->ip_ttl); + } + + return 0; +} + +static int mtrace_mc_forward_packet(struct pim_instance *pim, struct ip *ip_hdr) +{ + struct prefix_sg sg; + struct channel_oil *c_oil; + struct listnode *chnode; + struct listnode *chnextnode; + struct pim_ifchannel *ch = NULL; + int ret = -1; + + memset(&sg, 0, sizeof(struct prefix_sg)); + sg.grp = ip_hdr->ip_dst; + + c_oil = pim_find_channel_oil(pim, &sg); + + if (c_oil == NULL) { + if (PIM_DEBUG_MTRACE) { + zlog_debug( + "Dropping mtrace multicast packet " + "len=%u to %s ttl=%u", + ntohs(ip_hdr->ip_len), + inet_ntoa(ip_hdr->ip_dst), ip_hdr->ip_ttl); + } + return -1; + } + if (c_oil->up == NULL) + return -1; + if (c_oil->up->ifchannels == NULL) + return -1; + for (ALL_LIST_ELEMENTS(c_oil->up->ifchannels, chnode, chnextnode, ch)) { + if (pim_macro_chisin_oiflist(ch)) { + int r; + + r = mtrace_un_forward_packet(pim, ip_hdr, + ch->interface); + if (r == 0) + ret = 0; + } + } + return ret; +} + + +static int mtrace_forward_packet(struct pim_instance *pim, struct ip *ip_hdr) +{ + if (IPV4_CLASS_DE(ntohl(ip_hdr->ip_dst.s_addr))) + return mtrace_mc_forward_packet(pim, ip_hdr); + else + return mtrace_un_forward_packet(pim, ip_hdr, NULL); +} + +/* 6.5 Sending Traceroute Responses */ +static int mtrace_send_mc_response(struct pim_instance *pim, + struct igmp_mtrace *mtracep, + size_t mtrace_len) +{ + struct prefix_sg sg; + struct channel_oil *c_oil; + struct listnode *chnode; + struct listnode *chnextnode; + struct pim_ifchannel *ch = NULL; + int ret = -1; + + memset(&sg, 0, sizeof(struct prefix_sg)); + sg.grp = mtracep->rsp_addr; + + c_oil = pim_find_channel_oil(pim, &sg); + + if (c_oil == NULL) { + if (PIM_DEBUG_MTRACE) { + zlog_debug( + "Dropping mtrace multicast response packet " + "len=%u to %s", + (unsigned int)mtrace_len, + inet_ntoa(mtracep->rsp_addr)); + } + return -1; + } + if (c_oil->up == NULL) + return -1; + if (c_oil->up->ifchannels == NULL) + return -1; + for (ALL_LIST_ELEMENTS(c_oil->up->ifchannels, chnode, chnextnode, ch)) { + if (pim_macro_chisin_oiflist(ch)) { + int r; + + r = mtrace_send_packet(ch->interface, mtracep, + mtrace_len, mtracep->rsp_addr, + mtracep->grp_addr); + if (r == 0) + ret = 0; + } + } + return ret; +} + +static int mtrace_send_response(struct pim_instance *pim, + struct igmp_mtrace *mtracep, size_t mtrace_len) +{ + struct pim_nexthop nexthop; + int ret; + + mtracep->type = PIM_IGMP_MTRACE_RESPONSE; + + mtracep->checksum = 0; + mtracep->checksum = in_cksum((char *)mtracep, mtrace_len); + + if (IPV4_CLASS_DE(ntohl(mtracep->rsp_addr.s_addr))) { + struct pim_rpf *p_rpf; + char grp_str[INET_ADDRSTRLEN]; + + if (pim_rp_i_am_rp(pim, mtracep->rsp_addr)) + return mtrace_send_mc_response(pim, mtracep, + mtrace_len); + + p_rpf = pim_rp_g(pim, mtracep->rsp_addr); + + if (p_rpf == NULL) { + if (PIM_DEBUG_MTRACE) + zlog_warn("mtrace no RP for %s", + inet_ntop(AF_INET, + &(mtracep->rsp_addr), + grp_str, sizeof(grp_str))); + return -1; + } + nexthop = p_rpf->source_nexthop; + if (PIM_DEBUG_MTRACE) + zlog_debug("mtrace response to RP"); + } else { + /* TODO: should use unicast rib lookup */ + ret = pim_nexthop_lookup(pim, &nexthop, mtracep->rsp_addr, 1); + + if (ret != 0) { + if (PIM_DEBUG_MTRACE) + zlog_warn( + "Dropped response qid=%ud, no route to " + "response address", + mtracep->qry_id); + return -1; + } + } + + return mtrace_send_packet(nexthop.interface, mtracep, mtrace_len, + mtracep->rsp_addr, mtracep->grp_addr); +} + +int igmp_mtrace_recv_qry_req(struct igmp_sock *igmp, struct ip *ip_hdr, + struct in_addr from, const char *from_str, + char *igmp_msg, int igmp_msg_len) +{ + static uint32_t qry_id, qry_src; + char mtrace_buf[MTRACE_HDR_SIZE + MTRACE_MAX_HOPS * MTRACE_RSP_SIZE]; + struct pim_nexthop nexthop; + struct interface *ifp; + struct interface *out_ifp; + struct pim_interface *pim_ifp; + struct pim_interface *pim_out_ifp; + struct pim_instance *pim; + struct igmp_mtrace *mtracep; + struct igmp_mtrace_rsp *rspp; + struct in_addr nh_addr; + enum mtrace_fwd_code fwd_code = MTRACE_FWD_CODE_NO_ERROR; + int ret; + size_t r_len; + int last_rsp_ind = 0; + size_t mtrace_len; + uint16_t recv_checksum; + uint16_t checksum; + + ifp = igmp->interface; + pim_ifp = ifp->info; + pim = pim_ifp->pim; + + /* + * 6. Router Behaviour + * Check if mtrace packet is addressed elsewhere and forward, + * if applicable + */ + if (!IPV4_CLASS_DE(ntohl(ip_hdr->ip_dst.s_addr))) + if (!if_lookup_exact_address(&ip_hdr->ip_dst, AF_INET, + pim->vrf_id)) + return mtrace_forward_packet(pim, ip_hdr); + + if (igmp_msg_len < (int)sizeof(struct igmp_mtrace)) { + if (PIM_DEBUG_MTRACE) + zlog_warn( + "Recv mtrace packet from %s on %s: too short," + " len=%d, min=%lu", + from_str, ifp->name, igmp_msg_len, + sizeof(struct igmp_mtrace)); + return -1; + } + + mtracep = (struct igmp_mtrace *)igmp_msg; + + recv_checksum = mtracep->checksum; + + mtracep->checksum = 0; + + checksum = in_cksum(igmp_msg, igmp_msg_len); + + if (recv_checksum != checksum) { + if (PIM_DEBUG_MTRACE) + zlog_warn( + "Recv mtrace packet from %s on %s: checksum" + " mismatch: received=%x computed=%x", + from_str, ifp->name, recv_checksum, checksum); + return -1; + } + + if (PIM_DEBUG_MTRACE) + mtrace_debug(pim_ifp, mtracep, igmp_msg_len); + + /* subtract header from message length */ + r_len = igmp_msg_len - sizeof(struct igmp_mtrace); + + /* Classify mtrace packet, check if it is a query */ + if (!r_len) { + if (PIM_DEBUG_MTRACE) + zlog_debug("Received IGMP multicast traceroute query"); + + /* 6.1.1 Packet verification */ + if (!pim_if_connected_to_source(ifp, mtracep->dst_addr)) { + if (IPV4_CLASS_DE(ntohl(ip_hdr->ip_dst.s_addr))) { + if (PIM_DEBUG_MTRACE) + zlog_debug( + "Dropping multicast query " + "on wrong interface"); + return -1; + } + /* Unicast query on wrong interface */ + fwd_code = MTRACE_FWD_CODE_WRONG_IF; + } + if (qry_id == mtracep->qry_id && qry_src == from.s_addr) { + if (PIM_DEBUG_MTRACE) + zlog_debug( + "Dropping multicast query with " + "duplicate source and id"); + return -1; + } + qry_id = mtracep->qry_id; + qry_src = from.s_addr; + } + /* if response fields length is equal to a whole number of responses */ + else if ((r_len % sizeof(struct igmp_mtrace_rsp)) == 0) { + r_len = igmp_msg_len - sizeof(struct igmp_mtrace); + + if (r_len != 0) + last_rsp_ind = r_len / sizeof(struct igmp_mtrace_rsp); + if (last_rsp_ind > MTRACE_MAX_HOPS) { + if (PIM_DEBUG_MTRACE) + zlog_warn("Mtrace request of excessive size"); + return -1; + } + } else { + if (PIM_DEBUG_MTRACE) + zlog_warn( + "Recv mtrace packet from %s on %s: " + "invalid length %d", + from_str, ifp->name, igmp_msg_len); + return -1; + } + + /* 6.2.1 Packet Verification - drop not link-local multicast */ + if (IPV4_CLASS_DE(ntohl(ip_hdr->ip_dst.s_addr)) + && !IPV4_MC_LINKLOCAL(ntohl(ip_hdr->ip_dst.s_addr))) { + if (PIM_DEBUG_MTRACE) + zlog_warn( + "Recv mtrace packet from %s on %s:" + " not link-local multicast %s", + from_str, ifp->name, inet_ntoa(ip_hdr->ip_dst)); + return -1; + } + + /* 6.2.2. Normal Processing */ + + /* 6.2.2. 1. */ + + if (last_rsp_ind == MTRACE_MAX_HOPS) { + mtracep->rsp[MTRACE_MAX_HOPS - 1].fwd_code = + MTRACE_FWD_CODE_NO_SPACE; + return mtrace_send_response(pim_ifp->pim, mtracep, + igmp_msg_len); + } + + /* calculate new mtrace mtrace lenght with extra response */ + mtrace_len = igmp_msg_len + sizeof(struct igmp_mtrace_rsp); + + /* copy received query/request */ + memcpy(mtrace_buf, igmp_msg, igmp_msg_len); + + /* repoint mtracep pointer to copy */ + mtracep = (struct igmp_mtrace *)mtrace_buf; + + /* pointer for extra response field to be filled in */ + rspp = &mtracep->rsp[last_rsp_ind]; + + /* initialize extra response field */ + mtrace_rsp_init(rspp); + + rspp->arrival = htonl(query_arrival_time()); + rspp->outgoing = pim_ifp->primary_address; + rspp->out_count = htonl(MTRACE_UNKNOWN_COUNT); + + /* 6.2.2. 2. Attempt to determine forwarding information */ + + nh_addr.s_addr = 0; + + ret = pim_nexthop_lookup(pim, &nexthop, mtracep->src_addr, 1); + + if (ret == 0) { + char nexthop_str[INET_ADDRSTRLEN]; + + if (PIM_DEBUG_MTRACE) + zlog_debug("mtrace pim_nexthop_lookup OK"); + + if (PIM_DEBUG_MTRACE) + zlog_warn("mtrace next_hop=%s", + inet_ntop(nexthop.mrib_nexthop_addr.family, + &nexthop.mrib_nexthop_addr.u.prefix, + nexthop_str, sizeof(nexthop_str))); + + if (nexthop.mrib_nexthop_addr.family == AF_INET) + nh_addr = nexthop.mrib_nexthop_addr.u.prefix4; + } + /* 6.4 Forwarding Traceroute Requests: ... Otherwise, ... */ + else { + if (PIM_DEBUG_MTRACE) + zlog_debug("mtrace not found neighbor"); + if (!fwd_code) + rspp->fwd_code = MTRACE_FWD_CODE_NO_ROUTE; + else + rspp->fwd_code = fwd_code; + /* 6.5 Sending Traceroute Responses */ + return mtrace_send_response(pim, mtracep, mtrace_len); + } + + out_ifp = nexthop.interface; + pim_out_ifp = out_ifp->info; + + rspp->incoming = pim_out_ifp->primary_address; + rspp->prev_hop = nh_addr; + rspp->in_count = htonl(MTRACE_UNKNOWN_COUNT); + rspp->total = htonl(MTRACE_UNKNOWN_COUNT); + rspp->rtg_proto = MTRACE_RTG_PROTO_PIM; + rspp->s = 1; + rspp->src_mask = 32; + + if (nh_addr.s_addr == 0) { + /* reached source? */ + if (pim_if_connected_to_source(out_ifp, mtracep->src_addr)) + return mtrace_send_response(pim, mtracep, mtrace_len); + /* + * 6.4 Forwarding Traceroute Requests: + * Previous-hop router not known + */ + inet_aton(MCAST_ALL_ROUTERS, &nh_addr); + } + + if (mtracep->hops <= (last_rsp_ind + 1)) + return mtrace_send_response(pim, mtracep, mtrace_len); + + mtracep->checksum = 0; + + mtracep->checksum = in_cksum(mtrace_buf, mtrace_len); + + return mtrace_send_packet(out_ifp, mtracep, mtrace_len, nh_addr, + mtracep->grp_addr); +} + +int igmp_mtrace_recv_response(struct igmp_sock *igmp, struct ip *ip_hdr, + struct in_addr from, const char *from_str, + char *igmp_msg, int igmp_msg_len) +{ + static uint32_t qry_id, rsp_dst; + struct interface *ifp; + struct pim_interface *pim_ifp; + struct pim_instance *pim; + struct igmp_mtrace *mtracep; + uint16_t recv_checksum; + uint16_t checksum; + + ifp = igmp->interface; + pim_ifp = ifp->info; + pim = pim_ifp->pim; + + mtracep = (struct igmp_mtrace *)igmp_msg; + + recv_checksum = mtracep->checksum; + + mtracep->checksum = 0; + + checksum = in_cksum(igmp_msg, igmp_msg_len); + + if (recv_checksum != checksum) { + if (PIM_DEBUG_MTRACE) + zlog_warn( + "Recv mtrace response from %s on %s: checksum" + " mismatch: received=%x computed=%x", + from_str, ifp->name, recv_checksum, checksum); + return -1; + } + + mtracep->checksum = checksum; + + if (PIM_DEBUG_MTRACE) + mtrace_debug(pim_ifp, mtracep, igmp_msg_len); + + /* Drop duplicate packets */ + if (qry_id == mtracep->qry_id && rsp_dst == ip_hdr->ip_dst.s_addr) { + if (PIM_DEBUG_MTRACE) + zlog_debug("duplicate mtrace response packet dropped"); + return -1; + } + + qry_id = mtracep->qry_id; + rsp_dst = ip_hdr->ip_dst.s_addr; + + return mtrace_forward_packet(pim, ip_hdr); +} diff --git a/pimd/pim_igmp_mtrace.h b/pimd/pim_igmp_mtrace.h new file mode 100644 index 0000000000..b5c1008444 --- /dev/null +++ b/pimd/pim_igmp_mtrace.h @@ -0,0 +1,103 @@ +/* + * Multicast traceroute for FRRouting + * Copyright (C) 2017 Mladen Sablic + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef PIM_IGMP_MTRACE_H +#define PIM_IGMP_MTRACE_H + +#include + +#include "pim_igmp.h" + +#define MTRACE_MAX_HOPS (255) +#define MTRACE_UNKNOWN_COUNT (0xffffffff) + +enum mtrace_fwd_code { + MTRACE_FWD_CODE_NO_ERROR = 0x00, + MTRACE_FWD_CODE_WRONG_IF = 0x01, + MTRACE_FWD_CODE_PRUNE_SENT = 0x02, + MTRACE_FWD_CODE_PRUNE_RCVD = 0x03, + MTRACE_FWD_CODE_SCOPED = 0x04, + MTRACE_FWD_CODE_NO_ROUTE = 0x05, + MTRACE_FWD_CODE_WRONG_LAST_HOP = 0x06, + MTRACE_FWD_CODE_NOT_FORWARDING = 0x07, + MTRACE_FWD_CODE_REACHED_RP = 0x08, + MTRACE_FWD_CODE_RPF_IF = 0x09, + MTRACE_FWD_CODE_NO_MULTICAST = 0x0A, + MTRACE_FWD_CODE_INFO_HIDDEN = 0x0B, + MTRACE_FWD_CODE_NO_SPACE = 0x81, + MTRACE_FWD_CODE_OLD_ROUTER = 0x82, + MTRACE_FWD_CODE_ADMIN_PROHIB = 0x83 +}; + +enum mtrace_rtg_proto { + MTRACE_RTG_PROTO_DVMRP = 1, + MTRACE_RTG_PROTO_MOSPF = 2, + MTRACE_RTG_PROTO_PIM = 3, + MTRACE_RTG_PROTO_CBT = 4, + MTRACE_RTG_PROTO_PIM_SPECIAL = 5, + MTRACE_RTG_PROTO_PIM_STATIC = 6, + MTRACE_RTG_PROTO_DVMRP_STATIC = 7, + MTRACE_RTG_PROTO_PIM_MBGP = 8, + MTRACE_RTG_PROTO_CBT_SPECIAL = 9, + MTRACE_RTG_PROTO_CBT_STATIC = 10, + MTRACE_RTG_PROTO_PIM_ASSERT = 11, +}; + +struct igmp_mtrace_rsp { + uint32_t arrival; + struct in_addr incoming; + struct in_addr outgoing; + struct in_addr prev_hop; + uint32_t in_count; + uint32_t out_count; + uint32_t total; + uint32_t rtg_proto : 8; + uint32_t fwd_ttl : 8; + /* little endian order for next three fields */ + uint32_t src_mask : 6; + uint32_t s : 1; + uint32_t mbz : 1; + uint32_t fwd_code : 8; +} __attribute__((packed)); + +struct igmp_mtrace { + uint8_t type; + uint8_t hops; + uint16_t checksum; + struct in_addr grp_addr; + struct in_addr src_addr; + struct in_addr dst_addr; + struct in_addr rsp_addr; + uint32_t rsp_ttl : 8; + uint32_t qry_id : 24; + struct igmp_mtrace_rsp rsp[0]; +} __attribute__((packed)); + +#define MTRACE_HDR_SIZE (sizeof(struct igmp_mtrace)) +#define MTRACE_RSP_SIZE (sizeof(struct igmp_mtrace_rsp)) + +int igmp_mtrace_recv_qry_req(struct igmp_sock *igmp, struct ip *ip_hdr, + struct in_addr from, const char *from_str, + char *igmp_msg, int igmp_msg_len); + +int igmp_mtrace_recv_response(struct igmp_sock *igmp, struct ip *ip_hdr, + struct in_addr from, const char *from_str, + char *igmp_msg, int igmp_msg_len); + +#endif /* PIM_IGMP_MTRACE_H */ diff --git a/pimd/pim_oil.c b/pimd/pim_oil.c index c45b0ce14c..53bbf54f3e 100644 --- a/pimd/pim_oil.c +++ b/pimd/pim_oil.c @@ -135,8 +135,8 @@ void pim_channel_oil_free(struct channel_oil *c_oil) XFREE(MTYPE_PIM_CHANNEL_OIL, c_oil); } -static struct channel_oil *pim_find_channel_oil(struct pim_instance *pim, - struct prefix_sg *sg) +struct channel_oil *pim_find_channel_oil(struct pim_instance *pim, + struct prefix_sg *sg) { struct channel_oil *c_oil = NULL; struct channel_oil lookup; diff --git a/pimd/pim_oil.h b/pimd/pim_oil.h index 1168ba0a8f..94d3840e98 100644 --- a/pimd/pim_oil.h +++ b/pimd/pim_oil.h @@ -86,6 +86,8 @@ void pim_oil_init(struct pim_instance *pim); void pim_oil_terminate(struct pim_instance *pim); void pim_channel_oil_free(struct channel_oil *c_oil); +struct channel_oil *pim_find_channel_oil(struct pim_instance *pim, + struct prefix_sg *sg); struct channel_oil *pim_channel_oil_add(struct pim_instance *pim, struct prefix_sg *sg, int input_vif_index); diff --git a/pimd/pim_vty.c b/pimd/pim_vty.c index 791680a911..688bc42c3d 100644 --- a/pimd/pim_vty.c +++ b/pimd/pim_vty.c @@ -78,6 +78,11 @@ int pim_debug_config_write(struct vty *vty) ++writes; } + if (PIM_DEBUG_MTRACE) { + vty_out(vty, "debug mtrace\n"); + ++writes; + } + if (PIM_DEBUG_MROUTE_DETAIL) { vty_out(vty, "debug mroute detail\n"); ++writes; diff --git a/pimd/pimd.h b/pimd/pimd.h index cd00a2df46..de7f259319 100644 --- a/pimd/pimd.h +++ b/pimd/pimd.h @@ -112,6 +112,7 @@ #define PIM_MASK_PIM_NHT (1 << 22) #define PIM_MASK_PIM_NHT_DETAIL (1 << 23) #define PIM_MASK_PIM_NHT_RP (1 << 24) +#define PIM_MASK_MTRACE (1 << 25) /* Remember 32 bits!!! */ /* PIM error codes */ @@ -188,6 +189,7 @@ extern int32_t qpim_register_probe_time; #define PIM_DEBUG_PIM_NHT (qpim_debugs & PIM_MASK_PIM_NHT) #define PIM_DEBUG_PIM_NHT_DETAIL (qpim_debugs & PIM_MASK_PIM_NHT_DETAIL) #define PIM_DEBUG_PIM_NHT_RP (qpim_debugs & PIM_MASK_PIM_NHT_RP) +#define PIM_DEBUG_MTRACE (qpim_debugs & PIM_MASK_MTRACE) #define PIM_DEBUG_EVENTS (qpim_debugs & (PIM_MASK_PIM_EVENTS | PIM_MASK_IGMP_EVENTS | PIM_MASK_MSDP_EVENTS)) #define PIM_DEBUG_PACKETS (qpim_debugs & (PIM_MASK_PIM_PACKETS | PIM_MASK_IGMP_PACKETS | PIM_MASK_MSDP_PACKETS)) @@ -216,6 +218,7 @@ extern int32_t qpim_register_probe_time; #define PIM_DO_DEBUG_MSDP_INTERNAL (qpim_debugs |= PIM_MASK_MSDP_INTERNAL) #define PIM_DO_DEBUG_PIM_NHT (qpim_debugs |= PIM_MASK_PIM_NHT) #define PIM_DO_DEBUG_PIM_NHT_RP (qpim_debugs |= PIM_MASK_PIM_NHT_RP) +#define PIM_DO_DEBUG_MTRACE (qpim_debugs |= PIM_MASK_MTRACE) #define PIM_DONT_DEBUG_PIM_EVENTS (qpim_debugs &= ~PIM_MASK_PIM_EVENTS) #define PIM_DONT_DEBUG_PIM_PACKETS (qpim_debugs &= ~PIM_MASK_PIM_PACKETS) @@ -240,6 +243,7 @@ extern int32_t qpim_register_probe_time; #define PIM_DONT_DEBUG_MSDP_INTERNAL (qpim_debugs &= ~PIM_MASK_MSDP_INTERNAL) #define PIM_DONT_DEBUG_PIM_NHT (qpim_debugs &= ~PIM_MASK_PIM_NHT) #define PIM_DONT_DEBUG_PIM_NHT_RP (qpim_debugs &= ~PIM_MASK_PIM_NHT_RP) +#define PIM_DONT_DEBUG_MTRACE (qpim_debugs &= ~PIM_MASK_MTRACE) void pim_init(void); void pim_terminate(void); diff --git a/pimd/subdir.am b/pimd/subdir.am index 2fcb061576..2254362221 100644 --- a/pimd/subdir.am +++ b/pimd/subdir.am @@ -5,6 +5,7 @@ if PIMD noinst_LIBRARIES += pimd/libpim.a sbin_PROGRAMS += pimd/pimd +bin_PROGRAMS += pimd/mtracebis noinst_PROGRAMS += pimd/test_igmpv3_join dist_examples_DATA += pimd/pimd.conf.sample endif @@ -18,6 +19,7 @@ pimd_libpim_a_SOURCES = \ pimd/pim_iface.c \ pimd/pim_ifchannel.c \ pimd/pim_igmp.c \ + pimd/pim_igmp_mtrace.c \ pimd/pim_igmpv2.c \ pimd/pim_igmpv3.c \ pimd/pim_instance.c \ @@ -66,6 +68,7 @@ noinst_HEADERS += \ pimd/pim_ifchannel.h \ pimd/pim_igmp.h \ pimd/pim_igmp_join.h \ + pimd/pim_igmp_mtrace.h \ pimd/pim_igmpv2.h \ pimd/pim_igmpv3.h \ pimd/pim_instance.h \ @@ -101,6 +104,8 @@ noinst_HEADERS += \ pimd/pim_zebra.h \ pimd/pim_zlookup.h \ pimd/pimd.h \ + pimd/mtracebis_netlink.h \ + pimd/mtracebis_routeget.h \ # end pimd_pimd_LDADD = pimd/libpim.a lib/libfrr.la @LIBCAP@ @@ -108,3 +113,9 @@ pimd_pimd_SOURCES = pimd/pim_main.c pimd_test_igmpv3_join_LDADD = lib/libfrr.la pimd_test_igmpv3_join_SOURCES = pimd/test_igmpv3_join.c + +pimd_mtracebis_LDADD = lib/libfrr.la +pimd_mtracebis_SOURCES = pimd/mtracebis.c \ + pimd/mtracebis_netlink.c \ + pimd/mtracebis_routeget.c \ + # end diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index 94c4ba4330..65e9c9f8c5 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -2628,6 +2628,19 @@ ALIAS(vtysh_traceroute, vtysh_traceroute_ip_cmd, "traceroute ip WORD", "IP trace\n" "Trace route to destination address or hostname\n") +DEFUN (vtysh_mtrace, + vtysh_mtrace_cmd, + "mtrace WORD", + "Multicast trace route to multicast source\n" + "Multicast trace route to multicast source address\n") +{ + int idx = 1; + + argv_find(argv, argc, "WORD", &idx); + execute_command("mtracebis", 1, argv[idx]->arg, NULL); + return CMD_SUCCESS; +} + DEFUN (vtysh_ping6, vtysh_ping6_cmd, "ping ipv6 WORD", @@ -3327,6 +3340,7 @@ void vtysh_init_vty(void) install_element(VIEW_NODE, &vtysh_ping_ip_cmd); install_element(VIEW_NODE, &vtysh_traceroute_cmd); install_element(VIEW_NODE, &vtysh_traceroute_ip_cmd); + install_element(VIEW_NODE, &vtysh_mtrace_cmd); install_element(VIEW_NODE, &vtysh_ping6_cmd); install_element(VIEW_NODE, &vtysh_traceroute6_cmd); #if defined(HAVE_SHELL_ACCESS) From 7c557e5b3da8b8ba1db9542bc24bbf2a70bd1f1a Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Tue, 20 Feb 2018 12:37:41 -0500 Subject: [PATCH 22/98] *: move random tools into tools/ Signed-off-by: Quentin Young --- git-reindent-branch.py => tools/git-reindent-branch.py | 0 indent.py => tools/indent.py | 0 render_md.py => tools/render_md.py | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename git-reindent-branch.py => tools/git-reindent-branch.py (100%) rename indent.py => tools/indent.py (100%) rename render_md.py => tools/render_md.py (100%) diff --git a/git-reindent-branch.py b/tools/git-reindent-branch.py similarity index 100% rename from git-reindent-branch.py rename to tools/git-reindent-branch.py diff --git a/indent.py b/tools/indent.py similarity index 100% rename from indent.py rename to tools/indent.py diff --git a/render_md.py b/tools/render_md.py similarity index 100% rename from render_md.py rename to tools/render_md.py From 2ccf91b10848bed00f3424ac276f8549609791d4 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Wed, 21 Feb 2018 12:01:34 -0500 Subject: [PATCH 23/98] bgpd: fix incorrect keepalive timer evaluation Incorrect check for sentinel value effectively caused peers to sometimes use the keepalive timer value of other peers, which sometimes led to hold timer expiry. Signed-off-by: Quentin Young --- bgpd/bgp_keepalives.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bgpd/bgp_keepalives.c b/bgpd/bgp_keepalives.c index 5a48c7013e..1504893c47 100644 --- a/bgpd/bgp_keepalives.c +++ b/bgpd/bgp_keepalives.c @@ -119,7 +119,7 @@ static void peer_process(struct hash_backet *hb, void *arg) } /* if calculated next update for this peer < current delay, use it */ - if (next_update->tv_sec <= 0 || timercmp(&diff, next_update, <)) + if (next_update->tv_sec < 0 || timercmp(&diff, next_update, <)) *next_update = diff; } From b43444f53a3c21e116c66fd66fb776ffcb727205 Mon Sep 17 00:00:00 2001 From: Don Slice Date: Wed, 21 Feb 2018 10:45:05 -0800 Subject: [PATCH 24/98] zebra: fix rnh deleting nht entry Problem seen when a prefix was learned with nexthops from multiple route sources (static and ospf in this case) and the link to that nexthop flaps. The nht entry was incorrectly deleted so when the link came back up the static was not re-installed correctly. Ticket: CM-19675 Signed-off-by: Don Slice --- zebra/zebra_rib.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 967bc92850..c134028b9c 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -416,8 +416,6 @@ static int nexthop_active(afi_t afi, struct route_entry *re, if (set) { UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE); - zebra_deregister_rnh_static_nexthops(nexthop->vrf_id, - nexthop->resolved, top); nexthops_free(nexthop->resolved); nexthop->resolved = NULL; re->nexthop_mtu = 0; @@ -2115,7 +2113,9 @@ void rib_unlink(struct route_node *rn, struct route_entry *re) dest->selected_fib = NULL; /* free RE and nexthops */ - zebra_deregister_rnh_static_nexthops(re->vrf_id, re->nexthop, rn); + if (re->type == ZEBRA_ROUTE_STATIC) + zebra_deregister_rnh_static_nexthops(re->vrf_id, + re->nexthop, rn); nexthops_free(re->nexthop); XFREE(MTYPE_RE, re); } From 0908f2bfc73c9e419ebc0c212609544291793daf Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Thu, 22 Feb 2018 08:48:45 -0500 Subject: [PATCH 25/98] FRR: Update Community.md to reflect release process This is the new release process for FRR. Signed-off-by: Donald Sharp --- COMMUNITY.md | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/COMMUNITY.md b/COMMUNITY.md index 0eee524d58..081f7ab3b8 100644 --- a/COMMUNITY.md +++ b/COMMUNITY.md @@ -492,6 +492,36 @@ In all cases, compatibility pieces should be marked with compiler/preprocessor annotations to print warnings at compile time, pointing to the appropriate update path. A `-Werror` build should fail if compatibility bits are used. +### Release Process/Schedule + +FRR employs a .. versioning scheme. + +* MAJOR - Significant new features or multiple minor features + A example of a MAJOR feature is a New Routing Protocol +* MINOR - Smaller Features + A example of a MINOR feature is the addition of the BGP Shutdown feature. +* BUGFIX - Fixes for actual bugs and/or security issues. + +We will pull a new development branch for the next release every 4 months. +The current schedule is Feb/June/October 1. The decision for a MAJOR/MINOR +release is made at the time of branch pull based on what has been received +the previous 4 months. The branch name will be dev/MAJOR.MINOR. At +this point in time the master branch configure.ac and packaging systems +will be updated to reflect the next possible release name to allow +for easy distinguishing. Additionally the new dev branch will have +these files updated too. + +After one month the development branch will be renamed to +stable/MAJOR.MINOR. This process is not held up unless a crash or +security issue has been found and needs to be addressed. Issues +being fixed will not cause a delay. + +Bug fix releases are at 1 month intervals until next MAJOR.MINOR is +pulled. Then at that time as needed for issues filed. + +Security issues are fixed for 1 year minimum on old releases and +normal bug fixes for the current and previous release + ### Miscellaneous When in doubt, follow the guidelines in the Linux kernel style guide, or ask on From 86d488ce66e0b3affb4aec48f10ad5152a861f49 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Thu, 15 Feb 2018 13:37:03 -0500 Subject: [PATCH 26/98] tools: improve checkpatch.sh * Send reports to stderr; this allows you to get just the end result by redirecting stderr * Don't attempt to copy nonexistent files Signed-off-by: Quentin Young --- tools/checkpatch.sh | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/tools/checkpatch.sh b/tools/checkpatch.sh index 3536df79a9..f55692096f 100755 --- a/tools/checkpatch.sh +++ b/tools/checkpatch.sh @@ -45,10 +45,19 @@ echo $mod if [ -z "$mod" ]; then echo "There doesn't seem to be any changes." else - cp $tree/$mod /tmp/f1/ + echo "Copying source to temp directory..." + for file in $mod; do + echo "$tree/$file --> /tmp/f1/$file" + cp $tree/$file /tmp/f1/ + done git -C $tree reset --hard git -C $tree clean -fd - cp $tree/$mod /tmp/f2/ + for file in $mod; do + if [ -f $tree/$file ]; then + echo "$tree/$file --> /tmp/f2/$file" + cp $tree/$file /tmp/f2/ + fi + done echo "Running style checks..." for file in /tmp/f1/*; do echo "$checkpatch $file > $file _cp" @@ -60,12 +69,12 @@ else done echo "Done." for file in /tmp/f1/*_cp; do - echo "Report for $(basename $file _cp)" - echo "===============================================" + echo "Report for $(basename $file _cp)" 1>&2 + echo "===============================================" 1>&2 if [ -a /tmp/f2/$(basename $file) ]; then - diff $file /tmp/f2/$(basename $file) | grep -v "normally be const" | grep -A3 "ERROR\|WARNING" + diff $file /tmp/f2/$(basename $file) | grep -v "normally be const" | grep -A3 "ERROR\|WARNING" 1>&2 else - cat $file | grep -v "normally be const" | grep -A3 "ERROR\|WARNING" + cat $file | grep -v "normally be const" | grep -A3 "ERROR\|WARNING" 1>&2 fi if [ "$?" -eq "0" ]; then stat=1 From 5b02bd3ad01c1bdcf354987133aad75b5cb2f041 Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Fri, 23 Feb 2018 02:07:47 -0500 Subject: [PATCH 27/98] pimd: Fix some compiler issues Fix some compiler issues that were not picked up. Signed-off-by: Donald Sharp --- pimd/mtracebis.c | 2 ++ pimd/pim_igmp_mtrace.c | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/pimd/mtracebis.c b/pimd/mtracebis.c index 2032cdcfbe..1e7aee858a 100644 --- a/pimd/mtracebis.c +++ b/pimd/mtracebis.c @@ -35,6 +35,8 @@ #include #include #include +#include +#include #define MTRACEBIS_VERSION "0.1" #define MTRACE_TIMEOUT (5) diff --git a/pimd/pim_igmp_mtrace.c b/pimd/pim_igmp_mtrace.c index 629e10cb0c..feb326c45c 100644 --- a/pimd/pim_igmp_mtrace.c +++ b/pimd/pim_igmp_mtrace.c @@ -492,7 +492,7 @@ int igmp_mtrace_recv_qry_req(struct igmp_sock *igmp, struct ip *ip_hdr, if (PIM_DEBUG_MTRACE) zlog_warn( "Recv mtrace packet from %s on %s: too short," - " len=%d, min=%lu", + " len=%d, min=%zu", from_str, ifp->name, igmp_msg_len, sizeof(struct igmp_mtrace)); return -1; From 5335613bc741a45a2f307e66b73b4f1303567146 Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Tue, 13 Feb 2018 20:25:11 -0500 Subject: [PATCH 28/98] zebra: Move zvrf->other_tables into zns The other_tables data structure does not belong to a vrf. It belongs to the zns. This is because each vrf does not need to have copies of each of other_tables. Additionally move the array into a RB_TREE. This will allow us to sort quickly and easily expand the number of tables we can support to beyond the ZEBRA_KERNEL_TABLE_MAX define. Signed-off-by: Donald Sharp --- zebra/zebra_ns.c | 69 +++++++++++++++++++++++++++++++++++++++++++++++ zebra/zebra_ns.h | 18 +++++++++++++ zebra/zebra_vrf.c | 50 +++++----------------------------- zebra/zebra_vrf.h | 6 ++--- 4 files changed, 96 insertions(+), 47 deletions(-) diff --git a/zebra/zebra_ns.c b/zebra/zebra_ns.c index c48a6f7bd8..91dbd34387 100644 --- a/zebra/zebra_ns.c +++ b/zebra/zebra_ns.c @@ -34,8 +34,25 @@ DEFINE_MTYPE(ZEBRA, ZEBRA_NS, "Zebra Name Space") +static __inline int +zebra_ns_table_entry_compare(const struct zebra_ns_table *e1, + const struct zebra_ns_table *e2); + +RB_GENERATE(zebra_ns_table_head, zebra_ns_table, zebra_ns_table_entry, + zebra_ns_table_entry_compare); + static struct zebra_ns *dzns; +static __inline int +zebra_ns_table_entry_compare(const struct zebra_ns_table *e1, + const struct zebra_ns_table *e2) +{ + if (e1->tableid == e2->tableid) + return (e1->afi - e2->afi); + + return e1->tableid - e2->tableid; +} + struct zebra_ns *zebra_ns_lookup(ns_id_t ns_id) { return dzns; @@ -57,10 +74,61 @@ int zebra_ns_enable(ns_id_t ns_id, void **info) return 0; } +struct route_table *zebra_ns_get_table(struct zebra_ns *zns, + struct zebra_vrf *zvrf, uint32_t tableid, + afi_t afi) +{ + struct zebra_ns_table finder; + struct zebra_ns_table *znst; + rib_table_info_t *info; + + memset(&finder, 0, sizeof(finder)); + finder.afi = afi; + finder.tableid = tableid; + znst = RB_FIND(zebra_ns_table_head, &zns->ns_tables, &finder); + + if (znst) + return znst->table; + + znst = XCALLOC(MTYPE_ZEBRA_NS, sizeof(*znst)); + znst->tableid = tableid; + znst->afi = afi; + znst->table = + (afi == AFI_IP6) ? srcdest_table_init() : route_table_init(); + + info = XCALLOC(MTYPE_RIB_TABLE_INFO, sizeof(*info)); + info->zvrf = zvrf; + info->afi = afi; + info->safi = SAFI_UNICAST; + znst->table->info = info; + znst->table->cleanup = zebra_rtable_node_cleanup; + + RB_INSERT(zebra_ns_table_head, &zns->ns_tables, znst); + return znst->table; +} + +static struct zebra_ns_table *zebra_ns_free_table(struct zebra_ns_table *znst) +{ + void *table_info; + rib_close_table(znst->table); + + table_info = znst->table->info; + route_table_finish(znst->table); + XFREE(MTYPE_RIB_TABLE_INFO, table_info); + XFREE(MTYPE_ZEBRA_NS, znst); + return NULL; +} + int zebra_ns_disable(ns_id_t ns_id, void **info) { + struct zebra_ns_table *znst; struct zebra_ns *zns = (struct zebra_ns *)(*info); + while ((znst = RB_ROOT(zebra_ns_table_head, &zns->ns_tables)) + != NULL) { + RB_REMOVE(zebra_ns_table_head, &zns->ns_tables, znst); + znst = zebra_ns_free_table(znst); + } route_table_finish(zns->if_table); zebra_vxlan_ns_disable(zns); #if defined(HAVE_RTADV) @@ -72,6 +140,7 @@ int zebra_ns_disable(ns_id_t ns_id, void **info) return 0; } + int zebra_ns_init(void) { dzns = XCALLOC(MTYPE_ZEBRA_NS, sizeof(struct zebra_ns)); diff --git a/zebra/zebra_ns.h b/zebra/zebra_ns.h index 5d90b9be67..29905ccad3 100644 --- a/zebra/zebra_ns.h +++ b/zebra/zebra_ns.h @@ -34,6 +34,18 @@ struct nlsock { }; #endif +struct zebra_ns_table { + RB_ENTRY(zebra_ns_table) zebra_ns_table_entry; + + uint32_t tableid; + afi_t afi; + + struct route_table *table; +}; +RB_HEAD(zebra_ns_table_head, zebra_ns_table); +RB_PROTOTYPE(zebra_ns_table_head, zebra_ns_table, zebra_ns_table_entry, + zebra_ns_table_entry_compare) + struct zebra_ns { /* net-ns name. */ char name[VRF_NAMSIZ]; @@ -55,6 +67,8 @@ struct zebra_ns { #if defined(HAVE_RTADV) struct rtadv rtadv; #endif /* HAVE_RTADV */ + + struct zebra_ns_table_head ns_tables; }; struct zebra_ns *zebra_ns_lookup(ns_id_t ns_id); @@ -62,4 +76,8 @@ struct zebra_ns *zebra_ns_lookup(ns_id_t ns_id); int zebra_ns_init(void); int zebra_ns_enable(ns_id_t ns_id, void **info); int zebra_ns_disable(ns_id_t ns_id, void **info); + +extern struct route_table *zebra_ns_get_table(struct zebra_ns *zns, + struct zebra_vrf *zvrf, + uint32_t tableid, afi_t afi); #endif diff --git a/zebra/zebra_vrf.c b/zebra/zebra_vrf.c index b9b3048486..a0c7929b44 100644 --- a/zebra/zebra_vrf.c +++ b/zebra/zebra_vrf.c @@ -174,7 +174,6 @@ static int zebra_vrf_disable(struct vrf *vrf) struct static_route *si; struct route_table *table; struct interface *ifp; - u_int32_t table_id; afi_t afi; safi_t safi; unsigned i; @@ -213,12 +212,6 @@ static int zebra_vrf_disable(struct vrf *vrf) for (afi = AFI_IP; afi <= AFI_IP6; afi++) { for (safi = SAFI_UNICAST; safi <= SAFI_MULTICAST; safi++) rib_close_table(zvrf->table[afi][safi]); - - if (vrf->vrf_id == VRF_DEFAULT) - for (table_id = 0; table_id < ZEBRA_KERNEL_TABLE_MAX; - table_id++) - if (zvrf->other_table[afi][table_id]) - rib_close_table(zvrf->other_table[afi][table_id]); } /* Cleanup Vxlan, MPLS and PW tables. */ @@ -258,17 +251,6 @@ static int zebra_vrf_disable(struct vrf *vrf) zvrf->table[afi][safi] = NULL; } - if (vrf->vrf_id == VRF_DEFAULT) - for (table_id = 0; table_id < ZEBRA_KERNEL_TABLE_MAX; - table_id++) - if (zvrf->other_table[afi][table_id]) { - table = zvrf->other_table[afi][table_id]; - table_info = table->info; - route_table_finish(table); - XFREE(MTYPE_RIB_TABLE_INFO, table_info); - zvrf->other_table[afi][table_id] = NULL; - } - route_table_finish(zvrf->rnh_table[afi]); zvrf->rnh_table[afi] = NULL; route_table_finish(zvrf->import_check_table[afi]); @@ -282,7 +264,6 @@ static int zebra_vrf_delete(struct vrf *vrf) { struct zebra_vrf *zvrf = vrf->info; struct route_table *table; - u_int32_t table_id; afi_t afi; safi_t safi; unsigned i; @@ -328,14 +309,6 @@ static int zebra_vrf_delete(struct vrf *vrf) route_table_finish(table); } - for (table_id = 0; table_id < ZEBRA_KERNEL_TABLE_MAX; table_id++) - if (zvrf->other_table[afi][table_id]) { - table = zvrf->other_table[afi][table_id]; - table_info = table->info; - route_table_finish(table); - XFREE(MTYPE_RIB_TABLE_INFO, table_info); - } - route_table_finish(zvrf->rnh_table[afi]); route_table_finish(zvrf->import_check_table[afi]); } @@ -407,8 +380,8 @@ struct route_table *zebra_vrf_table_with_table_id(afi_t afi, safi_t safi, return table; } -static void zebra_rtable_node_cleanup(struct route_table *table, - struct route_node *node) +void zebra_rtable_node_cleanup(struct route_table *table, + struct route_node *node) { struct route_entry *re, *next; @@ -545,13 +518,14 @@ struct route_table *zebra_vrf_other_route_table(afi_t afi, u_int32_t table_id, vrf_id_t vrf_id) { struct zebra_vrf *zvrf; - rib_table_info_t *info; - struct route_table *table; + struct zebra_ns *zns; zvrf = vrf_info_lookup(vrf_id); if (!zvrf) return NULL; + zns = zvrf->zns; + if (afi >= AFI_MAX) return NULL; @@ -560,19 +534,7 @@ struct route_table *zebra_vrf_other_route_table(afi_t afi, u_int32_t table_id, if ((vrf_id == VRF_DEFAULT) && (table_id != RT_TABLE_MAIN) && (table_id != zebrad.rtm_table_default)) { - if (zvrf->other_table[afi][table_id] == NULL) { - table = (afi == AFI_IP6) ? srcdest_table_init() - : route_table_init(); - info = XCALLOC(MTYPE_RIB_TABLE_INFO, sizeof(*info)); - info->zvrf = zvrf; - info->afi = afi; - info->safi = SAFI_UNICAST; - table->info = info; - table->cleanup = zebra_rtable_node_cleanup; - zvrf->other_table[afi][table_id] = table; - } - - return (zvrf->other_table[afi][table_id]); + return zebra_ns_get_table(zns, zvrf, table_id, afi); } return zvrf->table[afi][SAFI_UNICAST]; diff --git a/zebra/zebra_vrf.h b/zebra/zebra_vrf.h index d3a5316b9d..4d53eee093 100644 --- a/zebra/zebra_vrf.h +++ b/zebra/zebra_vrf.h @@ -62,9 +62,6 @@ struct zebra_vrf { /* Import check table (used mostly by BGP */ struct route_table *import_check_table[AFI_MAX]; - /* Routing tables off of main table for redistribute table */ - struct route_table *other_table[AFI_MAX][ZEBRA_KERNEL_TABLE_MAX]; - /* 2nd pointer type used primarily to quell a warning on * ALL_LIST_ELEMENTS_RO */ @@ -154,4 +151,7 @@ extern struct route_table * zebra_vrf_other_route_table(afi_t afi, u_int32_t table_id, vrf_id_t vrf_id); extern int zebra_vrf_has_config(struct zebra_vrf *zvrf); extern void zebra_vrf_init(void); + +extern void zebra_rtable_node_cleanup(struct route_table *table, + struct route_node *node); #endif From 36064c0d9bb0a123c9a34919e58fa8dfed53b007 Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Tue, 13 Feb 2018 20:29:38 -0500 Subject: [PATCH 29/98] zebra: Allow table creation for tables greater than 252 The linux kernel allows a vast expanse of tables to be used. It would be useful for zebra to track these tables if they are being used. Signed-off-by: Donald Sharp --- zebra/zebra_vrf.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/zebra/zebra_vrf.c b/zebra/zebra_vrf.c index a0c7929b44..f0bd91bd4b 100644 --- a/zebra/zebra_vrf.c +++ b/zebra/zebra_vrf.c @@ -529,9 +529,6 @@ struct route_table *zebra_vrf_other_route_table(afi_t afi, u_int32_t table_id, if (afi >= AFI_MAX) return NULL; - if (table_id >= ZEBRA_KERNEL_TABLE_MAX) - return NULL; - if ((vrf_id == VRF_DEFAULT) && (table_id != RT_TABLE_MAIN) && (table_id != zebrad.rtm_table_default)) { return zebra_ns_get_table(zns, zvrf, table_id, afi); From ae825b8bf07764dc77248c83249c57a26ba2fd2b Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Tue, 13 Feb 2018 22:38:47 -0500 Subject: [PATCH 30/98] zebra: Add code to display interesting tables With the ability of zebra to handle random tables, add code to display those tables via the show route table (1-...) [json] command. Signed-off-by: Donald Sharp --- zebra/zebra_ns.c | 17 ++++++++ zebra/zebra_ns.h | 2 + zebra/zebra_vty.c | 105 +++++++++++++++++++++++++++++++--------------- 3 files changed, 90 insertions(+), 34 deletions(-) diff --git a/zebra/zebra_ns.c b/zebra/zebra_ns.c index 91dbd34387..ac724a3299 100644 --- a/zebra/zebra_ns.c +++ b/zebra/zebra_ns.c @@ -74,6 +74,23 @@ int zebra_ns_enable(ns_id_t ns_id, void **info) return 0; } +struct route_table *zebra_ns_find_table(struct zebra_ns *zns, + uint32_t tableid, afi_t afi) +{ + struct zebra_ns_tables finder; + struct zebra_ns_tables *znst; + + memset(&finder, 0, sizeof(finder)); + finder.afi = afi; + finder.tableid = tableid; + znst = RB_FIND(zebra_ns_tables_head, &zns->ns_tables, &finder); + + if (znst) + return znst->table; + else + return NULL; +} + struct route_table *zebra_ns_get_table(struct zebra_ns *zns, struct zebra_vrf *zvrf, uint32_t tableid, afi_t afi) diff --git a/zebra/zebra_ns.h b/zebra/zebra_ns.h index 29905ccad3..765f2c6893 100644 --- a/zebra/zebra_ns.h +++ b/zebra/zebra_ns.h @@ -77,6 +77,8 @@ int zebra_ns_init(void); int zebra_ns_enable(ns_id_t ns_id, void **info); int zebra_ns_disable(ns_id_t ns_id, void **info); +extern struct route_table *zebra_ns_find_table(struct zebra_ns *zns, + uint32_t tableid, afi_t afi); extern struct route_table *zebra_ns_get_table(struct zebra_ns *zns, struct zebra_vrf *zvrf, uint32_t tableid, afi_t afi); diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index 269244f768..d2248f4fbb 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -1235,46 +1235,21 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn, } } -static int do_show_ip_route(struct vty *vty, const char *vrf_name, afi_t afi, - safi_t safi, bool use_fib, u_char use_json, - route_tag_t tag, - const struct prefix *longer_prefix_p, - bool supernets_only, int type, - u_short ospf_instance_id) +static void do_show_route_helper(struct vty *vty, struct zebra_vrf *zvrf, + struct route_table *table, afi_t afi, + bool use_fib, route_tag_t tag, + const struct prefix *longer_prefix_p, + bool supernets_only, int type, + u_short ospf_instance_id, u_char use_json) { - struct route_table *table; - rib_dest_t *dest; struct route_node *rn; struct route_entry *re; int first = 1; - struct zebra_vrf *zvrf = NULL; - char buf[BUFSIZ]; + rib_dest_t *dest; json_object *json = NULL; json_object *json_prefix = NULL; - u_int32_t addr; - - if (!(zvrf = zebra_vrf_lookup_by_name(vrf_name))) { - if (use_json) - vty_out(vty, "{}\n"); - else - vty_out(vty, "vrf %s not defined\n", vrf_name); - return CMD_SUCCESS; - } - - if (zvrf_id(zvrf) == VRF_UNKNOWN) { - if (use_json) - vty_out(vty, "{}\n"); - else - vty_out(vty, "vrf %s inactive\n", vrf_name); - return CMD_SUCCESS; - } - - table = zebra_vrf_table(afi, safi, zvrf_id(zvrf)); - if (!table) { - if (use_json) - vty_out(vty, "{}\n"); - return CMD_SUCCESS; - } + uint32_t addr; + char buf[BUFSIZ]; if (use_json) json = json_object_new_object(); @@ -1352,6 +1327,67 @@ static int do_show_ip_route(struct vty *vty, const char *vrf_name, afi_t afi, json, JSON_C_TO_STRING_PRETTY)); json_object_free(json); } +} + +static int do_show_ip_route(struct vty *vty, const char *vrf_name, afi_t afi, + safi_t safi, bool use_fib, u_char use_json, + route_tag_t tag, + const struct prefix *longer_prefix_p, + bool supernets_only, int type, + u_short ospf_instance_id) +{ + struct route_table *table; + struct zebra_vrf *zvrf = NULL; + + if (!(zvrf = zebra_vrf_lookup_by_name(vrf_name))) { + if (use_json) + vty_out(vty, "{}\n"); + else + vty_out(vty, "vrf %s not defined\n", vrf_name); + return CMD_SUCCESS; + } + + if (zvrf_id(zvrf) == VRF_UNKNOWN) { + if (use_json) + vty_out(vty, "{}\n"); + else + vty_out(vty, "vrf %s inactive\n", vrf_name); + return CMD_SUCCESS; + } + + table = zebra_vrf_table(afi, safi, zvrf_id(zvrf)); + if (!table) { + if (use_json) + vty_out(vty, "{}\n"); + return CMD_SUCCESS; + } + + do_show_route_helper(vty, zvrf, table, afi, use_fib, tag, + longer_prefix_p, supernets_only, type, + ospf_instance_id, use_json); + + return CMD_SUCCESS; +} + +DEFPY (show_route_table, + show_route_table_cmd, + "show route table (1-4294967295)$table [json$json]", + SHOW_STR + IP_STR + IP6_STR + "IP routing table\n" + "Table to display\n" + "The table number to display, if available\n" + JSON_STR) +{ + afi_t afi = ipv4 ? AFI_IP : AFI_IP6; + struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(VRF_DEFAULT); + struct route_table *t; + + t = zebra_ns_find_table(zvrf->zns, table, afi); + if (t) + do_show_route_helper(vty, zvrf, t, afi, false, 0, false, false, + 0, 0, !!json); return CMD_SUCCESS; } @@ -3341,6 +3377,7 @@ void zebra_vty_init(void) install_element(VIEW_NODE, &show_vrf_cmd); install_element(VIEW_NODE, &show_vrf_vni_cmd); install_element(VIEW_NODE, &show_route_cmd); + install_element(VIEW_NODE, &show_route_table_cmd); install_element(VIEW_NODE, &show_route_detail_cmd); install_element(VIEW_NODE, &show_route_summary_cmd); install_element(VIEW_NODE, &show_ip_nht_cmd); From a031a7e4c9232fc9433e51af6b67f22d9b0cd663 Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Thu, 15 Feb 2018 21:33:22 -0500 Subject: [PATCH 31/98] zebra: On shutdown don't count removals Some of the tables are no longer stored in the zvrf and in the zns now. On shutdown zns is cleaned up after vrf( and rightly so!) As such we should not attempt to count the information if we don't have a zvrf. Signed-off-by: Donald Sharp --- zebra/zebra_rib.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 967bc92850..b5ce17d9f8 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -1150,7 +1150,8 @@ void rib_uninstall_kernel(struct route_node *rn, struct route_entry *re) */ hook_call(rib_update, rn, "uninstalling from kernel"); kernel_route_rib(rn, p, src_p, re, NULL); - zvrf->removals++; + if (zvrf) + zvrf->removals++; return; } From 55cd0f612a046137f0be936e7856921ada4546ca Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Sat, 17 Feb 2018 19:02:55 -0500 Subject: [PATCH 32/98] *: Make assignment from RB_ROOT in while loop work better Fix up the assignment of the variable = RB_ROOT inside of while loop patter we were using. Signed-off-by: Donald Sharp --- ldpd/interface.c | 5 ++++- ldpd/l2vpn.c | 13 +++++++++---- ldpd/lde.c | 5 ++++- ldpd/lde_lib.c | 4 +++- ldpd/ldp_vty_conf.c | 13 +++++++++---- ldpd/ldpd.c | 41 +++++++++++++++++++++++++++++------------ ldpd/ldpe.c | 5 ++++- ldpd/neighbor.c | 4 +++- lib/if.c | 6 ++++-- lib/ns.c | 5 ++++- lib/vrf.c | 9 +++++++-- pimd/pim_iface.c | 12 ++++++++---- pimd/pim_ifchannel.c | 5 +++-- zebra/zebra_ns.c | 11 ++++++----- zebra/zebra_pw.c | 5 ++++- 15 files changed, 101 insertions(+), 42 deletions(-) diff --git a/ldpd/interface.c b/ldpd/interface.c index bbcea9f553..b25be43a5c 100644 --- a/ldpd/interface.c +++ b/ldpd/interface.c @@ -306,8 +306,11 @@ if_reset(struct iface *iface, int af) ia = iface_af_get(iface, af); if_stop_hello_timer(ia); - while ((adj = RB_ROOT(ia_adj_head, &ia->adj_tree)) != NULL) + while (!RB_EMPTY(ia_adj_head, &ia->adj_tree)) { + adj = RB_ROOT(ia_adj_head, &ia->adj_tree); + adj_del(adj, S_SHUTDOWN); + } /* try to cleanup */ switch (af) { diff --git a/ldpd/l2vpn.c b/ldpd/l2vpn.c index f638d6a65b..1cfeae3092 100644 --- a/ldpd/l2vpn.c +++ b/ldpd/l2vpn.c @@ -76,16 +76,21 @@ l2vpn_del(struct l2vpn *l2vpn) struct l2vpn_if *lif; struct l2vpn_pw *pw; - while ((lif = RB_ROOT(l2vpn_if_head, &l2vpn->if_tree)) != NULL) { + while (!RB_EMPTY(l2vpn_if_head, &l2vpn->if_tree)) { + lif = RB_ROOT(l2vpn_if_head, &l2vpn->if_tree); + RB_REMOVE(l2vpn_if_head, &l2vpn->if_tree, lif); free(lif); } - while ((pw = RB_ROOT(l2vpn_pw_head, &l2vpn->pw_tree)) != NULL) { + while (!RB_EMPTY(l2vpn_pw_head, &l2vpn->pw_tree)) { + pw = RB_ROOT(l2vpn_pw_head, &l2vpn->pw_tree); + RB_REMOVE(l2vpn_pw_head, &l2vpn->pw_tree, pw); free(pw); } - while ((pw = RB_ROOT(l2vpn_pw_head, - &l2vpn->pw_inactive_tree)) != NULL) { + while (!RB_EMPTY(l2vpn_pw_head, &l2vpn->pw_inactive_tree)) { + pw = RB_ROOT(l2vpn_pw_head, &l2vpn->pw_inactive_tree); + RB_REMOVE(l2vpn_pw_head, &l2vpn->pw_inactive_tree, pw); free(pw); } diff --git a/ldpd/lde.c b/ldpd/lde.c index a70b97d06b..5aa53fd39e 100644 --- a/ldpd/lde.c +++ b/ldpd/lde.c @@ -1324,8 +1324,11 @@ lde_nbr_clear(void) { struct lde_nbr *ln; - while ((ln = RB_ROOT(nbr_tree, &lde_nbrs)) != NULL) + while (!RB_EMPTY(nbr_tree, &lde_nbrs)) { + ln = RB_ROOT(nbr_tree, &lde_nbrs); + lde_nbr_del(ln); + } } static void diff --git a/ldpd/lde_lib.c b/ldpd/lde_lib.c index 18c8c0a122..28e455c7a5 100644 --- a/ldpd/lde_lib.c +++ b/ldpd/lde_lib.c @@ -129,7 +129,9 @@ fec_clear(struct fec_tree *fh, void (*free_cb)(void *)) { struct fec *f; - while ((f = RB_ROOT(fec_tree, fh)) != NULL) { + while (!RB_EMPTY(fec_tree, fh)) { + f = RB_ROOT(fec_tree, fh); + fec_remove(fh, f); free_cb(f); } diff --git a/ldpd/ldp_vty_conf.c b/ldpd/ldp_vty_conf.c index 76c602afbb..382b006884 100644 --- a/ldpd/ldp_vty_conf.c +++ b/ldpd/ldp_vty_conf.c @@ -1475,18 +1475,23 @@ l2vpn_del_api(struct ldpd_conf *conf, struct l2vpn *l2vpn) struct l2vpn_if *lif; struct l2vpn_pw *pw; - while ((lif = RB_ROOT(l2vpn_if_head, &l2vpn->if_tree)) != NULL) { + while (!RB_EMPTY(l2vpn_if_head, &l2vpn->if_tree)) { + lif = RB_ROOT(l2vpn_if_head, &l2vpn->if_tree); + QOBJ_UNREG(lif); RB_REMOVE(l2vpn_if_head, &l2vpn->if_tree, lif); free(lif); } - while ((pw = RB_ROOT(l2vpn_pw_head, &l2vpn->pw_tree)) != NULL) { + while (!RB_EMPTY(l2vpn_pw_head, &l2vpn->pw_tree)) { + pw = RB_ROOT(l2vpn_pw_head, &l2vpn->pw_tree); + QOBJ_UNREG(pw); RB_REMOVE(l2vpn_pw_head, &l2vpn->pw_tree, pw); free(pw); } - while ((pw = RB_ROOT(l2vpn_pw_head, - &l2vpn->pw_inactive_tree)) != NULL) { + while (!RB_EMPTY(l2vpn_pw_head, &l2vpn->pw_inactive_tree)) { + pw = RB_ROOT(l2vpn_pw_head, &l2vpn->pw_inactive_tree); + QOBJ_UNREG(pw); RB_REMOVE(l2vpn_pw_head, &l2vpn->pw_inactive_tree, pw); free(pw); diff --git a/ldpd/ldpd.c b/ldpd/ldpd.c index 12aeb1fff3..255febeb60 100644 --- a/ldpd/ldpd.c +++ b/ldpd/ldpd.c @@ -1066,13 +1066,17 @@ ldp_config_reset_main(struct ldpd_conf *conf) struct iface *iface; struct nbr_params *nbrp; - while ((iface = RB_ROOT(iface_head, &conf->iface_tree)) != NULL) { + while (!RB_EMPTY(iface_head, &conf->iface_tree)) { + iface = RB_ROOT(iface_head, &conf->iface_tree); + QOBJ_UNREG(iface); RB_REMOVE(iface_head, &conf->iface_tree, iface); free(iface); } - while ((nbrp = RB_ROOT(nbrp_head, &conf->nbrp_tree)) != NULL) { + while (!RB_EMPTY(nbrp_head, &conf->nbrp_tree)) { + nbrp = RB_ROOT(nbrp_head, &conf->nbrp_tree); + QOBJ_UNREG(nbrp); RB_REMOVE(nbrp_head, &conf->nbrp_tree, nbrp); free(nbrp); @@ -1128,20 +1132,25 @@ ldp_config_reset_l2vpns(struct ldpd_conf *conf) struct l2vpn_if *lif; struct l2vpn_pw *pw; - while ((l2vpn = RB_ROOT(l2vpn_head, &conf->l2vpn_tree)) != NULL) { - while ((lif = RB_ROOT(l2vpn_if_head, - &l2vpn->if_tree)) != NULL) { + while (!RB_EMPTY(l2vpn_head, &conf->l2vpn_tree)) { + l2vpn = RB_ROOT(l2vpn_head, &conf->l2vpn_tree); + while (!RB_EMPTY(l2vpn_if_head, &l2vpn->if_tree)) { + lif = RB_ROOT(l2vpn_if_head, &l2vpn->if_tree); + QOBJ_UNREG(lif); RB_REMOVE(l2vpn_if_head, &l2vpn->if_tree, lif); free(lif); } - while ((pw = RB_ROOT(l2vpn_pw_head, &l2vpn->pw_tree)) != NULL) { + while (!RB_EMPTY(l2vpn_pw_head, &l2vpn->pw_tree)) { + pw = RB_ROOT(l2vpn_pw_head, &l2vpn->pw_tree); + QOBJ_UNREG(pw); RB_REMOVE(l2vpn_pw_head, &l2vpn->pw_tree, pw); free(pw); } - while ((pw = RB_ROOT(l2vpn_pw_head, - &l2vpn->pw_inactive_tree)) != NULL) { + while (!RB_EMPTY(l2vpn_pw_head, &l2vpn->pw_inactive_tree)) { + pw = RB_ROOT(l2vpn_pw_head, &l2vpn->pw_inactive_tree); + QOBJ_UNREG(pw); RB_REMOVE(l2vpn_pw_head, &l2vpn->pw_inactive_tree, pw); free(pw); @@ -1160,19 +1169,27 @@ ldp_clear_config(struct ldpd_conf *xconf) struct nbr_params *nbrp; struct l2vpn *l2vpn; - while ((iface = RB_ROOT(iface_head, &xconf->iface_tree)) != NULL) { + while (!RB_EMPTY(iface_head, &xconf->iface_tree)) { + iface = RB_ROOT(iface_head, &xconf->iface_tree); + RB_REMOVE(iface_head, &xconf->iface_tree, iface); free(iface); } - while ((tnbr = RB_ROOT(tnbr_head, &xconf->tnbr_tree)) != NULL) { + while (!RB_EMPTY(tnbr_head, &xconf->tnbr_tree)) { + tnbr = RB_ROOT(tnbr_head, &xconf->tnbr_tree); + RB_REMOVE(tnbr_head, &xconf->tnbr_tree, tnbr); free(tnbr); } - while ((nbrp = RB_ROOT(nbrp_head, &xconf->nbrp_tree)) != NULL) { + while (!RB_EMPTY(nbrp_head, &xconf->nbrp_tree)) { + nbrp = RB_ROOT(nbrp_head, &xconf->nbrp_tree); + RB_REMOVE(nbrp_head, &xconf->nbrp_tree, nbrp); free(nbrp); } - while ((l2vpn = RB_ROOT(l2vpn_head, &xconf->l2vpn_tree)) != NULL) { + while (!RB_EMPTY(l2vpn_head, &xconf->l2vpn_tree)) { + l2vpn = RB_ROOT(l2vpn_head, &xconf->l2vpn_tree); + RB_REMOVE(l2vpn_head, &xconf->l2vpn_tree, l2vpn); l2vpn_del(l2vpn); } diff --git a/ldpd/ldpe.c b/ldpd/ldpe.c index 9d00bcd2b6..56af76d94e 100644 --- a/ldpd/ldpe.c +++ b/ldpd/ldpe.c @@ -219,8 +219,11 @@ ldpe_shutdown(void) assert(if_addr != LIST_FIRST(&global.addr_list)); free(if_addr); } - while ((adj = RB_ROOT(global_adj_head, &global.adj_tree)) != NULL) + while (!RB_EMPTY(global_adj_head, &global.adj_tree)) { + adj = RB_ROOT(global_adj_head, &global.adj_tree); + adj_del(adj, S_SHUTDOWN); + } /* clean up */ if (iev_lde) diff --git a/ldpd/neighbor.c b/ldpd/neighbor.c index 39860a1859..1c3f650dff 100644 --- a/ldpd/neighbor.c +++ b/ldpd/neighbor.c @@ -316,7 +316,9 @@ nbr_del(struct nbr *nbr) mapping_list_clr(&nbr->release_list); mapping_list_clr(&nbr->abortreq_list); - while ((adj = RB_ROOT(nbr_adj_head, &nbr->adj_tree)) != NULL) { + while (!RB_EMPTY(nbr_adj_head, &nbr->adj_tree)) { + adj = RB_ROOT(nbr_adj_head, &nbr->adj_tree); + adj->nbr = NULL; RB_REMOVE(nbr_adj_head, &nbr->adj_tree, adj); } diff --git a/lib/if.c b/lib/if.c index 7866ddb8c4..12d123a8fa 100644 --- a/lib/if.c +++ b/lib/if.c @@ -1064,7 +1064,7 @@ ifaddr_ipv4_lookup (struct in_addr *addr, ifindex_t ifindex) rn = route_node_lookup (ifaddr_ipv4_table, (struct prefix *) &p); if (! rn) return NULL; - + ifp = rn->info; route_unlock_node (rn); return ifp; @@ -1078,7 +1078,9 @@ void if_terminate(struct vrf *vrf) { struct interface *ifp; - while ((ifp = RB_ROOT(if_name_head, &vrf->ifaces_by_name)) != NULL) { + while (!RB_EMPTY(if_name_head, &vrf->ifaces_by_name)) { + ifp = RB_ROOT(if_name_head, &vrf->ifaces_by_name); + if (ifp->node) { ifp->node->info = NULL; route_unlock_node(ifp->node); diff --git a/lib/ns.c b/lib/ns.c index fdf93d0742..0b2a3bec78 100644 --- a/lib/ns.c +++ b/lib/ns.c @@ -424,8 +424,11 @@ void ns_terminate(void) { struct ns *ns; - while ((ns = RB_ROOT(ns_head, &ns_tree)) != NULL) + while (!RB_EMPTY(ns_head, &ns_tree)) { + ns = RB_ROOT(ns_head, &ns_tree); + ns_delete(ns); + } } /* Create a socket for the NS. */ diff --git a/lib/vrf.c b/lib/vrf.c index 2e15fa2f5c..02946df2bc 100644 --- a/lib/vrf.c +++ b/lib/vrf.c @@ -419,12 +419,17 @@ void vrf_terminate(void) zlog_debug("%s: Shutting down vrf subsystem", __PRETTY_FUNCTION__); - while ((vrf = RB_ROOT(vrf_id_head, &vrfs_by_id)) != NULL) { + while (!RB_EMPTY(vrf_id_head, &vrfs_by_id)) { + vrf = RB_ROOT(vrf_id_head, &vrfs_by_id); + /* Clear configured flag and invoke delete. */ UNSET_FLAG(vrf->status, VRF_CONFIGURED); vrf_delete(vrf); } - while ((vrf = RB_ROOT(vrf_name_head, &vrfs_by_name)) != NULL) { + + while (!RB_EMPTY(vrf_name_head, &vrfs_by_name)) { + vrf = RB_ROOT(vrf_name_head, &vrfs_by_name); + /* Clear configured flag and invoke delete. */ UNSET_FLAG(vrf->status, VRF_CONFIGURED); vrf_delete(vrf); diff --git a/pimd/pim_iface.c b/pimd/pim_iface.c index f02cf7ed31..a807c69c60 100644 --- a/pimd/pim_iface.c +++ b/pimd/pim_iface.c @@ -85,9 +85,11 @@ static void *if_list_clean(struct pim_interface *pim_ifp) if (pim_ifp->sec_addr_list) list_delete_and_null(&pim_ifp->sec_addr_list); - while ((ch = RB_ROOT(pim_ifchannel_rb, - &pim_ifp->ifchannel_rb)) != NULL) + while (!RB_EMPTY(pim_ifchannel_rb, &pim_ifp->ifchannel_rb)) { + ch = RB_ROOT(pim_ifchannel_rb, &pim_ifp->ifchannel_rb); + pim_ifchannel_delete(ch); + } XFREE(MTYPE_PIM_INTERFACE, pim_ifp); @@ -250,9 +252,11 @@ void pim_if_delete(struct interface *ifp) if (pim_ifp->boundary_oil_plist) XFREE(MTYPE_PIM_INTERFACE, pim_ifp->boundary_oil_plist); - while ((ch = RB_ROOT(pim_ifchannel_rb, - &pim_ifp->ifchannel_rb)) != NULL) + while (!RB_EMPTY(pim_ifchannel_rb, &pim_ifp->ifchannel_rb)) { + ch = RB_ROOT(pim_ifchannel_rb, &pim_ifp->ifchannel_rb); + pim_ifchannel_delete(ch); + } XFREE(MTYPE_PIM_INTERFACE, pim_ifp); diff --git a/pimd/pim_ifchannel.c b/pimd/pim_ifchannel.c index 7d3b783adf..4d564e5046 100644 --- a/pimd/pim_ifchannel.c +++ b/pimd/pim_ifchannel.c @@ -211,8 +211,9 @@ void pim_ifchannel_delete_all(struct interface *ifp) if (!pim_ifp) return; - while ((ch = RB_ROOT(pim_ifchannel_rb, - &pim_ifp->ifchannel_rb)) != NULL) { + while (!RB_EMPTY(pim_ifchannel_rb, &pim_ifp->ifchannel_rb)) { + ch = RB_ROOT(pim_ifchannel_rb, &pim_ifp->ifchannel_rb); + pim_ifchannel_delete(ch); } } diff --git a/zebra/zebra_ns.c b/zebra/zebra_ns.c index ac724a3299..e8bdadee50 100644 --- a/zebra/zebra_ns.c +++ b/zebra/zebra_ns.c @@ -77,13 +77,13 @@ int zebra_ns_enable(ns_id_t ns_id, void **info) struct route_table *zebra_ns_find_table(struct zebra_ns *zns, uint32_t tableid, afi_t afi) { - struct zebra_ns_tables finder; - struct zebra_ns_tables *znst; + struct zebra_ns_table finder; + struct zebra_ns_table *znst; memset(&finder, 0, sizeof(finder)); finder.afi = afi; finder.tableid = tableid; - znst = RB_FIND(zebra_ns_tables_head, &zns->ns_tables, &finder); + znst = RB_FIND(zebra_ns_table_head, &zns->ns_tables, &finder); if (znst) return znst->table; @@ -141,8 +141,9 @@ int zebra_ns_disable(ns_id_t ns_id, void **info) struct zebra_ns_table *znst; struct zebra_ns *zns = (struct zebra_ns *)(*info); - while ((znst = RB_ROOT(zebra_ns_table_head, &zns->ns_tables)) - != NULL) { + while (!RB_EMPTY(zebra_ns_table_head, &zns->ns_tables)) { + znst = RB_ROOT(zebra_ns_table_head, &zns->ns_tables); + RB_REMOVE(zebra_ns_table_head, &zns->ns_tables, znst); znst = zebra_ns_free_table(znst); } diff --git a/zebra/zebra_pw.c b/zebra/zebra_pw.c index bbd01a759e..96bee36be6 100644 --- a/zebra/zebra_pw.c +++ b/zebra/zebra_pw.c @@ -294,8 +294,11 @@ void zebra_pw_exit(struct zebra_vrf *zvrf) { struct zebra_pw *pw; - while ((pw = RB_ROOT(zebra_pw_head, &zvrf->pseudowires)) != NULL) + while (!RB_EMPTY(zebra_pw_head, &zvrf->pseudowires)) { + pw = RB_ROOT(zebra_pw_head, &zvrf->pseudowires); + zebra_pw_del(zvrf, pw); + } } DEFUN_NOSH (pseudowire_if, From 783fc3cd45eb2160304b749535c61f8830d9e1ba Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Sun, 18 Feb 2018 17:23:50 -0500 Subject: [PATCH 33/98] zebra: Fix warning found in CI system The Clang SA system found a new issue: Dead store: Dead assignment. This fixes that issue Signed-off-by: Donald Sharp --- zebra/zebra_ns.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/zebra/zebra_ns.c b/zebra/zebra_ns.c index e8bdadee50..c7561f4264 100644 --- a/zebra/zebra_ns.c +++ b/zebra/zebra_ns.c @@ -124,7 +124,7 @@ struct route_table *zebra_ns_get_table(struct zebra_ns *zns, return znst->table; } -static struct zebra_ns_table *zebra_ns_free_table(struct zebra_ns_table *znst) +static void zebra_ns_free_table(struct zebra_ns_table *znst) { void *table_info; rib_close_table(znst->table); @@ -133,7 +133,6 @@ static struct zebra_ns_table *zebra_ns_free_table(struct zebra_ns_table *znst) route_table_finish(znst->table); XFREE(MTYPE_RIB_TABLE_INFO, table_info); XFREE(MTYPE_ZEBRA_NS, znst); - return NULL; } int zebra_ns_disable(ns_id_t ns_id, void **info) @@ -145,7 +144,7 @@ int zebra_ns_disable(ns_id_t ns_id, void **info) znst = RB_ROOT(zebra_ns_table_head, &zns->ns_tables); RB_REMOVE(zebra_ns_table_head, &zns->ns_tables, znst); - znst = zebra_ns_free_table(znst); + zebra_ns_free_table(znst); } route_table_finish(zns->if_table); zebra_vxlan_ns_disable(zns); From 27b136bd589ca2e6713ccaa6e0d8ea852c5f25a0 Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Fri, 23 Feb 2018 07:50:23 -0500 Subject: [PATCH 34/98] zebra: Fix up some code formatting issues. Signed-off-by: Donald Sharp --- zebra/zebra_ns.c | 5 +++-- zebra/zebra_vrf.c | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/zebra/zebra_ns.c b/zebra/zebra_ns.c index c7561f4264..1715881f7e 100644 --- a/zebra/zebra_ns.c +++ b/zebra/zebra_ns.c @@ -34,7 +34,7 @@ DEFINE_MTYPE(ZEBRA, ZEBRA_NS, "Zebra Name Space") -static __inline int +static inline int zebra_ns_table_entry_compare(const struct zebra_ns_table *e1, const struct zebra_ns_table *e2); @@ -43,7 +43,7 @@ RB_GENERATE(zebra_ns_table_head, zebra_ns_table, zebra_ns_table_entry, static struct zebra_ns *dzns; -static __inline int +static inline int zebra_ns_table_entry_compare(const struct zebra_ns_table *e1, const struct zebra_ns_table *e2) { @@ -127,6 +127,7 @@ struct route_table *zebra_ns_get_table(struct zebra_ns *zns, static void zebra_ns_free_table(struct zebra_ns_table *znst) { void *table_info; + rib_close_table(znst->table); table_info = znst->table->info; diff --git a/zebra/zebra_vrf.c b/zebra/zebra_vrf.c index f0bd91bd4b..80553d5c5f 100644 --- a/zebra/zebra_vrf.c +++ b/zebra/zebra_vrf.c @@ -229,7 +229,8 @@ static int zebra_vrf_disable(struct vrf *vrf) struct route_node *rnode; rib_dest_t *dest; - for (ALL_LIST_ELEMENTS(zebrad.mq->subq[i], lnode, nnode, rnode)) { + for (ALL_LIST_ELEMENTS(zebrad.mq->subq[i], + lnode, nnode, rnode)) { dest = rib_dest_from_rnode(rnode); if (dest && rib_dest_vrf(dest) == zvrf) { route_unlock_node(rnode); From 513e922309f4f83db5094354f6ffbc6cdd984d47 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Fri, 23 Feb 2018 16:16:30 +0100 Subject: [PATCH 35/98] tools: fix that filters issues on resulting file only Because checkpatch result is applied to original and new file, the analysis also parses what may be wrong with the original file. Whereas the script should limit to analyse only what is wrong on new file. Signed-off-by: Philippe Guibert --- tools/checkpatch.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/checkpatch.sh b/tools/checkpatch.sh index f55692096f..73421784fa 100755 --- a/tools/checkpatch.sh +++ b/tools/checkpatch.sh @@ -72,7 +72,7 @@ else echo "Report for $(basename $file _cp)" 1>&2 echo "===============================================" 1>&2 if [ -a /tmp/f2/$(basename $file) ]; then - diff $file /tmp/f2/$(basename $file) | grep -v "normally be const" | grep -A3 "ERROR\|WARNING" 1>&2 + diff $file /tmp/f2/$(basename $file) | grep -v "normally be const" | grep -A3 "ERROR\|WARNING" | grep -A2 -B2 '/tmp/f1' 1>&2 else cat $file | grep -v "normally be const" | grep -A3 "ERROR\|WARNING" 1>&2 fi From e062e87d7f6cc4c3ac8d9c24263f212dba2aa611 Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Fri, 23 Feb 2018 10:06:04 -0500 Subject: [PATCH 36/98] doc: Update centos7 build to include systemd Update the centos7 build instructions to include data on how to build w/ systemd. This is especially useful because we tell the user to install the frr.service file and the frr.service file expects systemd integration Signed-off-by: Donald Sharp --- doc/Building_FRR_on_CentOS7.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/Building_FRR_on_CentOS7.md b/doc/Building_FRR_on_CentOS7.md index 787b80fbf7..38802483b6 100644 --- a/doc/Building_FRR_on_CentOS7.md +++ b/doc/Building_FRR_on_CentOS7.md @@ -20,7 +20,7 @@ Add packages: sudo yum install git autoconf automake libtool make gawk \ readline-devel texinfo net-snmp-devel groff pkgconfig \ json-c-devel pam-devel bison flex pytest c-ares-devel \ - perl-XML-LibXML python-devel + perl-XML-LibXML python-devel systemd-devel Get FRR, compile it and install it (from Git) --------------------------------------------- @@ -59,6 +59,7 @@ an example.) --enable-group=frr \ --enable-vty-group=frrvt \ --enable-rtadv \ + --enable-systemd=yes \ --disable-exampledir \ --enable-watchfrr \ --disable-ldpd \ From 6f3835891a217af960c73366f6b635f65b256901 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Fri, 23 Feb 2018 10:11:19 -0500 Subject: [PATCH 37/98] tools: ignore FSF warning, fn macros in checkpatch * Unlike Linux we do require the GPL file header * When checking for spaces between function names and parentheses, ignore all-uppercase function names as these are likely to be macros, and function-like macros may have that space Signed-off-by: Quentin Young --- tools/checkpatch.pl | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/tools/checkpatch.pl b/tools/checkpatch.pl index 8477383469..fe771ebbba 100755 --- a/tools/checkpatch.pl +++ b/tools/checkpatch.pl @@ -2766,18 +2766,6 @@ sub process { $rpt_cleaners = 1; } -# Check for FSF mailing addresses. - if ($rawline =~ /\bwrite to the Free/i || - $rawline =~ /\b675\s+Mass\s+Ave/i || - $rawline =~ /\b59\s+Temple\s+Pl/i || - $rawline =~ /\b51\s+Franklin\s+St/i) { - my $herevet = "$here\n" . cat_vet($rawline) . "\n"; - my $msg_level = \&ERROR; - $msg_level = \&CHK if ($file); - &{$msg_level}("FSF_MAILING_ADDRESS", - "Do not include the paragraph about writing to the Free Software Foundation's mailing address from the sample GPL notice. The FSF has changed addresses in the past, and may do so again. Linux already includes a copy of the GPL.\n" . $herevet) - } - # check for Kconfig help text having a real description # Only applies when adding the entry originally, after that we do not have # sufficient context to determine whether it is indeed long enough. @@ -4058,6 +4046,10 @@ sub process { # likely a typedef for a function. } elsif ($ctx =~ /$Type$/) { + # All-uppercase function names are usually macros, + # ignore those + } elsif ($name eq uc $name) { + } else { if (WARN("SPACING", "space prohibited between function name and open parenthesis '('\n" . $herecurr) && From 2580e72f8d62d9094ddea2af72de222edcac4ccc Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Fri, 23 Feb 2018 10:58:17 -0500 Subject: [PATCH 38/98] tools: finer-grained error codes for checkpatch * 2 for errors * 1 for warnings * 0 for clean * Suppress all report text for a clean result * Remove check for const structs from perl script * Remove grep suppression for that check from shell script Signed-off-by: Quentin Young --- tools/checkpatch.pl | 13 ------------- tools/checkpatch.sh | 15 ++++++++++----- 2 files changed, 10 insertions(+), 18 deletions(-) diff --git a/tools/checkpatch.pl b/tools/checkpatch.pl index fe771ebbba..a952de8947 100755 --- a/tools/checkpatch.pl +++ b/tools/checkpatch.pl @@ -55,7 +55,6 @@ my $min_conf_desc_length = 4; my $spelling_file = "$D/spelling.txt"; my $codespell = 0; my $codespellfile = "/usr/share/codespell/dictionary.txt"; -my $conststructsfile = "$D/const_structs.checkpatch"; my $typedefsfile = ""; my $color = "auto"; my $allow_c99_comments = 1; @@ -685,10 +684,6 @@ sub read_words { return 0; } -my $const_structs = ""; -read_words(\$const_structs, $conststructsfile) - or warn "No structs that should be const will be found - file '$conststructsfile': $!\n"; - my $typeOtherTypedefs = ""; if (length($typedefsfile)) { read_words(\$typeOtherTypedefs, $typedefsfile) @@ -6197,14 +6192,6 @@ sub process { "please use device_initcall() or more appropriate function instead of __initcall() (see include/linux/init.h)\n" . $herecurr); } -# check for various structs that are normally const (ops, kgdb, device_tree) -# and avoid what seem like struct definitions 'struct foo {' - if ($line !~ /\bconst\b/ && - $line =~ /\bstruct\s+($const_structs)\b(?!\s*\{)/) { - WARN("CONST_STRUCT", - "struct $1 should normally be const\n" . $herecurr); - } - # use of NR_CPUS is usually wrong # ignore definitions of NR_CPUS and usage to define arrays as likely right if ($line =~ /\bNR_CPUS\b/ && diff --git a/tools/checkpatch.sh b/tools/checkpatch.sh index f55692096f..930c8e2df6 100755 --- a/tools/checkpatch.sh +++ b/tools/checkpatch.sh @@ -69,15 +69,20 @@ else done echo "Done." for file in /tmp/f1/*_cp; do - echo "Report for $(basename $file _cp)" 1>&2 - echo "===============================================" 1>&2 if [ -a /tmp/f2/$(basename $file) ]; then - diff $file /tmp/f2/$(basename $file) | grep -v "normally be const" | grep -A3 "ERROR\|WARNING" 1>&2 + result=$(diff $file /tmp/f2/$(basename $file) | grep -A3 "ERROR\|WARNING") else - cat $file | grep -v "normally be const" | grep -A3 "ERROR\|WARNING" 1>&2 + result=$(cat $file | grep -A4 "ERROR\|WARNING") fi if [ "$?" -eq "0" ]; then - stat=1 + echo "Report for $(basename $file _cp)" 1>&2 + echo "===============================================" 1>&2 + echo "$result" 1>&2 + if echo $result | grep -q "ERROR"; then + stat=2 + elif [ "$stat" -eq "0" ]; then + stat=1 + fi fi done fi From ddb3dcc733ba285ea47f49a2645d8055a1e89438 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Fri, 23 Feb 2018 12:40:57 -0500 Subject: [PATCH 39/98] vtysh: .history_quagga --> .history_frr Signed-off-by: Quentin Young --- vtysh/vtysh_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vtysh/vtysh_main.c b/vtysh/vtysh_main.c index a4985c423c..ca6c7798eb 100644 --- a/vtysh/vtysh_main.c +++ b/vtysh/vtysh_main.c @@ -527,7 +527,7 @@ int main(int argc, char **argv, char **env) homedir = vtysh_get_home(); if (homedir) { snprintf(history_file, sizeof(history_file), - "%s/.history_quagga", homedir); + "%s/.history_frr", homedir); if (read_history(history_file) != 0) { int fp; From 28610f7e444dbb2ea4175582f84e566a6645927b Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Fri, 9 Feb 2018 19:27:52 -0500 Subject: [PATCH 40/98] *: Add tableid the route entry was sent to Add for the southbound pass back the route entries tableid used for installation. Signed-off-by: Donald Sharp --- eigrpd/eigrp_zebra.c | 3 ++- lib/zclient.c | 2 ++ lib/zclient.h | 1 + sharpd/sharp_zebra.c | 3 ++- zebra/zebra_rib.c | 10 +++------- zebra/zserv.c | 15 +++++++++------ zebra/zserv.h | 3 +-- 7 files changed, 20 insertions(+), 17 deletions(-) diff --git a/eigrpd/eigrp_zebra.c b/eigrpd/eigrp_zebra.c index 3759c64148..513fda06f3 100644 --- a/eigrpd/eigrp_zebra.c +++ b/eigrpd/eigrp_zebra.c @@ -99,8 +99,9 @@ static int eigrp_zebra_notify_owner(int command, struct zclient *zclient, { struct prefix p; enum zapi_route_notify_owner note; + uint32_t table; - if (!zapi_route_notify_decode(zclient->ibuf, &p, ¬e)) + if (!zapi_route_notify_decode(zclient->ibuf, &p, &table, ¬e)) return -1; return 0; diff --git a/lib/zclient.c b/lib/zclient.c index 714888a3f3..fc4d37458c 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -1210,6 +1210,7 @@ stream_failure: } bool zapi_route_notify_decode(struct stream *s, struct prefix *p, + uint32_t *tableid, enum zapi_route_notify_owner *note) { STREAM_GET(note, s, sizeof(*note)); @@ -1218,6 +1219,7 @@ bool zapi_route_notify_decode(struct stream *s, struct prefix *p, STREAM_GETC(s, p->prefixlen); STREAM_GET(&p->u.prefix, s, PSIZE(p->prefixlen)); + STREAM_GETL(s, *tableid); return true; diff --git a/lib/zclient.h b/lib/zclient.h index d8a70c6cf3..21785abfbc 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -513,6 +513,7 @@ extern int zclient_send_rnh(struct zclient *zclient, int command, extern int zapi_route_encode(u_char, struct stream *, struct zapi_route *); extern int zapi_route_decode(struct stream *, struct zapi_route *); bool zapi_route_notify_decode(struct stream *s, struct prefix *p, + uint32_t *tableid, enum zapi_route_notify_owner *note); extern struct nexthop *nexthop_from_zapi_nexthop(struct zapi_nexthop *znh); extern bool zapi_nexthop_update_decode(struct stream *s, diff --git a/sharpd/sharp_zebra.c b/sharpd/sharp_zebra.c index 78e8cf0adc..b39fc47d3d 100644 --- a/sharpd/sharp_zebra.c +++ b/sharpd/sharp_zebra.c @@ -136,8 +136,9 @@ static int notify_owner(int command, struct zclient *zclient, { struct prefix p; enum zapi_route_notify_owner note; + uint32_t table; - if (!zapi_route_notify_decode(zclient->ibuf, &p, ¬e)) + if (!zapi_route_notify_decode(zclient->ibuf, &p, &table, ¬e)) return -1; installed_routes++; diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 967bc92850..1ba85f0ea2 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -1033,8 +1033,7 @@ void kernel_route_rib_pass_fail(struct route_node *rn, struct prefix *p, else UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); } - zsend_route_notify_owner(re->type, re->instance, re->vrf_id, - p, ZAPI_ROUTE_INSTALLED); + zsend_route_notify_owner(re, p, ZAPI_ROUTE_INSTALLED); break; case SOUTHBOUND_INSTALL_FAILURE: /* @@ -1044,8 +1043,7 @@ void kernel_route_rib_pass_fail(struct route_node *rn, struct prefix *p, */ dest->selected_fib = re; - zsend_route_notify_owner(re->type, re->instance, re->vrf_id, - p, ZAPI_ROUTE_FAIL_INSTALL); + zsend_route_notify_owner(re, p, ZAPI_ROUTE_FAIL_INSTALL); zlog_warn("%u:%s: Route install failed", re->vrf_id, prefix2str(p, buf, sizeof(buf))); break; @@ -1113,9 +1111,7 @@ void rib_install_kernel(struct route_node *rn, struct route_entry *re, * know that they've lost */ if (old && old != re) - zsend_route_notify_owner(old->type, old->instance, - old->vrf_id, p, - ZAPI_ROUTE_BETTER_ADMIN_WON); + zsend_route_notify_owner(old, p, ZAPI_ROUTE_BETTER_ADMIN_WON); /* * Make sure we update the FPM any time we send new information to diff --git a/zebra/zserv.c b/zebra/zserv.c index b3b1fa79e9..425a9549e6 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -978,21 +978,22 @@ static int zsend_ipv4_nexthop_lookup_mrib(struct zserv *client, return zebra_server_send_message(client); } -int zsend_route_notify_owner(u_char proto, u_short instance, - vrf_id_t vrf_id, struct prefix *p, +int zsend_route_notify_owner(struct route_entry *re, struct prefix *p, enum zapi_route_notify_owner note) { struct zserv *client; struct stream *s; uint8_t blen; - client = zebra_find_client(proto, instance); + client = zebra_find_client(re->type, re->instance); if (!client || !client->notify_owner) { if (IS_ZEBRA_DEBUG_PACKET) { char buff[PREFIX_STRLEN]; - zlog_debug("Not Notifying Owner: %u about prefix %s", - proto, prefix2str(p, buff, sizeof(buff))); + zlog_debug( + "Not Notifying Owner: %u about prefix %s(%u)", + re->type, prefix2str(p, buff, sizeof(buff)), + re->table); } return 0; } @@ -1000,7 +1001,7 @@ int zsend_route_notify_owner(u_char proto, u_short instance, s = client->obuf; stream_reset(s); - zclient_create_header(s, ZEBRA_ROUTE_NOTIFY_OWNER, vrf_id); + zclient_create_header(s, ZEBRA_ROUTE_NOTIFY_OWNER, re->vrf_id); stream_put(s, ¬e, sizeof(note)); @@ -1010,6 +1011,8 @@ int zsend_route_notify_owner(u_char proto, u_short instance, stream_putc(s, p->prefixlen); stream_put(s, &p->u.prefix, blen); + stream_putl(s, re->table); + stream_putw_at(s, 0, stream_get_endp(s)); return zebra_server_send_message(client); diff --git a/zebra/zserv.h b/zebra/zserv.h index 7d5f6b4543..2044816227 100644 --- a/zebra/zserv.h +++ b/zebra/zserv.h @@ -175,8 +175,7 @@ extern int zsend_interface_vrf_update(struct zserv *, struct interface *, extern int zsend_interface_link_params(struct zserv *, struct interface *); extern int zsend_pw_update(struct zserv *, struct zebra_pw *); -extern int zsend_route_notify_owner(u_char proto, u_short instance, - vrf_id_t vrf_id, struct prefix *p, +extern int zsend_route_notify_owner(struct route_entry *re, struct prefix *p, enum zapi_route_notify_owner note); extern void zserv_nexthop_num_warn(const char *, const struct prefix *, From 7a1eb44b301236d81de8ba5bde6be62e496f44bc Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Tue, 13 Feb 2018 21:34:07 -0500 Subject: [PATCH 41/98] lib: Fix notify_owner decode The notification of the owner was not properly decoding the prefix and as such we were not properly reading the table it was installed into. Signed-off-by: Donald Sharp --- lib/zclient.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/zclient.c b/lib/zclient.c index fc4d37458c..d6a6cee731 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -1213,13 +1213,17 @@ bool zapi_route_notify_decode(struct stream *s, struct prefix *p, uint32_t *tableid, enum zapi_route_notify_owner *note) { + uint32_t t; + STREAM_GET(note, s, sizeof(*note)); STREAM_GETC(s, p->family); STREAM_GETC(s, p->prefixlen); STREAM_GET(&p->u.prefix, s, - PSIZE(p->prefixlen)); - STREAM_GETL(s, *tableid); + prefix_blen(p)); + STREAM_GETL(s, t); + + *tableid = t; return true; From eaa23e020fdd139639a060e924f3a524d8f50393 Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Tue, 13 Feb 2018 21:32:22 -0500 Subject: [PATCH 42/98] zebra: Add some useful debugs for notifying the owner Add a bit more detail to tell us what we are sending up to a protocol so we can debug it better in the future. Signed-off-by: Donald Sharp --- zebra/zserv.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/zebra/zserv.c b/zebra/zserv.c index 425a9549e6..301639cbd1 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -991,13 +991,21 @@ int zsend_route_notify_owner(struct route_entry *re, struct prefix *p, char buff[PREFIX_STRLEN]; zlog_debug( - "Not Notifying Owner: %u about prefix %s(%u)", + "Not Notifying Owner: %u about prefix %s(%u) %d", re->type, prefix2str(p, buff, sizeof(buff)), - re->table); + re->table, note); } return 0; } + if (IS_ZEBRA_DEBUG_PACKET) { + char buff[PREFIX_STRLEN]; + + zlog_debug("Notifying Owner: %u about prefix %s(%u) %d", + re->type, prefix2str(p, buff, sizeof(buff)), + re->table, note); + } + s = client->obuf; stream_reset(s); From 9a9f89267a4aaf2904673b204a21e05e0832bbef Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Tue, 13 Feb 2018 21:30:03 -0500 Subject: [PATCH 43/98] zebra: Fix situation where we would notify the owner it lost due to admin distance The 'struct route_entry *old' and 'struct route_entry *new' can sometimes be the same route type( for a route replace ), so when we are checking to see if a new owner has taken over, don't tell the owner it is replacing it self. Signed-off-by: Donald Sharp type != re->type)) zsend_route_notify_owner(old, p, ZAPI_ROUTE_BETTER_ADMIN_WON); /* From 28b11f81061b12f599daf8016d948ca59a2f38cc Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Fri, 23 Feb 2018 14:40:46 -0500 Subject: [PATCH 44/98] *: Modify notify_owner to route_notify_owner In the future we are going to have a rule_notify_owner so make the distinction between the two types of notification clearer. Signed-off-by: Donald Sharp --- eigrpd/eigrp_zebra.c | 6 +++--- lib/zclient.c | 6 +++--- lib/zclient.h | 4 ++-- sharpd/sharp_zebra.c | 6 +++--- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/eigrpd/eigrp_zebra.c b/eigrpd/eigrp_zebra.c index 513fda06f3..e8392f50b4 100644 --- a/eigrpd/eigrp_zebra.c +++ b/eigrpd/eigrp_zebra.c @@ -94,8 +94,8 @@ static int eigrp_router_id_update_zebra(int command, struct zclient *zclient, return 0; } -static int eigrp_zebra_notify_owner(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int eigrp_zebra_route_notify_owner(int command, struct zclient *zclient, + zebra_size_t length, vrf_id_t vrf_id) { struct prefix p; enum zapi_route_notify_owner note; @@ -129,7 +129,7 @@ void eigrp_zebra_init(void) zclient->interface_address_delete = eigrp_interface_address_delete; zclient->redistribute_route_add = eigrp_zebra_read_route; zclient->redistribute_route_del = eigrp_zebra_read_route; - zclient->notify_owner = eigrp_zebra_notify_owner; + zclient->route_notify_owner = eigrp_zebra_route_notify_owner; } diff --git a/lib/zclient.c b/lib/zclient.c index d6a6cee731..d11457a859 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -2369,9 +2369,9 @@ static int zclient_read(struct thread *thread) vrf_id); break; case ZEBRA_ROUTE_NOTIFY_OWNER: - if (zclient->notify_owner) - (*zclient->notify_owner)(command, zclient, - length, vrf_id); + if (zclient->route_notify_owner) + (*zclient->route_notify_owner)(command, zclient, length, + vrf_id); break; default: break; diff --git a/lib/zclient.h b/lib/zclient.h index 21785abfbc..4c84af1f61 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -213,8 +213,8 @@ struct zclient { int (*local_macip_add)(int, struct zclient *, uint16_t, vrf_id_t); int (*local_macip_del)(int, struct zclient *, uint16_t, vrf_id_t); int (*pw_status_update)(int, struct zclient *, uint16_t, vrf_id_t); - int (*notify_owner)(int command, struct zclient *zclient, - uint16_t length, vrf_id_t vrf_id); + int (*route_notify_owner)(int command, struct zclient *zclient, + uint16_t length, vrf_id_t vrf_id); }; /* Zebra API message flag. */ diff --git a/sharpd/sharp_zebra.c b/sharpd/sharp_zebra.c index b39fc47d3d..3b22db20aa 100644 --- a/sharpd/sharp_zebra.c +++ b/sharpd/sharp_zebra.c @@ -131,8 +131,8 @@ static int interface_state_down(int command, struct zclient *zclient, extern uint32_t total_routes; extern uint32_t installed_routes; -static int notify_owner(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int route_notify_owner(int command, struct zclient *zclient, + zebra_size_t length, vrf_id_t vrf_id) { struct prefix p; enum zapi_route_notify_owner note; @@ -211,5 +211,5 @@ void sharp_zebra_init(void) zclient->interface_down = interface_state_down; zclient->interface_address_add = interface_address_add; zclient->interface_address_delete = interface_address_delete; - zclient->notify_owner = notify_owner; + zclient->route_notify_owner = route_notify_owner; } From 194a4f2c5c7f1fd1ea93e97d71bb841ac08947a4 Mon Sep 17 00:00:00 2001 From: Daniel Walton Date: Mon, 26 Feb 2018 22:13:22 +0000 Subject: [PATCH 45/98] bgpd: use peer->ifp->ifindex instead of peer->ifindex Signed-off-by: Daniel Walton peer->ifindex was only used in two places but it was never populated so neither of them worked as they should. 'struct peer' also has a 'struct interface' pointer which we can use to get the ifindex. --- bgpd/bgp_dump.c | 4 ++-- bgpd/bgp_mpath.c | 23 ++++++++++++++++++++++- bgpd/bgpd.c | 1 - bgpd/bgpd.h | 1 - 4 files changed, 24 insertions(+), 5 deletions(-) diff --git a/bgpd/bgp_dump.c b/bgpd/bgp_dump.c index 0831369062..4e998b1fd9 100644 --- a/bgpd/bgp_dump.c +++ b/bgpd/bgp_dump.c @@ -465,7 +465,7 @@ static void bgp_dump_common(struct stream *obuf, struct peer *peer, } if (peer->su.sa.sa_family == AF_INET) { - stream_putw(obuf, peer->ifindex); + stream_putw(obuf, peer->ifp ? peer->ifp->ifindex : 0); stream_putw(obuf, AFI_IP); stream_put(obuf, &peer->su.sin.sin_addr, IPV4_MAX_BYTELEN); @@ -477,7 +477,7 @@ static void bgp_dump_common(struct stream *obuf, struct peer *peer, stream_put(obuf, empty, IPV4_MAX_BYTELEN); } else if (peer->su.sa.sa_family == AF_INET6) { /* Interface Index and Address family. */ - stream_putw(obuf, peer->ifindex); + stream_putw(obuf, peer->ifp ? peer->ifp->ifindex : 0); stream_putw(obuf, AFI_IP6); /* Source IP Address and Destination IP Address. */ diff --git a/bgpd/bgp_mpath.c b/bgpd/bgp_mpath.c index 9d32c4bee1..5d83192a30 100644 --- a/bgpd/bgp_mpath.c +++ b/bgpd/bgp_mpath.c @@ -92,6 +92,26 @@ int bgp_maximum_paths_unset(struct bgp *bgp, afi_t afi, safi_t safi, return 0; } +/* + * bgp_interface_same + * + * Return true if ifindex for ifp1 and ifp2 are the same, else return false. + */ +static int bgp_interface_same(struct interface *ifp1, struct interface *ifp2) +{ + if (!ifp1 && !ifp2) + return 1; + + if (!ifp1 && ifp2) + return 0; + + if (ifp1 && !ifp2) + return 0; + + return (ifp1->ifindex == ifp2->ifindex); +} + + /* * bgp_info_nexthop_cmp * @@ -130,7 +150,8 @@ int bgp_info_nexthop_cmp(struct bgp_info *bi1, struct bgp_info *bi2) if (!bi1->attr->mp_nexthop_prefer_global && !bi2->attr->mp_nexthop_prefer_global) - compare = !(bi1->peer->ifindex == bi2->peer->ifindex); + compare = !bgp_interface_same(bi1->peer->ifp, bi2->peer->ifp); + if (!compare) compare = IPV6_ADDR_CMP(&addr1, &addr2); break; diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 78e748fb6c..93493001b5 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -1200,7 +1200,6 @@ void peer_xfer_config(struct peer *peer_dst, struct peer *peer_src) peer_dst->config = peer_src->config; peer_dst->local_as = peer_src->local_as; - peer_dst->ifindex = peer_src->ifindex; peer_dst->port = peer_src->port; (void)peer_sort(peer_dst); peer_dst->rmap_type = peer_src->rmap_type; diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 220b6d989e..dd0f64f565 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -686,7 +686,6 @@ struct peer { time_t readtime; /* Last read time */ time_t resettime; /* Last reset time */ - ifindex_t ifindex; /* ifindex of the BGP connection. */ char *conf_if; /* neighbor interface config name. */ struct interface *ifp; /* corresponding interface */ char *ifname; /* bind interface name. */ From 4e8b02f4df5d6bcfde6390955b8feda2a17dc9bd Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Mon, 26 Feb 2018 21:26:33 -0500 Subject: [PATCH 46/98] *: Rename ZEBRA_FLAG_INTERNAL -> ZEBRA_FLAG_ALLOW_RECURSION The ZEBRA_FLAG_INTERNAL flag is used to signal to zebra that the route being added, the nexthops for it can be recursively resolved. This name keeps throwing me off when I read it so let's rename to something that allows the developer to understand what is going on. Signed-off-by: Donald Sharp --- bgpd/bgp_zebra.c | 8 ++++---- lib/zebra.h | 2 +- nhrpd/nhrp_route.c | 2 +- pimd/TODO | 4 ++-- zebra/zebra_rib.c | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index 1da6136428..daa7ccbf42 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -1026,14 +1026,14 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p, if (peer->sort == BGP_PEER_IBGP || peer->sort == BGP_PEER_CONFED || info->sub_type == BGP_ROUTE_AGGREGATE) { SET_FLAG(api.flags, ZEBRA_FLAG_IBGP); - SET_FLAG(api.flags, ZEBRA_FLAG_INTERNAL); + SET_FLAG(api.flags, ZEBRA_FLAG_ALLOW_RECURSION); } if ((peer->sort == BGP_PEER_EBGP && peer->ttl != 1) || CHECK_FLAG(peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK) || bgp_flag_check(bgp, BGP_FLAG_DISABLE_NH_CONNECTED_CHK)) - SET_FLAG(api.flags, ZEBRA_FLAG_INTERNAL); + SET_FLAG(api.flags, ZEBRA_FLAG_ALLOW_RECURSION); /* Metric is currently based on the best-path only */ metric = info->attr->med; @@ -1265,14 +1265,14 @@ void bgp_zebra_withdraw(struct prefix *p, struct bgp_info *info, safi_t safi) SET_FLAG(api.flags, ZEBRA_FLAG_EVPN_ROUTE); if (peer->sort == BGP_PEER_IBGP) { - SET_FLAG(api.flags, ZEBRA_FLAG_INTERNAL); + SET_FLAG(api.flags, ZEBRA_FLAG_ALLOW_RECURSION); SET_FLAG(api.flags, ZEBRA_FLAG_IBGP); } if ((peer->sort == BGP_PEER_EBGP && peer->ttl != 1) || CHECK_FLAG(peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK) || bgp_flag_check(peer->bgp, BGP_FLAG_DISABLE_NH_CONNECTED_CHK)) - SET_FLAG(api.flags, ZEBRA_FLAG_INTERNAL); + SET_FLAG(api.flags, ZEBRA_FLAG_ALLOW_RECURSION); if (bgp_debug_zebra(p)) { char buf[PREFIX_STRLEN]; diff --git a/lib/zebra.h b/lib/zebra.h index b9a795d160..11bf764b63 100644 --- a/lib/zebra.h +++ b/lib/zebra.h @@ -402,7 +402,7 @@ extern const char *zserv_command_string(unsigned int command); #define strmatch(a,b) (!strcmp((a), (b))) /* Zebra message flags */ -#define ZEBRA_FLAG_INTERNAL 0x01 +#define ZEBRA_FLAG_ALLOW_RECURSION 0x01 #define ZEBRA_FLAG_SELFROUTE 0x02 #define ZEBRA_FLAG_IBGP 0x08 #define ZEBRA_FLAG_SELECTED 0x10 diff --git a/nhrpd/nhrp_route.c b/nhrpd/nhrp_route.c index d43aa4929e..8178a8b4b5 100644 --- a/nhrpd/nhrp_route.c +++ b/nhrpd/nhrp_route.c @@ -114,7 +114,7 @@ void nhrp_route_announce(int add, enum nhrp_cache_type type, const struct prefix SET_FLAG(api.flags, ZEBRA_FLAG_FIB_OVERRIDE); break; } - SET_FLAG(api.flags, ZEBRA_FLAG_INTERNAL); + SET_FLAG(api.flags, ZEBRA_FLAG_ALLOW_RECURSION); SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP); api.nexthop_num = 1; diff --git a/pimd/TODO b/pimd/TODO index 57d2a17b5f..b9e4152830 100644 --- a/pimd/TODO +++ b/pimd/TODO @@ -167,9 +167,9 @@ T26 DONE Zebra daemon is marking recursive static route as inactive. pointing to kernel routes as inactive: zebra/zebra_rib.c nexthop_active_ipv4: -- Original: - else if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_INTERNAL)) + else if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_ALLOW_RECURSION)) -- Fixed: - else if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_INTERNAL) || + else if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_ALLOW_RECURSION) || match->type == ZEBRA_ROUTE_KERNEL) Old problem description: diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 5c316e077f..7f6c8aefa8 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -514,7 +514,7 @@ static int nexthop_active(afi_t afi, struct route_entry *re, nexthop->ifindex = newhop->ifindex; } return 1; - } else if (CHECK_FLAG(re->flags, ZEBRA_FLAG_INTERNAL)) { + } else if (CHECK_FLAG(re->flags, ZEBRA_FLAG_ALLOW_RECURSION)) { resolved = 0; for (ALL_NEXTHOPS(match->nexthop, newhop)) { if (!CHECK_FLAG(newhop->flags, From 95d962bf1dcd60669e14d5fc7fb97a21501607c3 Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Mon, 26 Feb 2018 21:33:02 -0500 Subject: [PATCH 47/98] pimd: Cleanup TODO file The TODO file had become a dumping ground of changes made to the protocol. That when we modified the underlying code we would need to update the TODO file again due to it's nature. There is no point in keeping the list of items that we've done as that commit messages will indicate the new features. So pull out all the Finished work and rework the Todo numbers used. Signed-off-by: Donald Sharp --- pimd/TODO | 372 ++---------------------------------------------------- 1 file changed, 9 insertions(+), 363 deletions(-) diff --git a/pimd/TODO b/pimd/TODO index b9e4152830..4a147751dd 100644 --- a/pimd/TODO +++ b/pimd/TODO @@ -1,375 +1,21 @@ -T1 DONE Implement debug command - test pim receive join - -T2 DONE Implement debug command - test pim receive prune - -T3 DONE Per-interface Downstream (S,G) state machine - (RFC 4601 4.5.3. Receiving (S,G) Join/Prune Messages) - -T4 DONE Upstream (S,G) state machine - (RFC 4601 4.5.7. Sending (S,G) Join/Prune Messages) - -T5 DONE Verify Data Packet Forwarding Rules - RFC 4601 4.2. Data Packet Forwarding Rules - RFC 4601 4.8.2. PIM-SSM-Only Routers - - Additionally, the Packet forwarding rules of Section 4.2 can be - simplified in a PIM-SSM-only router: - - iif is the incoming interface of the packet. - oiflist = NULL - if (iif == RPF_interface(S) AND UpstreamJPState(S,G) == Joined) { - oiflist = inherited_olist(S,G) - } else if (iif is in inherited_olist(S,G)) { - send Assert(S,G) on iif - } - oiflist = oiflist (-) iif - forward packet on all interfaces in oiflist - - Macro: - inherited_olist(S,G) = - joins(S,G) (+) pim_include(S,G) (-) lost_assert(S,G) - -T6 DONE Implement (S,G) Assert state machine (RFC 4601, section 4.6.1). - Changes in pim_ifchannel.ifassert_winner should trigger - pim_upstream_update_join_desired(). - Depends on TODO T27. - Depends on TODO T33. - See also CAVEAT C7. - See also: RFC 4601 4.5.7. Sending (S,G) Join/Prune Messages - Transitions from Joined State - RPF'(S,G) changes due to an Assert - - http://www.hep.ucl.ac.uk/~ytl/multi-cast/pim-dm_01.html: - - The PIM Assert mechanism is used to shutoff duplicate flows onto - the same multiaccess network. Routers detect this condiction when - they receive an (S,G) packet via a multi-access interface that is - in the (S,G) OIL. This causes the routers to send Assert - Messages. - - Note that neighbors will not accept Join/Prune or Assert messages - from a router unless they have first heard a Hello message from that - router. Thus, if a router needs to send a Join/Prune or Assert - message on an interface on which it has not yet sent a Hello message - with the currently configured IP address, then it MUST immediately - send the relevant Hello message without waiting for the Hello Timer - to expire, followed by the Join/Prune or Assert message. - -T7 DONE Implement hello option: LAN Prune Delay - -T8 DONE Implement J/P_Override_Interval(I) - Depends on TODO T7. - See pim_ifchannel.c, pim_ifchannel_prune(), jp_override_interval. - -T9 DONE Detect change in IGMPv3 RPF interface/next-hop for S and update. - channel_oil vif index accordingly ? - Beware accidentaly adding looped MFC entries (IIF=OIF). - -T10 DONE React to (S,G) join directed to another upstream address. See - also: - - RFC 4601: 4.5.7. Sending (S,G) Join/Prune Messages - - If a router wishes to propagate a Join(S,G) upstream, it must also - watch for messages on its upstream interface from other routers on - that subnet, and these may modify its behavior. If it sees a - Join(S,G) to the correct upstream neighbor, it should suppress its - own Join(S,G). If it sees a Prune(S,G), Prune(S,G,rpt), or - Prune(*,G) to the correct upstream neighbor towards S, it should - be prepared to override that prune by scheduling a Join(S,G) to be - sent almost immediately. - -T11 DONE Review protocol modifications for SSM - (RFC 4601 4.8.1. Protocol Modifications for SSM Destination - Addresses) - -T12 DONE Review updates of RPF entries. - FIXME pim_upstream.c send_join(): - Currently only one upstream state is affected by detection of RPF change. - RPF change should affect all upstream states sharing the RPF cache. - -T13 DONE Check that RFC macros using S,G,RPF_interface(S) are actually - implemented with this strategy: - rpf_ifch=find_ifch(up->rpf->interface). - See pim_rpf.c pim_rpf_find_rpf_addr() for a correct example. - - $ grep -i macro pimd/*.c - pimd/pim_iface.c: RFC 4601: 4.1.6. State Summarization Macros - pimd/pim_ifchannel.c: RFC 4601: 4.6.5. Assert State Macros - pimd/pim_ifchannel.c: RFC 4601: 4.1.6. State Summarization Macros - pimd/pim_ifchannel.c: RFC 4601: 4.1.6. State Summarization Macros - pimd/pim_ifchannel.c: RFC 4601: 4.6.5. Assert State Macros - pimd/pim_ifchannel.c: Macro: - pimd/pim_rpf.c: RFC 4601: 4.1.6. State Summarization Macros - -T14 DONE Send Assert(S,G) on iif as response to WRONGVIF kernel upcall. - See pim_mroute.c mroute_msg(). - -T15 DONE Interface command to statically join (S,G). - interface eth0 - ip igmp join-group 239.1.1.1 source 1.1.1.1 - -T16 DONE RPF'(S,G) lookup is not working for S reachable with default route. - See "RPF'(S,G) not found" in pim_rpf_update() from pim_rpf.c. - Zebra daemon RIB is not reflecting changes in kernel routes - accurately? - -T17 DONE Prevent CLI from creating bogus interfaces. - Example: - conf t - interface xxx - -T18 Consider reliable pim solution (refresh reduction) +T1 Consider reliable pim solution (refresh reduction) A Reliable Transport Mechanism for PIM http://tools.ietf.org/wg/pim/draft-ietf-pim-port/ PORT=PIM-Over-Reliable-Transport -T19 DONE Fix self as neighbor - See mailing list post: - http://lists.gnu.org/archive/html/qpimd-users/2009-04/msg00000.html - -T20 DONE Fix debug message: "pim_neighbor_update: internal error: - trying to replace same prefix list" - See mailing list post: - http://lists.gnu.org/archive/html/qpimd-users/2009-04/msg00000.html - -T21 DONE Clean-up PIM/IGMP interface mismatch debugging - See option PIM_CHECK_RECV_IFINDEX_SANITY in pimd/Makefile.am - See mailing list post: - http://lists.nongnu.org/archive/html/qpimd-users/2009-04/msg00003.html - -T22 DONE IGMP must be protected against adding looped MFC entries - created by both source and receiver attached to the same - interface. - -T23 DONE libfrr crash after zclient_lookup_nexthop. - See mailing list post: - http://lists.nongnu.org/archive/html/qpimd-users/2009-04/msg00008.html - -T24 DONE zserv may return recursive routes: - - nexthop type is set to ZEBRA_NEXTHOP_IPV4 - - ifindex is not reported - - calls expecting ifindex (fib_lookup_if_vif_index) are disrupted - See also this mailing list post: - [PATCH 21/21] Link detect and recursive routes - http://www.gossamer-threads.com/lists/quagga/dev/17564 - -T25 DONE Zclient nexthop lookup missing OSPF route to 1.1.1.1/32 - See also: - pim_zlookup.c zclient_lookup_nexthop misses OSPF 1.1.1.1/32 - zebra/zebra_vty.c show_ip_route_addr_cmd hits OSPF 1.1.1.1/32 - -T26 DONE Zebra daemon is marking recursive static route as inactive. - - FIXED: zebra daemon was incorrectly marking recursive routes - pointing to kernel routes as inactive: - zebra/zebra_rib.c nexthop_active_ipv4: - -- Original: - else if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_ALLOW_RECURSION)) - -- Fixed: - else if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_ALLOW_RECURSION) || - match->type == ZEBRA_ROUTE_KERNEL) - - Old problem description: - - This prevents rib_match_ipv4 from returning its nexthop: - client: pim_zlookup.c zclient_read_nexthop - server: zebra/zserv.c zsend_ipv4_nexthop_lookup_v2 -> rib_match_ipv4 - - Kernel route is injected into zebra in zebra_rib.c rib_add_ipv4 - Examples: - rt_netlink.c:726: rib_add_ipv4 (ZEBRA_ROUTE_KERNEL, flags, &p, gate, src, index, table, metric, 0); - rt_netlink.c:864: rib_add_ipv4 (ZEBRA_ROUTE_KERNEL, 0, &p, gate, src, index, table, 0, 0); - - This patch didn't fix the issue: - [PATCH 21/21] Link detect and recursive routes - http://www.gossamer-threads.com/lists/quagga/dev/17564 - - See the example below for the route 2.2.2.2. - -bash# route add -host 1.1.1.1 gw 127.0.0.1 -bash# route add -host 2.2.2.2 gw 1.1.1.1 -bash# netstat -nvr -Kernel IP routing table -Destination Gateway Genmask Flags MSS Window irtt Iface -2.2.2.2 1.1.1.1 255.255.255.255 UGH 0 0 0 lo -1.1.1.1 127.0.0.1 255.255.255.255 UGH 0 0 0 lo -192.168.0.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0 -0.0.0.0 192.168.0.2 0.0.0.0 UG 0 0 0 eth0 -bash# - -zebra# sh ip route -Codes: K - kernel route, C - connected, S - static, R - RIP, O - OSPF, - I - ISIS, B - BGP, > - selected route, * - FIB route - -K>* 0.0.0.0/0 via 192.168.0.2, eth0 -K>* 1.1.1.1/32 via 127.0.0.1, lo -K * 2.2.2.2/32 via 1.1.1.1, lo inactive -C>* 127.0.0.0/8 is directly connected, lo -C>* 192.168.0.0/24 is directly connected, eth0 - -quagga-pimd-router# sh ip route 1.1.1.1 -Address NextHop Interface Metric Preference -1.1.1.1 127.0.0.1 lo 0 0 -quagga-pimd-router# -quagga-pimd-router# sh ip route 2.2.2.2 -Address NextHop Interface Metric Preference -2.2.2.2 192.168.0.2 eth0 0 0 -quagga-pimd-router# - -T27 DONE Implement debug command - test pim receive assert - See also TODO T6: (S,G) Assert state machine. - -T28 DONE Bad IPv4 address family=02 in Join/Prune dump - Reported by Andrew Lunn - - # 58-byte pim v2 Join/Prune dump - # ------------------------------ - # IPv4 address family=02 is wrong, correct IPv4 address family is 01 - # See http://www.iana.org/assignments/address-family-numbers - # - c8XX YY03 : ip src 200.xx.yy.3 - e000 000d : ip dst 224.0.0.13 - 9404 0000 : ip router alert option 148.4.0.0 - 2300 ab13 : pimv2,type=3 res=00 checksum=ab13 - 0200 : upstream family=02, encoding=00 - c8XX YY08 : upstream 200.xx.yy.8 - 0001 00d2 : res=00 groups=01 holdtime=00d2 - 0200 0020 : group family=02, encoding=00, res=00, mask_len=20 - ef01 0101 : group address 239.1.1.1 - 0001 0000 : joined=0001 pruned=0000 - 0200 0020 : source family=02, encoding=00, res=00, mask_len=20 - 0101 0101 : source address 1.1.1.1 - -T29 DONE Reset interface PIM-hello-sent counter when primary address changes - See pim_ifp->pim_ifstat_hello_sent - - RFC 4601: 4.3.1. Sending Hello Messages - - Thus, if a router needs to send a Join/Prune or Assert message on - an interface on which it has not yet sent a Hello message with the - currently configured IP address, then it MUST immediately send the - relevant Hello message without waiting for the Hello Timer to - expire, followed by the Join/Prune or Assert message. - -T30 DONE Run interface DR election when primary address changes - Reported by Andrew Lunn - See pim_if_dr_election(). - -T31 If an interface changes one of its secondary IP addresses, a Hello +T2 If an interface changes one of its secondary IP addresses, a Hello message with an updated Address_List option and a non-zero HoldTime should be sent immediately. See also detect_secondary_address_change See also CAVEAT C15. See also RFC 4601: 4.3.1. Sending Hello Messages -T32 FIXED Detection of interface primary address changes may fail when - there are multiple addresses. - See also CAVEAT C14. - - pim_find_primary_addr() should return interface primary address - from connected list. Currently it returns the first address. - - Zebra daemon "show int" is able to keep the primary address as - first address. - -T33 DONE Implement debug command: test pim receive upcall - See also TODO T6: (S,G) Assert state machine. - -T34 DONE assert_action_a1 - -T35 DONE Review macros depending on interface I. - - See also: grep ,I\) pimd/*.c - - For the case (S,G,I) check if I is either - 1) interface attached to this per-interface S,G state (don't think so) - or - 2) an arbitrary interface (most probably) - - For the arbitrary interface case (2), consider representing - interface ifp as its primary address (struct in_addr ifaddr). The - benefit is in_addr does not need to be dereferenced, so it does - not demand protection against crashes. - -T36 DONE React to zebra daemon link-detect up/down notification. - pim_ifp->primary_address is managed by detect_primary_address_change() - depending on to ifp->connected (managed by zebra_interface_address_read()). - -T37 DONE Review list of variables which may affect pim_upstream.c - pim_upstream_evaluate_join_desired(). - Call pim_upstream_update_join_desired() accordingly. - - See the order of invokation: - pim_if_dr_election(ifp); - pim_if_update_join_desired(pim_ifp); /* depends on DR */ - pim_if_update_could_assert(ifp); /* depends on DR */ - pim_if_update_my_assert_metric(ifp); /* depends on could_assert */ - - join_desired depends on: - pim_ifp->primary_address - pim_ifp->pim_dr_addr - ch->ifassert_winner_metric - ch->ifassert_winner - ch->local_ifmembership - ch->ifjoin_state - ch->upstream->rpf.source_nexthop.mrib_metric_preference - ch->upstream->rpf.source_nexthop.mrib_route_metric - ch->upstream->rpf.source_nexthop.interface - -T38 DONE Detect change in AssertTrackingDesired(S,G,I) - - See the order of invokation: - dr_election: none - update_join_desired: depends on DR - update_tracking_desired: depends on DR, join_desired - - AssertTrackingDesired(S,G,I) depends on: - pim_ifp->primary_address - pim_ifp->pim_dr_addr - ch->local_ifmembership - ch->ifassert_winner - ch->ifjoin_state - ch->upstream->rpf.source_nexthop.interface - PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED(ch->upstream->flags) - -T39 DONE AssertTrackingDesired: flags is not matching evaluation - - # show ip pim assert-internal - CA: CouldAssert - ECA: Evaluate CouldAssert - ATD: AssertTrackingDesired - eATD: Evaluate AssertTrackingDesired - - Interface Address Source Group CA eCA ATD eATD - eth0 192.168.1.100 1.1.1.1 239.1.1.1 no no no yes - # - -T40 Lightweight MLDv2 +T3 Lightweight MLDv2 http://tools.ietf.org/html/draft-ietf-mboned-lightweight-igmpv3-mldv2-05 http://www.ietf.org/internet-drafts/draft-ietf-mboned-lightweight-igmpv3-mldv2-05.txt http://www.ietf.org/html.charters/mboned-charter.html -T41 DONE ssmping support - - See also: - http://www.venaas.no/multicast/ssmping/ - draft-ietf-mboned-ssmping-07 - http://tools.ietf.org/html/draft-ietf-mboned-ssmping-07 - - Example: - - debug ssmpingd - - conf t - ip ssmpingd 1.1.1.1 - - show ip ssmpingd - -T42 Static igmp join fails when loading config at boot time +T4 Static igmp join fails when loading config at boot time ! Wrong behavior seen at boot time: ! @@ -396,7 +42,7 @@ T42 Static igmp join fails when loading config at boot time eth0 200.202.112.3 2 2 0 0 0 0 lo 127.0.0.1 1 1 0 0 0 0 -T43 PIM Neighbor Reduction +T5 PIM Neighbor Reduction https://datatracker.ietf.org/doc/draft-wijnands-pim-neighbor-reduction/ "In a transit LAN (no directly connected source or receiver), many @@ -404,19 +50,19 @@ T43 PIM Neighbor Reduction a procedure to reduce the amount of neighbors established over a transit LAN." -T44 Single Stream Multicast Fast Reroute (SMFR) Method +T6 Single Stream Multicast Fast Reroute (SMFR) Method https://datatracker.ietf.org/doc/draft-liu-pim-single-stream-multicast-frr/ "This document proposes an IP multicast fast convergence method based on differentiating primary and backup PIM join." -T45 RFC5384 - The Join Attribute Format +T7 RFC5384 - The Join Attribute Format "This document describes a modification of the Join message that allows a node to associate attributes with a particular tree." -T46 PIM Multi-Topology ID (MT-ID) Join-Attribute +T8 PIM Multi-Topology ID (MT-ID) Join-Attribute http://tools.ietf.org/html/draft-cai-pim-mtid-00 - Depends on T45. + Depends on T7. "This draft introduces a new type of PIM Join Attribute used to encode the identity of the topology PIM uses for RPF." From a3896844153e8f0105dc33e007805650af42185d Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Mon, 26 Feb 2018 21:44:47 -0500 Subject: [PATCH 48/98] sharpd: Allow sharp routes to recurse When passing in a sharp route, allow the nexthop choosen to recurse to find a match. Signed-off-by: Donald Sharp --- sharpd/sharp_zebra.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sharpd/sharp_zebra.c b/sharpd/sharp_zebra.c index 3b22db20aa..f02ce4979c 100644 --- a/sharpd/sharp_zebra.c +++ b/sharpd/sharp_zebra.c @@ -169,6 +169,7 @@ void route_add(struct prefix *p, struct nexthop *nh) api.safi = SAFI_UNICAST; memcpy(&api.prefix, p, sizeof(*p)); + SET_FLAG(api.flags, ZEBRA_FLAG_ALLOW_RECURSION); SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP); api_nh = &api.nexthops[0]; From ee851c8c771c475706b4b4980cad314c40291581 Mon Sep 17 00:00:00 2001 From: Mitesh Kanjariya Date: Mon, 19 Feb 2018 00:57:54 -0800 Subject: [PATCH 49/98] bgpd: add show bgp vrf all l2vpn evpn summary as an option Ticket: CM-19738 Review: CCR-7194 Testing: Manual Signed-off-by: Mitesh Kanjariya --- bgpd/bgp_evpn_vty.c | 11 +++++++++-- bgpd/bgp_vty.c | 3 +-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c index 1373afec4e..131bb82276 100644 --- a/bgpd/bgp_evpn_vty.c +++ b/bgpd/bgp_evpn_vty.c @@ -2967,16 +2967,23 @@ DEFUN(show_bgp_l2vpn_evpn_vni, */ DEFUN(show_bgp_l2vpn_evpn_summary, show_bgp_l2vpn_evpn_summary_cmd, - "show bgp l2vpn evpn summary [json]", + "show bgp [vrf VRFNAME] l2vpn evpn summary [json]", SHOW_STR BGP_STR + "bgp vrf\n" + "vrf name\n" L2VPN_HELP_STR EVPN_HELP_STR "Summary of BGP neighbor status\n" JSON_STR) { + int idx_vrf = 0; u_char uj = use_json(argc, argv); - return bgp_show_summary_vty(vty, NULL, AFI_L2VPN, SAFI_EVPN, uj); + char *vrf = NULL; + + if (argv_find(argv, argc, "vrf", &idx_vrf)) + vrf = argv[++idx_vrf]->arg; + return bgp_show_summary_vty(vty, vrf, AFI_L2VPN, SAFI_EVPN, uj); } /* diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index c7140b2f1f..15cc5673ac 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -7278,8 +7278,7 @@ static void bgp_show_summary_afi_safi(struct vty *vty, struct bgp *bgp, int afi, safi = SAFI_MAX; } afi++; - if (!afi_wildcard - || afi == AFI_L2VPN) /* special case, not handled yet */ + if (!afi_wildcard) afi = AFI_MAX; } From db29a4a8bc91c0a7bcea8c411b7c9d41b2ba44d2 Mon Sep 17 00:00:00 2001 From: Mitesh Kanjariya Date: Mon, 19 Feb 2018 01:39:48 -0800 Subject: [PATCH 50/98] bgpd: move route-target for a vrf under address-family evpn command Signed-off-by: Mitesh Kanjariya --- bgpd/bgp_evpn_vty.c | 32 ++++++++++++++++++++++++++++++++ bgpd/bgpd.c | 32 -------------------------------- 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c index 131bb82276..db865adf11 100644 --- a/bgpd/bgp_evpn_vty.c +++ b/bgpd/bgp_evpn_vty.c @@ -4393,6 +4393,38 @@ void bgp_config_write_evpn_info(struct vty *vty, struct bgp *bgp, afi_t afi, if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_ADVERTISE_IPV6_IN_EVPN)) vty_out(vty, " advertise ipv6 unicast\n"); + + /* import route-target */ + if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_IMPORT_RT_CFGD)) { + char *ecom_str; + struct listnode *node, *nnode; + struct ecommunity *ecom; + + for (ALL_LIST_ELEMENTS(bgp->vrf_import_rtl, node, nnode, + ecom)) { + ecom_str = ecommunity_ecom2str( + ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0); + vty_out(vty, " route-target import %s\n", + ecom_str); + XFREE(MTYPE_ECOMMUNITY_STR, ecom_str); + } + } + + /* export route-target */ + if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_EXPORT_RT_CFGD)) { + char *ecom_str; + struct listnode *node, *nnode; + struct ecommunity *ecom; + + for (ALL_LIST_ELEMENTS(bgp->vrf_export_rtl, node, nnode, + ecom)) { + ecom_str = ecommunity_ecom2str( + ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0); + vty_out(vty, " route-target export %s\n", + ecom_str); + XFREE(MTYPE_ECOMMUNITY_STR, ecom_str); + } + } } void bgp_ethernetvpn_init(void) diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 78e748fb6c..de2b913056 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -7315,38 +7315,6 @@ int bgp_config_write(struct vty *vty) if (bgp_option_check(BGP_OPT_CONFIG_CISCO)) vty_out(vty, " no auto-summary\n"); - /* import route-target */ - if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_IMPORT_RT_CFGD)) { - char *ecom_str; - struct listnode *node, *nnode; - struct ecommunity *ecom; - - for (ALL_LIST_ELEMENTS(bgp->vrf_import_rtl, node, nnode, - ecom)) { - ecom_str = ecommunity_ecom2str( - ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0); - vty_out(vty, " route-target import %s\n", - ecom_str); - XFREE(MTYPE_ECOMMUNITY_STR, ecom_str); - } - } - - /* export route-target */ - if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_EXPORT_RT_CFGD)) { - char *ecom_str; - struct listnode *node, *nnode; - struct ecommunity *ecom; - - for (ALL_LIST_ELEMENTS(bgp->vrf_export_rtl, node, nnode, - ecom)) { - ecom_str = ecommunity_ecom2str( - ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0); - vty_out(vty, " route-target export %s\n", - ecom_str); - XFREE(MTYPE_ECOMMUNITY_STR, ecom_str); - } - } - /* IPv4 unicast configuration. */ bgp_config_write_family(vty, bgp, AFI_IP, SAFI_UNICAST); From a75c25534a30b47b39f80b0fb8d66fd67f4836e0 Mon Sep 17 00:00:00 2001 From: Mitesh Kanjariya Date: Mon, 19 Feb 2018 02:04:27 -0800 Subject: [PATCH 51/98] bgpd: write vrf rd to config When a non-default vrf rd is configured under l2vpn evpn in a vrf, we need to update the config file. Signed-off-by: Mitesh Kanjariya --- bgpd/bgp_evpn_vty.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c index db865adf11..f65ea5a698 100644 --- a/bgpd/bgp_evpn_vty.c +++ b/bgpd/bgp_evpn_vty.c @@ -4376,6 +4376,8 @@ DEFUN (no_bgp_evpn_vni_rt_without_val, void bgp_config_write_evpn_info(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi) { + char buf1[RD_ADDRSTRLEN]; + if (bgp->vnihash) hash_iterate(bgp->vnihash, (void (*)(struct hash_backet *, @@ -4394,6 +4396,10 @@ void bgp_config_write_evpn_info(struct vty *vty, struct bgp *bgp, afi_t afi, if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_ADVERTISE_IPV6_IN_EVPN)) vty_out(vty, " advertise ipv6 unicast\n"); + if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_RD_CFGD)) + vty_out(vty, " rd %s\n", + prefix_rd2str(&bgp->vrf_prd, buf1, sizeof(buf1))); + /* import route-target */ if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_IMPORT_RT_CFGD)) { char *ecom_str; From bc4606bd1ac5c34dc1f735898e0ad37a862e2892 Mon Sep 17 00:00:00 2001 From: Mitesh Kanjariya Date: Fri, 23 Feb 2018 01:16:32 -0800 Subject: [PATCH 52/98] bgpd: keep a backpointer to vrf instance in struct bgpevpn We will keep a backpointer to bgp vrf instance in bgpevpn. struct bgpevpn denotes a l2vni and bgp_vrf corresponds to l3vni. A back pointer to the vrf will provide efficient access to vrf when needed. Signed-off-by: Mitesh Kanjariya --- bgpd/bgp_evpn_private.h | 50 +++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 27 deletions(-) diff --git a/bgpd/bgp_evpn_private.h b/bgpd/bgp_evpn_private.h index cc0ec82344..54e9b014cf 100644 --- a/bgpd/bgp_evpn_private.h +++ b/bgpd/bgp_evpn_private.h @@ -62,6 +62,8 @@ struct bgpevpn { #define VNI_FLAG_IMPRT_CFGD 0x8 /* Import RT is user configured */ #define VNI_FLAG_EXPRT_CFGD 0x10 /* Export RT is user configured */ + struct bgp *bgp_vrf; /* back pointer to the vrf instance */ + /* Flag to indicate if we are advertising the g/w mac ip for this VNI*/ u_int8_t advertise_gw_macip; @@ -132,65 +134,59 @@ static inline int bgp_evpn_vrf_rd_matches_existing(struct bgp *bgp_vrf, static inline vni_t bgpevpn_get_l3vni(struct bgpevpn *vpn) { - struct bgp *bgp_vrf = NULL; - - bgp_vrf = bgp_lookup_by_vrf_id(vpn->tenant_vrf_id); - if (!bgp_vrf) - return 0; - - return bgp_vrf->l3vni; + return vpn->bgp_vrf ? vpn->bgp_vrf->l3vni : 0; } static inline void bgpevpn_get_rmac(struct bgpevpn *vpn, struct ethaddr *rmac) { - struct bgp *bgp_vrf = NULL; - memset(rmac, 0, sizeof(struct ethaddr)); - bgp_vrf = bgp_lookup_by_vrf_id(vpn->tenant_vrf_id); - if (!bgp_vrf) + if (!vpn->bgp_vrf) return; - memcpy(rmac, &bgp_vrf->rmac, sizeof(struct ethaddr)); + memcpy(rmac, &vpn->bgp_vrf->rmac, sizeof(struct ethaddr)); } static inline struct list *bgpevpn_get_vrf_export_rtl(struct bgpevpn *vpn) { - struct bgp *bgp_vrf = NULL; - - bgp_vrf = bgp_lookup_by_vrf_id(vpn->tenant_vrf_id); - if (!bgp_vrf) + if (!vpn->bgp_vrf) return NULL; - return bgp_vrf->vrf_export_rtl; + return vpn->bgp_vrf->vrf_export_rtl; } static inline struct list *bgpevpn_get_vrf_import_rtl(struct bgpevpn *vpn) { - struct bgp *bgp_vrf = NULL; - - bgp_vrf = bgp_lookup_by_vrf_id(vpn->tenant_vrf_id); - if (!bgp_vrf) + if (!vpn->bgp_vrf) return NULL; - return bgp_vrf->vrf_import_rtl; + return vpn->bgp_vrf->vrf_import_rtl; } static inline void bgpevpn_unlink_from_l3vni(struct bgpevpn *vpn) { - struct bgp *bgp_vrf = NULL; - - bgp_vrf = bgp_lookup_by_vrf_id(vpn->tenant_vrf_id); - if (!bgp_vrf || !bgp_vrf->l2vnis) + /* bail if vpn is not associated to bgp_vrf */ + if (!vpn->bgp_vrf) return; - listnode_delete(bgp_vrf->l2vnis, vpn); + + listnode_delete(vpn->bgp_vrf->l2vnis, vpn); + + /* remove the backpointer to the vrf instance */ + vpn->bgp_vrf = NULL; } static inline void bgpevpn_link_to_l3vni(struct bgpevpn *vpn) { struct bgp *bgp_vrf = NULL; + /* bail if vpn is already associated to vrf */ + if (vpn->bgp_vrf) + return; + bgp_vrf = bgp_lookup_by_vrf_id(vpn->tenant_vrf_id); if (!bgp_vrf || !bgp_vrf->l2vnis) return; + + /* associate the vpn to the bgp_vrf instance */ + vpn->bgp_vrf = bgp_vrf; listnode_add_sort(bgp_vrf->l2vnis, vpn); } From be41eb6823defd48d7e84c63fffb3e618b67ab36 Mon Sep 17 00:00:00 2001 From: Mitesh Kanjariya Date: Fri, 23 Feb 2018 02:16:47 -0800 Subject: [PATCH 53/98] bgpd: Attach PMSI to only type-3 routes and rmac to only type-2 routes The PMSI attribute is only applicable to EVPN type-3 route. Rmac is applicable to type-2 and type-5 routes. We should attach these attributes appropiately based on route-type. Signed-off-by: Mitesh Kanjariya --- bgpd/bgp_evpn.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index ec8e2907a6..9d32e0c375 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -1254,8 +1254,14 @@ static int update_evpn_route(struct bgp *bgp, struct bgpevpn *vpn, attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4; attr.sticky = CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_STICKY) ? 1 : 0; attr.default_gw = CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_GW) ? 1 : 0; - attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_PMSI_TUNNEL); - bgpevpn_get_rmac(vpn, &attr.rmac); + + /* PMSI is only needed for type-3 routes */ + if (p->prefix.route_type == BGP_EVPN_IMET_ROUTE) + attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_PMSI_TUNNEL); + + /* router mac is only needed for type-2 and type-5 routes */ + if (p->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) + bgpevpn_get_rmac(vpn, &attr.rmac); vni2label(vpn->vni, &(attr.label)); /* Set up RT and ENCAP extended community. */ From 3f230b347b6485962620c379b918910d33639838 Mon Sep 17 00:00:00 2001 From: mitesh Date: Sun, 25 Feb 2018 02:15:50 -0800 Subject: [PATCH 54/98] bgpd: remove show bgp l2vpn evpn route vrf cmd This command doesnt make sense as EVPN routes are only present in global table. Signed-off-by: Mitesh Kanjariya --- bgpd/bgp_evpn_vty.c | 75 --------------------------------------------- 1 file changed, 75 deletions(-) diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c index f65ea5a698..8001e5bd91 100644 --- a/bgpd/bgp_evpn_vty.c +++ b/bgpd/bgp_evpn_vty.c @@ -491,53 +491,6 @@ static void display_vni(struct vty *vty, struct bgpevpn *vpn, json_object *json) json_object_object_add(json, "exportRts", json_export_rtl); } -static void evpn_show_vrf_routes(struct vty *vty, - struct bgp *bgp_vrf) -{ - struct bgp *bgp_def = NULL; - struct bgp_node *rn; - struct bgp_info *ri; - int header = 1; - u_int32_t prefix_cnt, path_cnt; - struct bgp_table *table; - - prefix_cnt = path_cnt = 0; - bgp_def = bgp_get_default(); - if (!bgp_def) - return; - - table = (struct bgp_table *)bgp_vrf->rib[AFI_L2VPN][SAFI_EVPN]; - for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) { - char prefix_str[BUFSIZ]; - - bgp_evpn_route2str((struct prefix_evpn *)&rn->p, prefix_str, - sizeof(prefix_str)); - - if (rn->info) { - /* Overall header/legend displayed once. */ - if (header) { - bgp_evpn_show_route_header(vty, bgp_def, NULL); - header = 0; - } - prefix_cnt++; - } - - /* For EVPN, the prefix is displayed for each path (to fit in - * with code that already exists). - */ - for (ri = rn->info; ri; ri = ri->next) { - route_vty_out(vty, &rn->p, ri, 0, SAFI_EVPN, NULL); - path_cnt++; - } - } - - if (prefix_cnt == 0) - vty_out(vty, "No EVPN prefixes exist for this VRF"); - else - vty_out(vty, "\nDisplayed %u prefixes (%u paths)", - prefix_cnt, path_cnt); -} - static void show_vni_routes(struct bgp *bgp, struct bgpevpn *vpn, int type, struct vty *vty, struct in_addr vtep_ip, json_object *json) @@ -3190,33 +3143,6 @@ DEFUN(show_bgp_l2vpn_evpn_route_rd_macip, return CMD_SUCCESS; } -/* - * Display per-VRF EVPN routing table. - */ -DEFUN(show_bgp_l2vpn_evpn_route_vrf, show_bgp_l2vpn_evpn_route_vrf_cmd, - "show bgp l2vpn evpn route vrf VRFNAME", - SHOW_STR - BGP_STR - L2VPN_HELP_STR - EVPN_HELP_STR - "EVPN route information\n" - "VRF\n" - "VRF Name\n") -{ - int vrf_idx = 6; - char *vrf_name = NULL; - struct bgp *bgp_vrf = NULL; - - vrf_name = argv[vrf_idx]->arg; - bgp_vrf = bgp_lookup_by_name(vrf_name); - if (!bgp_vrf) - return CMD_WARNING; - - evpn_show_vrf_routes(vty, bgp_vrf); - - return CMD_SUCCESS; -} - /* * Display per-VNI EVPN routing table. */ @@ -4468,7 +4394,6 @@ void bgp_ethernetvpn_init(void) install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_rd_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_rd_macip_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_vni_cmd); - install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_vrf_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_vni_multicast_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_vni_macip_cmd); From 78dd30b263948ffb1d5c22592ef2d70b331bf071 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Mon, 22 Jan 2018 09:42:53 +0100 Subject: [PATCH 55/98] zebra: add a runtime flag to enable vrf with netns The netns backend is chosen by VRF if a runtime flag named vrfwnetns is selected when running zebra. In the case the NETNS backend is chosen, in some case the VRFID value is being assigned the value of the NSID. Within the perimeter of that work, this is why the vrf_lookup_by_table function is extended with a new parameter. Signed-off-by: Philippe Guibert --- lib/ns.c | 6 ++++-- lib/vrf.c | 17 +++++++++++++++++ lib/vrf.h | 6 ++++++ zebra/if_netlink.c | 12 ++++++++---- zebra/main.c | 9 ++++++++- zebra/rt_netlink.c | 32 ++++++++++++++++++++------------ zebra/zebra_vrf.c | 3 ++- 7 files changed, 65 insertions(+), 20 deletions(-) diff --git a/lib/ns.c b/lib/ns.c index 0b2a3bec78..e3a1d9d0dc 100644 --- a/lib/ns.c +++ b/lib/ns.c @@ -378,7 +378,9 @@ static int ns_config_write(struct vty *vty) struct ns *ns; int write = 0; - RB_FOREACH (ns, ns_head, &ns_tree) { + if (vrf_is_backend_netns()) + return 0; + RB_FOREACH(ns, ns_head, &ns_tree) { if (ns->ns_id == NS_DEFAULT || ns->name == NULL) continue; @@ -411,7 +413,7 @@ void ns_init(void) exit(1); } - if (have_netns()) { + if (have_netns() && !vrf_is_backend_netns()) { /* Install NS commands. */ install_node(&ns_node, ns_config_write); install_element(CONFIG_NODE, &ns_netns_cmd); diff --git a/lib/vrf.c b/lib/vrf.c index 02946df2bc..c300a87a36 100644 --- a/lib/vrf.c +++ b/lib/vrf.c @@ -44,6 +44,8 @@ RB_GENERATE(vrf_name_head, vrf, name_entry, vrf_name_compare); struct vrf_id_head vrfs_by_id = RB_INITIALIZER(&vrfs_by_id); struct vrf_name_head vrfs_by_name = RB_INITIALIZER(&vrfs_by_name); +static int vrf_backend; + /* * Turn on/off debug code * for vrf. @@ -446,6 +448,21 @@ int vrf_socket(int domain, int type, int protocol, vrf_id_t vrf_id) return ret; } +int vrf_is_backend_netns(void) +{ + return (vrf_backend == VRF_BACKEND_NETNS); +} + +int vrf_get_backend(void) +{ + return vrf_backend; +} + +void vrf_configure_backend(int vrf_backend_netns) +{ + vrf_backend = vrf_backend_netns; +} + /* vrf CLI commands */ DEFUN_NOSH (vrf, vrf_cmd, diff --git a/lib/vrf.h b/lib/vrf.h index 99c048c702..f1dc450194 100644 --- a/lib/vrf.h +++ b/lib/vrf.h @@ -96,6 +96,9 @@ RB_HEAD(vrf_name_head, vrf); RB_PROTOTYPE(vrf_name_head, vrf, name_entry, vrf_name_compare) DECLARE_QOBJ_TYPE(vrf) +/* Allow VRF with netns as backend */ +#define VRF_BACKEND_VRF_LITE 0 +#define VRF_BACKEND_NETNS 1 extern struct vrf_id_head vrfs_by_id; extern struct vrf_name_head vrfs_by_name; @@ -203,6 +206,9 @@ extern void vrf_cmd_init(int (*writefunc)(struct vty *vty)); /* Create a socket serving for the given VRF */ extern int vrf_socket(int, int, int, vrf_id_t); +extern void vrf_configure_backend(int vrf_backend_netns); +extern int vrf_get_backend(void); +extern int vrf_is_backend_netns(void); /* * VRF Debugging diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c index 14905b738b..ef30c7830f 100644 --- a/zebra/if_netlink.c +++ b/zebra/if_netlink.c @@ -615,13 +615,14 @@ static int netlink_interface(struct sockaddr_nl *snl, struct nlmsghdr *h, } /* If VRF, create the VRF structure itself. */ - if (zif_type == ZEBRA_IF_VRF) { + if (zif_type == ZEBRA_IF_VRF && !vrf_is_backend_netns()) { netlink_vrf_change(h, tb[IFLA_LINKINFO], name); vrf_id = (vrf_id_t)ifi->ifi_index; } if (tb[IFLA_MASTER]) { - if (slave_kind && (strcmp(slave_kind, "vrf") == 0)) { + if (slave_kind && (strcmp(slave_kind, "vrf") == 0) + && !vrf_is_backend_netns()) { zif_slave_type = ZEBRA_IF_SLAVE_VRF; vrf_id = *(u_int32_t *)RTA_DATA(tb[IFLA_MASTER]); } else if (slave_kind && (strcmp(slave_kind, "bridge") == 0)) { @@ -631,6 +632,8 @@ static int netlink_interface(struct sockaddr_nl *snl, struct nlmsghdr *h, } else zif_slave_type = ZEBRA_IF_SLAVE_OTHER; } + if (vrf_is_backend_netns()) + vrf_id = (vrf_id_t)ns_id; /* If linking to another interface, note it. */ if (tb[IFLA_LINK]) @@ -1074,7 +1077,7 @@ int netlink_link_change(struct sockaddr_nl *snl, struct nlmsghdr *h, } /* If VRF, create or update the VRF structure itself. */ - if (zif_type == ZEBRA_IF_VRF) { + if (zif_type == ZEBRA_IF_VRF && !vrf_is_backend_netns()) { netlink_vrf_change(h, tb[IFLA_LINKINFO], name); vrf_id = (vrf_id_t)ifi->ifi_index; } @@ -1091,7 +1094,8 @@ int netlink_link_change(struct sockaddr_nl *snl, struct nlmsghdr *h, if (h->nlmsg_type == RTM_NEWLINK) { if (tb[IFLA_MASTER]) { - if (slave_kind && (strcmp(slave_kind, "vrf") == 0)) { + if (slave_kind && (strcmp(slave_kind, "vrf") == 0) + && !vrf_is_backend_netns()) { zif_slave_type = ZEBRA_IF_SLAVE_VRF; vrf_id = *(u_int32_t *)RTA_DATA(tb[IFLA_MASTER]); diff --git a/zebra/main.c b/zebra/main.c index 19b16936d9..a881fcb9c6 100644 --- a/zebra/main.c +++ b/zebra/main.c @@ -85,6 +85,7 @@ struct option longopts[] = {{"batch", no_argument, NULL, 'b'}, {"label_socket", no_argument, NULL, 'l'}, {"retain", no_argument, NULL, 'r'}, #ifdef HAVE_NETLINK + {"vrfwnetns", no_argument, NULL, 'n'}, {"nl-bufsize", required_argument, NULL, 's'}, #endif /* HAVE_NETLINK */ {0}}; @@ -205,12 +206,14 @@ int main(int argc, char **argv) char *fuzzing = NULL; #endif + vrf_configure_backend(VRF_BACKEND_VRF_LITE); + frr_preinit(&zebra_di, argc, argv); frr_opt_add( "bakz:e:l:r" #ifdef HAVE_NETLINK - "s:" + "s:n" #endif #if defined(HANDLE_ZAPI_FUZZING) "c:" @@ -225,6 +228,7 @@ int main(int argc, char **argv) " -k, --keep_kernel Don't delete old routes which installed by zebra.\n" " -r, --retain When program terminates, retain added route by zebra.\n" #ifdef HAVE_NETLINK + " -n, --vrfwnetns Set VRF with NetNS\n" " -s, --nl-bufsize Set netlink receive buffer size\n" #endif /* HAVE_NETLINK */ #if defined(HANDLE_ZAPI_FUZZING) @@ -279,6 +283,9 @@ int main(int argc, char **argv) case 's': nl_rcvbufsize = atoi(optarg); break; + case 'n': + vrf_configure_backend(VRF_BACKEND_NETNS); + break; #endif /* HAVE_NETLINK */ #if defined(HANDLE_ZAPI_FUZZING) case 'c': diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index a80ab9d834..20abd76973 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -194,16 +194,25 @@ static inline int proto2zebra(int proto, int family) /* Pending: create an efficient table_id (in a tree/hash) based lookup) */ -static vrf_id_t vrf_lookup_by_table(u_int32_t table_id) +static vrf_id_t vrf_lookup_by_table(u_int32_t table_id, ns_id_t ns_id) { struct vrf *vrf; struct zebra_vrf *zvrf; RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id) { - if ((zvrf = vrf->info) == NULL || (zvrf->table_id != table_id)) + zvrf = vrf->info; + if (zvrf == NULL) continue; - - return zvrf_id(zvrf); + /* case vrf with netns : match the netnsid */ + if (vrf_is_backend_netns()) { + if (ns_id == zvrf_id(zvrf)) + return zvrf_id(zvrf); + } else { + /* VRF is VRF_BACKEND_VRF_LITE */ + if (zvrf->table_id != table_id) + continue; + return zvrf_id(zvrf); + } } return VRF_DEFAULT; @@ -220,7 +229,7 @@ static int netlink_route_change_read_unicast(struct sockaddr_nl *snl, u_char flags = 0; struct prefix p; struct prefix_ipv6 src_p = {}; - vrf_id_t vrf_id = VRF_DEFAULT; + vrf_id_t vrf_id; char anyaddr[16] = {0}; @@ -288,7 +297,7 @@ static int netlink_route_change_read_unicast(struct sockaddr_nl *snl, table = rtm->rtm_table; /* Map to VRF */ - vrf_id = vrf_lookup_by_table(table); + vrf_id = vrf_lookup_by_table(table, ns_id); if (vrf_id == VRF_DEFAULT) { if (!is_zebra_valid_kernel_table(table) && !is_zebra_main_routing_table(table)) @@ -609,7 +618,7 @@ static int netlink_route_change_read_multicast(struct sockaddr_nl *snl, char sbuf[40]; char gbuf[40]; char oif_list[256] = "\0"; - vrf_id_t vrf = ns_id; + vrf_id_t vrf; int table; if (mroute) @@ -631,7 +640,7 @@ static int netlink_route_change_read_multicast(struct sockaddr_nl *snl, else table = rtm->rtm_table; - vrf = vrf_lookup_by_table(table); + vrf = vrf_lookup_by_table(table, ns_id); if (tb[RTA_IIF]) iif = *(int *)RTA_DATA(tb[RTA_IIF]); @@ -687,24 +696,23 @@ int netlink_route_change(struct sockaddr_nl *snl, struct nlmsghdr *h, ns_id_t ns_id, int startup) { int len; - vrf_id_t vrf_id = ns_id; struct rtmsg *rtm; rtm = NLMSG_DATA(h); if (!(h->nlmsg_type == RTM_NEWROUTE || h->nlmsg_type == RTM_DELROUTE)) { /* If this is not route add/delete message print warning. */ - zlog_warn("Kernel message: %d vrf %u\n", h->nlmsg_type, vrf_id); + zlog_warn("Kernel message: %d NS %u\n", h->nlmsg_type, ns_id); return 0; } /* Connected route. */ if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("%s %s %s proto %s vrf %u", + zlog_debug("%s %s %s proto %s NS %u", nl_msg_type_to_str(h->nlmsg_type), nl_family_to_str(rtm->rtm_family), nl_rttype_to_str(rtm->rtm_type), - nl_rtproto_to_str(rtm->rtm_protocol), vrf_id); + nl_rtproto_to_str(rtm->rtm_protocol), ns_id); /* We don't care about change notifications for the MPLS table. */ /* TODO: Revisit this. */ diff --git a/zebra/zebra_vrf.c b/zebra/zebra_vrf.c index cd47f21278..6eec2c18c4 100644 --- a/zebra/zebra_vrf.c +++ b/zebra/zebra_vrf.c @@ -25,8 +25,9 @@ #include "command.h" #include "memory.h" #include "srcdest_table.h" - +#include "vrf.h" #include "vty.h" + #include "zebra/debug.h" #include "zebra/zserv.h" #include "zebra/rib.h" From b95c18833a36bcf03b7a72c110be24873a65420d Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Wed, 6 Dec 2017 12:03:59 +0100 Subject: [PATCH 56/98] zebra: copy logical-router-command under vrf subnode a vty command is added: in addition to this command ( kept for future usage): - [no] logical-router-id netns a new command is being placed under vrf subnode - vrf [no] netns exit This command permits to map a VRF with a Netnamespace. The commit only handles the relationship between vrf and ns structures. It adds 2 attributes to vrf structure: - one defines the kind of vrf ( mapped under netns or vrf from kernel) - the other is the opaque pointer to ns The show running-config is handled by zebra daemon. Signed-off-by: Philippe Guibert --- lib/ns.c | 184 ++++++++++++++++++++++++++++++++++++++-------- lib/ns.h | 4 + lib/vrf.c | 2 + lib/vrf.h | 3 + zebra/zebra_ns.c | 8 ++ zebra/zebra_ns.h | 1 + zebra/zebra_vrf.c | 1 + 7 files changed, 173 insertions(+), 30 deletions(-) diff --git a/lib/ns.c b/lib/ns.c index e3a1d9d0dc..25136d0a1e 100644 --- a/lib/ns.c +++ b/lib/ns.c @@ -35,12 +35,14 @@ #include "command.h" #include "vty.h" +#include "vrf.h" -DEFINE_MTYPE_STATIC(LIB, NS, "Logical-Router") -DEFINE_MTYPE_STATIC(LIB, NS_NAME, "Logical-Router Name") +DEFINE_MTYPE_STATIC(LIB, NS, "NetNS Context") +DEFINE_MTYPE_STATIC(LIB, NS_NAME, "NetNS Name") static __inline int ns_compare(const struct ns *, const struct ns *); static struct ns *ns_lookup(ns_id_t); +static struct ns *ns_lookup_name(const char *); RB_GENERATE(ns_head, ns, entry, ns_compare) @@ -105,12 +107,31 @@ struct ns_master { static int ns_is_enabled(struct ns *ns); static int ns_enable(struct ns *ns); static void ns_disable(struct ns *ns); +static void ns_get_created(struct ns *ns); static __inline int ns_compare(const struct ns *a, const struct ns *b) { return (a->ns_id - b->ns_id); } +static void ns_get_created(struct ns *ns) +{ + /* + * Initialize interfaces. + * + * I'm not sure if this belongs here or in + * the vrf code. + */ + // if_init (&ns->iflist); + + if (ns->ns_id != NS_UNKNOWN) + zlog_info("NS %u is created.", ns->ns_id); + else + zlog_info("NS %s is created.", ns->name); + if (ns_master.ns_new_hook) + (*ns_master.ns_new_hook)(ns->ns_id, &ns->info); +} + /* Get a NS. If not found, create one. */ static struct ns *ns_get(ns_id_t ns_id) { @@ -124,20 +145,27 @@ static struct ns *ns_get(ns_id_t ns_id) ns->ns_id = ns_id; ns->fd = -1; RB_INSERT(ns_head, &ns_tree, ns); + ns_get_created(ns); + return ns; +} - /* - * Initialize interfaces. - * - * I'm not sure if this belongs here or in - * the vrf code. - */ - // if_init (&ns->iflist); +/* Get a NS. If not found, create one. */ +static struct ns *ns_get_by_name(char *ns_name) +{ + struct ns *ns; - zlog_info("NS %u is created.", ns_id); + ns = ns_lookup_name(ns_name); + if (ns) + return (ns); - if (ns_master.ns_new_hook) - (*ns_master.ns_new_hook)(ns_id, &ns->info); + ns = XCALLOC(MTYPE_NS, sizeof(struct ns)); + ns->ns_id = NS_UNKNOWN; + ns->name = XSTRDUP(MTYPE_NS_NAME, ns_name); + ns->fd = -1; + RB_INSERT(ns_head, &ns_tree, ns); + /* ns_id not initialised */ + ns_get_created(ns); return ns; } @@ -172,6 +200,20 @@ static struct ns *ns_lookup(ns_id_t ns_id) return (RB_FIND(ns_head, &ns_tree, &ns)); } +/* Look up a NS by name */ +static struct ns *ns_lookup_name(const char *name) +{ + struct ns *ns = NULL; + + RB_FOREACH(ns, ns_head, &ns_tree) { + if (ns->name != NULL) { + if (strcmp(name, ns->name) == 0) + return ns; + } + } + return NULL; +} + /* * Check whether the NS is enabled - that is, whether the NS * is ready to allocate resources. Currently there's only one @@ -289,8 +331,8 @@ static char *ns_netns_pathname(struct vty *vty, const char *name) return pathname; } -DEFUN_NOSH (ns_netns, - ns_netns_cmd, +DEFUN_NOSH (ns_logicalrouter, + ns_logicalrouter_cmd, "logical-router (1-65535) ns NAME", "Enable a logical-router\n" "Specify the logical-router indentifier\n" @@ -299,7 +341,7 @@ DEFUN_NOSH (ns_netns, { int idx_number = 1; int idx_name = 3; - ns_id_t ns_id = NS_DEFAULT; + ns_id_t ns_id; struct ns *ns = NULL; char *pathname = ns_netns_pathname(vty, argv[idx_name]->arg); @@ -327,8 +369,11 @@ DEFUN_NOSH (ns_netns, return CMD_SUCCESS; } -DEFUN (no_ns_netns, - no_ns_netns_cmd, +static struct cmd_node logicalrouter_node = {NS_NODE, "", /* NS node has no interface. */ + 1}; + +DEFUN (no_ns_logicalrouter, + no_ns_logicalrouter_cmd, "no logical-router (1-65535) ns NAME", NO_STR "Enable a Logical-Router\n" @@ -338,7 +383,7 @@ DEFUN (no_ns_netns, { int idx_number = 2; int idx_name = 4; - ns_id_t ns_id = NS_DEFAULT; + ns_id_t ns_id; struct ns *ns = NULL; char *pathname = ns_netns_pathname(vty, argv[idx_name]->arg); @@ -368,30 +413,99 @@ DEFUN (no_ns_netns, return CMD_SUCCESS; } -/* NS node. */ -static struct cmd_node ns_node = {NS_NODE, "", /* NS node has no interface. */ - 1}; +DEFUN_NOSH (ns_netns, + ns_netns_cmd, + "netns NAME", + "Attach VRF to a Namespace\n" + "The file name in " NS_RUN_DIR ", or a full pathname\n") +{ + int idx_name = 1; + struct ns *ns = NULL; + char *pathname = ns_netns_pathname(vty, argv[idx_name]->arg); -/* NS configuration write function. */ -static int ns_config_write(struct vty *vty) + VTY_DECLVAR_CONTEXT(vrf, vrf); + + if (!pathname) + return CMD_WARNING_CONFIG_FAILED; + + if (!vrf) + return CMD_WARNING_CONFIG_FAILED; + if (vrf->vrf_id != VRF_UNKNOWN && vrf->ns_ctxt == NULL) { + vty_out(vty, "VRF %u is already configured with VRF %s\n", + vrf->vrf_id, vrf->name); + return CMD_WARNING_CONFIG_FAILED; + } + if (vrf->ns_ctxt != NULL) { + ns = (struct ns *) vrf->ns_ctxt; + if (ns && 0 != strcmp(ns->name, pathname)) { + vty_out(vty, "VRF %u is already configured" + " with NETNS %s\n", + vrf->vrf_id, ns->name); + return CMD_WARNING_CONFIG_FAILED; + } + } + ns = ns_lookup_name(pathname); + if (ns && ns->vrf_ctxt) { + struct vrf *vrf2 = (struct vrf *)ns->vrf_ctxt; + + vty_out(vty, "NS %s is already configured" + " with VRF %u(%s)\n", + ns->name, vrf2->vrf_id, vrf2->name); + return CMD_WARNING_CONFIG_FAILED; + } else if (!ns) + ns = ns_get_by_name(pathname); + + if (!ns_enable(ns)) { + vty_out(vty, "Can not associate NS %u with NETNS %s\n", + ns->ns_id, ns->name); + return CMD_WARNING_CONFIG_FAILED; + } + + vrf->ns_ctxt = (void *)ns; + ns->vrf_ctxt = (void *)vrf; + return CMD_SUCCESS; +} + +static int ns_logicalrouter_config_write(struct vty *vty) { struct ns *ns; int write = 0; - if (vrf_is_backend_netns()) - return 0; RB_FOREACH(ns, ns_head, &ns_tree) { if (ns->ns_id == NS_DEFAULT || ns->name == NULL) continue; - vty_out(vty, "logical-router %u netns %s\n", ns->ns_id, ns->name); write = 1; } - return write; } +DEFUN (no_ns_netns, + no_ns_netns_cmd, + "no netns [NAME]", + NO_STR + "Detach VRF from a Namespace\n" + "The file name in " NS_RUN_DIR ", or a full pathname\n") +{ + struct ns *ns = NULL; + + VTY_DECLVAR_CONTEXT(vrf, vrf); + + if (!vrf->ns_ctxt) { + vty_out(vty, "VRF %s(%u) is not configured with NetNS\n", + vrf->name, vrf->vrf_id); + return CMD_WARNING_CONFIG_FAILED; + } + + ns = (struct ns *)vrf->ns_ctxt; + + ns->vrf_ctxt = NULL; + ns_delete(ns); + vrf->ns_ctxt = NULL; + return CMD_SUCCESS; +} + /* Initialize NS module. */ void ns_init(void) { @@ -415,9 +529,19 @@ void ns_init(void) if (have_netns() && !vrf_is_backend_netns()) { /* Install NS commands. */ - install_node(&ns_node, ns_config_write); - install_element(CONFIG_NODE, &ns_netns_cmd); - install_element(CONFIG_NODE, &no_ns_netns_cmd); + install_node(&logicalrouter_node, + ns_logicalrouter_config_write); + install_element(CONFIG_NODE, &ns_logicalrouter_cmd); + install_element(CONFIG_NODE, &no_ns_logicalrouter_cmd); + } +} + +void ns_cmd_init(void) +{ + if (have_netns()) { + /* Install NS commands. */ + install_element(VRF_NODE, &ns_netns_cmd); + install_element(VRF_NODE, &no_ns_netns_cmd); } } diff --git a/lib/ns.h b/lib/ns.h index 79b4cab04d..fab3e19368 100644 --- a/lib/ns.h +++ b/lib/ns.h @@ -49,6 +49,9 @@ struct ns { /* Master list of interfaces belonging to this NS */ struct list *iflist; + /* Back Pointer to VRF */ + void *vrf_ctxt; + /* User data */ void *info; }; @@ -89,5 +92,6 @@ extern void ns_terminate(void); /* Create a socket serving for the given NS */ extern int ns_socket(int, int, int, ns_id_t); +extern void ns_cmd_init(void); #endif /*_ZEBRA_NS_H*/ diff --git a/lib/vrf.c b/lib/vrf.c index c300a87a36..56c8bdbabe 100644 --- a/lib/vrf.c +++ b/lib/vrf.c @@ -29,6 +29,7 @@ #include "log.h" #include "memory.h" #include "command.h" +#include "ns.h" DEFINE_MTYPE_STATIC(LIB, VRF, "VRF") DEFINE_MTYPE_STATIC(LIB, VRF_BITMAP, "VRF bit-map") @@ -574,4 +575,5 @@ void vrf_cmd_init(int (*writefunc)(struct vty *vty)) install_element(CONFIG_NODE, &no_vrf_cmd); install_node(&vrf_node, writefunc); install_default(VRF_NODE); + ns_cmd_init(); } diff --git a/lib/vrf.h b/lib/vrf.h index f1dc450194..40e6ab6cd6 100644 --- a/lib/vrf.h +++ b/lib/vrf.h @@ -88,6 +88,9 @@ struct vrf { /* The table_id from the kernel */ struct vrf_data data; + /* Back pointer to namespace context */ + void *ns_ctxt; + QOBJ_FIELDS }; RB_HEAD(vrf_id_head, vrf); diff --git a/zebra/zebra_ns.c b/zebra/zebra_ns.c index 1715881f7e..80847518a7 100644 --- a/zebra/zebra_ns.c +++ b/zebra/zebra_ns.c @@ -1,6 +1,7 @@ /* zebra NS Routines * Copyright (C) 2016 Cumulus Networks, Inc. * Donald Sharp + * Copyright (C) 2017/2018 6WIND * * This file is part of Quagga. * @@ -177,3 +178,10 @@ int zebra_ns_init(void) return 0; } + +int zebra_ns_config_write(struct vty *vty, struct ns *ns) +{ + if (ns && ns->name != NULL) + vty_out(vty, " netns %s\n", ns->name); + return 0; +} diff --git a/zebra/zebra_ns.h b/zebra/zebra_ns.h index 765f2c6893..99e4984164 100644 --- a/zebra/zebra_ns.h +++ b/zebra/zebra_ns.h @@ -82,4 +82,5 @@ extern struct route_table *zebra_ns_find_table(struct zebra_ns *zns, extern struct route_table *zebra_ns_get_table(struct zebra_ns *zns, struct zebra_vrf *zvrf, uint32_t tableid, afi_t afi); +int zebra_ns_config_write(struct vty *vty, struct ns *ns); #endif diff --git a/zebra/zebra_vrf.c b/zebra/zebra_vrf.c index 6eec2c18c4..a3596a4263 100644 --- a/zebra/zebra_vrf.c +++ b/zebra/zebra_vrf.c @@ -563,6 +563,7 @@ static int vrf_config_write(struct vty *vty) zvrf->l3vni, is_l3vni_for_prefix_routes_only(zvrf->l3vni) ? " prefix-routes-only" :""); + zebra_ns_config_write(vty, (struct ns *)vrf->ns_ctxt); vty_out(vty, "!\n"); } From c17d483845c4b772270cade18f2ce5a5e14bc14e Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Wed, 10 Jan 2018 10:04:59 +0100 Subject: [PATCH 57/98] lib: netns vty command not available when vrf backend is vrf lite Using the vrf backend kind, the vty command that configured netns under vty will not be installed if the vrf backend is vrf lite Signed-off-by: Philippe Guibert --- lib/ns.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/ns.c b/lib/ns.c index 25136d0a1e..fcac28cf7e 100644 --- a/lib/ns.c +++ b/lib/ns.c @@ -492,6 +492,10 @@ DEFUN (no_ns_netns, VTY_DECLVAR_CONTEXT(vrf, vrf); + if (!vrf_is_backend_netns()) { + vty_out(vty, "VRF backend is not Netns. Aborting\n"); + return CMD_WARNING_CONFIG_FAILED; + } if (!vrf->ns_ctxt) { vty_out(vty, "VRF %s(%u) is not configured with NetNS\n", vrf->name, vrf->vrf_id); @@ -538,7 +542,7 @@ void ns_init(void) void ns_cmd_init(void) { - if (have_netns()) { + if (have_netns() && vrf_is_backend_netns()) { /* Install NS commands. */ install_element(VRF_NODE, &ns_netns_cmd); install_element(VRF_NODE, &no_ns_netns_cmd); From fe533c564e1901ee6b767708345abb52a56056af Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Fri, 8 Dec 2017 19:06:34 +0100 Subject: [PATCH 58/98] zebra: socket operations stick to namespace if necessary Upon following calls: interface poll, address poll, route poll, and ICMPv6 handling, each new Namespace is being parsed. For that, the socket operations need to switch from one NS to one other, to get the necessary information. As of now, there is a crash when dumping interfaces, through show running-config. Signed-off-by: Philippe Guibert --- lib/ns.c | 4 ++-- lib/vrf.c | 3 ++- zebra/if_netlink.c | 10 ++++++++-- zebra/interface.c | 7 ++++++- zebra/kernel_netlink.c | 2 +- zebra/kernel_socket.c | 3 ++- zebra/rtadv.c | 7 ++++--- 7 files changed, 25 insertions(+), 11 deletions(-) diff --git a/lib/ns.c b/lib/ns.c index fcac28cf7e..170290a9e9 100644 --- a/lib/ns.c +++ b/lib/ns.c @@ -565,9 +565,9 @@ void ns_terminate(void) int ns_socket(int domain, int type, int protocol, ns_id_t ns_id) { struct ns *ns = ns_lookup(ns_id); - int ret = -1; + int ret; - if (!ns_is_enabled(ns)) { + if (!ns || !ns_is_enabled(ns)) { errno = ENOSYS; return -1; } diff --git a/lib/vrf.c b/lib/vrf.c index 56c8bdbabe..81ff6d56fd 100644 --- a/lib/vrf.c +++ b/lib/vrf.c @@ -97,7 +97,8 @@ struct vrf *vrf_get(vrf_id_t vrf_id, const char *name) int new = 0; if (debug_vrf) - zlog_debug("VRF_GET: %s(%u)", name, vrf_id); + zlog_debug("VRF_GET: %s(%u)", + name == NULL ? "(NULL)" : name, vrf_id); /* Nothing to see, move along here */ if (!name && vrf_id == VRF_UNKNOWN) diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c index ef30c7830f..6897bd4ee2 100644 --- a/zebra/if_netlink.c +++ b/zebra/if_netlink.c @@ -792,8 +792,12 @@ static int netlink_address(int cmd, int family, struct interface *ifp, char buf[NL_PKT_BUF_SIZE]; } req; - struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT); + 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); @@ -1020,6 +1024,7 @@ int netlink_link_change(struct sockaddr_nl *snl, struct nlmsghdr *h, zns = zebra_ns_lookup(ns_id); ifi = NLMSG_DATA(h); + /* assume if not default zns, then new VRF */ if (!(h->nlmsg_type == RTM_NEWLINK || h->nlmsg_type == RTM_DELLINK)) { /* If this is not link add/delete message so print warning. */ zlog_warn("netlink_link_change: wrong kernel message %d", @@ -1107,7 +1112,8 @@ int netlink_link_change(struct sockaddr_nl *snl, struct nlmsghdr *h, } else zif_slave_type = ZEBRA_IF_SLAVE_OTHER; } - + if (vrf_is_backend_netns()) + vrf_id = (vrf_id_t)ns_id; if (ifp == NULL || !CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE)) { /* Add interface notification from kernel */ diff --git a/zebra/interface.c b/zebra/interface.c index 07570e64bf..6ee1db6a4c 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -512,8 +512,13 @@ static void if_addr_wakeup(struct interface *ifp) void if_add_update(struct interface *ifp) { struct zebra_if *if_data; + struct zebra_ns *zns; - if_link_per_ns(zebra_ns_lookup(NS_DEFAULT), ifp); + if (vrf_is_backend_netns()) + zns = zebra_ns_lookup((ns_id_t)ifp->vrf_id); + else + zns = zebra_ns_lookup(NS_DEFAULT); + if_link_per_ns(zns, ifp); if_data = ifp->info; assert(if_data); diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c index 1be2cbcaf5..0b3b6eed45 100644 --- a/zebra/kernel_netlink.c +++ b/zebra/kernel_netlink.c @@ -188,7 +188,7 @@ static int netlink_socket(struct nlsock *nl, unsigned long groups, return -1; } - sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + sock = ns_socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE, ns_id); if (sock < 0) { zlog_err("Can't open %s socket: %s", nl->name, safe_strerror(errno)); diff --git a/zebra/kernel_socket.c b/zebra/kernel_socket.c index 4d888d8069..3b28a9b242 100644 --- a/zebra/kernel_socket.c +++ b/zebra/kernel_socket.c @@ -1384,7 +1384,8 @@ static void routing_socket(struct zebra_ns *zns) if (zserv_privs.change(ZPRIVS_RAISE)) zlog_err("routing_socket: Can't raise privileges"); - routing_sock = socket(AF_ROUTE, SOCK_RAW, 0); + routing_sock = ns_socket(AF_ROUTE, SOCK_RAW, + 0, (ns_id_t)zns->ns->ns_id); if (routing_sock < 0) { if (zserv_privs.change(ZPRIVS_LOWER)) diff --git a/zebra/rtadv.c b/zebra/rtadv.c index 32418eb82f..860e8710d6 100644 --- a/zebra/rtadv.c +++ b/zebra/rtadv.c @@ -34,6 +34,7 @@ #include "command.h" #include "privs.h" #include "vrf.h" +#include "ns.h" #include "zebra/interface.h" #include "zebra/rtadv.h" @@ -621,7 +622,7 @@ static int rtadv_read(struct thread *thread) return 0; } -static int rtadv_make_socket(void) +static int rtadv_make_socket(ns_id_t ns_id) { int sock; int ret = 0; @@ -631,7 +632,7 @@ static int rtadv_make_socket(void) zlog_err("rtadv_make_socket: could not raise privs, %s", safe_strerror(errno)); - sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); + sock = ns_socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6, ns_id); if (zserv_privs.change(ZPRIVS_LOWER)) zlog_err("rtadv_make_socket: could not lower privs, %s", @@ -1686,7 +1687,7 @@ static void rtadv_event(struct zebra_ns *zns, enum rtadv_event event, int val) void rtadv_init(struct zebra_ns *zns) { - zns->rtadv.sock = rtadv_make_socket(); + zns->rtadv.sock = rtadv_make_socket(zns->ns_id); } void rtadv_terminate(struct zebra_ns *zns) From 9a76375f39f556188d25920fb53802c091bc4064 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Mon, 11 Dec 2017 15:19:15 +0100 Subject: [PATCH 59/98] zebra: route configuration fix for vrf when applied to namespaces For each route to be added or deleted, instead of applying directly to default namespaces, when a vrf is mapped to a namespace, then the correct zns must be found out. Signed-off-by: Philippe Guibert --- zebra/rt_netlink.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index 20abd76973..84c7b8e9cf 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -1334,9 +1334,13 @@ static int netlink_route_multipath(int cmd, struct prefix *p, char buf[NL_PKT_BUF_SIZE]; } req; - struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT); + struct zebra_ns *zns; struct zebra_vrf *zvrf = vrf_info_lookup(re->vrf_id); + if (!vrf_is_backend_netns()) + zns = zebra_ns_lookup(NS_DEFAULT); + else + zns = (struct zebra_ns *)ns_info_lookup(re->vrf_id); memset(&req, 0, sizeof req - NL_PKT_BUF_SIZE); bytelen = (family == AF_INET ? 4 : 16); From 5895d33f40fdf22f1b422154dc598bbba69e1796 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Tue, 19 Dec 2017 12:23:32 +0100 Subject: [PATCH 60/98] zebra: ipv6 operations stick to namespace All ipv6 operations stick to namespace. Signed-off-by: Philippe Guibert --- zebra/interface.c | 9 +++++++-- zebra/rt.h | 3 ++- zebra/rt_netlink.c | 48 ++++++++++++++++++++++++++++++---------------- zebra/rt_socket.c | 2 +- 4 files changed, 42 insertions(+), 20 deletions(-) diff --git a/zebra/interface.c b/zebra/interface.c index 6ee1db6a4c..74ffdee31f 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -805,21 +805,26 @@ void if_nbr_ipv6ll_to_ipv4ll_neigh_update(struct interface *ifp, char buf[16] = "169.254.0.1"; struct in_addr ipv4_ll; char mac[6]; + ns_id_t ns_id; inet_pton(AF_INET, buf, &ipv4_ll); ipv6_ll_address_to_mac(address, (u_char *)mac); + if (!vrf_is_backend_netns()) + ns_id = NS_DEFAULT; + else + ns_id = (ns_id_t)(ifp->vrf_id); /* * Remove existed arp record for the interface as netlink * protocol does not have update message types * * supported message types are RTM_NEWNEIGH and RTM_DELNEIGH */ - kernel_neigh_update (0, ifp->ifindex, ipv4_ll.s_addr, mac, 6); + kernel_neigh_update(0, ifp->ifindex, ipv4_ll.s_addr, mac, 6, ns_id); /* Add arp record */ - kernel_neigh_update (add, ifp->ifindex, ipv4_ll.s_addr, mac, 6); + kernel_neigh_update(add, ifp->ifindex, ipv4_ll.s_addr, mac, 6, ns_id); zvrf->neigh_updates++; } diff --git a/zebra/rt.h b/zebra/rt.h index 54d45b889a..472f2d7a97 100644 --- a/zebra/rt.h +++ b/zebra/rt.h @@ -78,7 +78,8 @@ 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 *); -extern int kernel_neigh_update(int, int, uint32_t, char *, int); +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, struct interface *slave); diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index 84c7b8e9cf..0371c6b99b 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -1282,7 +1282,7 @@ static void _netlink_mpls_debug(int cmd, u_int32_t label, const char *routedesc) } static int netlink_neigh_update(int cmd, int ifindex, uint32_t addr, char *lla, - int llalen) + int llalen, ns_id_t ns_id) { struct { struct nlmsghdr n; @@ -1290,7 +1290,7 @@ static int netlink_neigh_update(int cmd, int ifindex, uint32_t addr, char *lla, char buf[256]; } req; - struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT); + struct zebra_ns *zns = zebra_ns_lookup(ns_id); memset(&req.n, 0, sizeof(req.n)); memset(&req.ndm, 0, sizeof(req.ndm)); @@ -1638,8 +1638,12 @@ int kernel_get_ipmr_sg_stats(struct zebra_vrf *zvrf, void *in) } req; mroute = mr; - struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT); + struct zebra_ns *zns; + if (!vrf_is_backend_netns()) + zns = zebra_ns_lookup(NS_DEFAULT); + else + zns = (struct zebra_ns *)ns_info_lookup(zvrf->vrf->vrf_id); memset(&req.n, 0, sizeof(req.n)); memset(&req.ndm, 0, sizeof(req.ndm)); @@ -1712,10 +1716,10 @@ void kernel_route_rib(struct route_node *rn, struct prefix *p, } int kernel_neigh_update(int add, int ifindex, uint32_t addr, char *lla, - int llalen) + int llalen, ns_id_t ns_id) { return netlink_neigh_update(add ? RTM_NEWNEIGH : RTM_DELNEIGH, ifindex, - addr, lla, llalen); + addr, lla, llalen, ns_id); } /* @@ -1725,7 +1729,7 @@ int kernel_neigh_update(int add, int ifindex, uint32_t addr, char *lla, static int netlink_vxlan_flood_list_update(struct interface *ifp, struct in_addr *vtep_ip, int cmd) { - struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT); + struct zebra_ns *zns; struct { struct nlmsghdr n; struct ndmsg ndm; @@ -1733,6 +1737,10 @@ static int netlink_vxlan_flood_list_update(struct interface *ifp, } req; u_char dst_mac[6] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0}; + if (!vrf_is_backend_netns()) + zns = zebra_ns_lookup(NS_DEFAULT); + else + zns = (struct zebra_ns *)ns_info_lookup(ifp->vrf_id); memset(&req.n, 0, sizeof(req.n)); memset(&req.ndm, 0, sizeof(req.ndm)); @@ -1788,7 +1796,7 @@ int kernel_del_vtep(vni_t vni, struct interface *ifp, struct in_addr *vtep_ip) #endif static int netlink_macfdb_change(struct sockaddr_nl *snl, struct nlmsghdr *h, - int len) + int len, ns_id_t ns_id) { struct ndmsg *ndm; struct interface *ifp; @@ -1811,7 +1819,7 @@ static int netlink_macfdb_change(struct sockaddr_nl *snl, struct nlmsghdr *h, return 0; /* The interface should exist. */ - ifp = if_lookup_by_index_per_ns(zebra_ns_lookup(NS_DEFAULT), + ifp = if_lookup_by_index_per_ns(zebra_ns_lookup(ns_id), ndm->ndm_ifindex); if (!ifp || !ifp->info) return 0; @@ -1942,7 +1950,7 @@ static int netlink_macfdb_table(struct sockaddr_nl *snl, struct nlmsghdr *h, if (ndm->ndm_family != AF_BRIDGE) return 0; - return netlink_macfdb_change(snl, h, len); + return netlink_macfdb_change(snl, h, len, ns_id); } /* Request for MAC FDB information from the kernel */ @@ -2024,7 +2032,7 @@ static int netlink_macfdb_update(struct interface *ifp, vlanid_t vid, struct ethaddr *mac, struct in_addr vtep_ip, int local, int cmd, u_char sticky) { - struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT); + struct zebra_ns *zns; struct { struct nlmsghdr n; struct ndmsg ndm; @@ -2039,6 +2047,10 @@ static int netlink_macfdb_update(struct interface *ifp, vlanid_t vid, char vid_buf[20]; char dst_buf[30]; + if (!vrf_is_backend_netns()) + zns = zebra_ns_lookup(NS_DEFAULT); + else + zns = (struct zebra_ns *)ns_info_lookup(ifp->vrf_id); zif = ifp->info; if ((br_if = zif->brslave_info.br_if) == NULL) { zlog_warn("MAC %s on IF %s(%u) - no mapping to bridge", @@ -2098,7 +2110,7 @@ static int netlink_macfdb_update(struct interface *ifp, vlanid_t vid, | NUD_DELAY) static int netlink_ipneigh_change(struct sockaddr_nl *snl, struct nlmsghdr *h, - int len) + int len, ns_id_t ns_id) { struct ndmsg *ndm; struct interface *ifp; @@ -2119,7 +2131,7 @@ static int netlink_ipneigh_change(struct sockaddr_nl *snl, struct nlmsghdr *h, return 0; /* The interface should exist. */ - ifp = if_lookup_by_index_per_ns(zebra_ns_lookup(NS_DEFAULT), + ifp = if_lookup_by_index_per_ns(zebra_ns_lookup(ns_id), ndm->ndm_ifindex); if (!ifp || !ifp->info) return 0; @@ -2141,7 +2153,7 @@ static int netlink_ipneigh_change(struct sockaddr_nl *snl, struct nlmsghdr *h, * itself */ if (IS_ZEBRA_IF_VLAN(ifp)) { - link_if = if_lookup_by_index_per_ns(zebra_ns_lookup(NS_DEFAULT), + link_if = if_lookup_by_index_per_ns(zebra_ns_lookup(ns_id), zif->link_ifindex); if (!link_if) return 0; @@ -2319,13 +2331,13 @@ int netlink_neigh_change(struct sockaddr_nl *snl, struct nlmsghdr *h, /* Is this a notification for the MAC FDB or IP neighbor table? */ ndm = NLMSG_DATA(h); if (ndm->ndm_family == AF_BRIDGE) - return netlink_macfdb_change(snl, h, len); + return netlink_macfdb_change(snl, h, len, ns_id); if (ndm->ndm_type != RTN_UNICAST) return 0; if (ndm->ndm_family == AF_INET || ndm->ndm_family == AF_INET6) - return netlink_ipneigh_change(snl, h, len); + return netlink_ipneigh_change(snl, h, len, ns_id); return 0; } @@ -2340,10 +2352,14 @@ static int netlink_neigh_update2(struct interface *ifp, struct ipaddr *ip, } req; int ipa_len; - struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT); + struct zebra_ns *zns; char buf[INET6_ADDRSTRLEN]; char buf2[ETHER_ADDR_STRLEN]; + if (!vrf_is_backend_netns()) + zns = zebra_ns_lookup(NS_DEFAULT); + else + zns = (struct zebra_ns *)ns_info_lookup(ifp->vrf_id); memset(&req.n, 0, sizeof(req.n)); memset(&req.ndm, 0, sizeof(req.ndm)); diff --git a/zebra/rt_socket.c b/zebra/rt_socket.c index 6d4af1203c..b2baee5728 100644 --- a/zebra/rt_socket.c +++ b/zebra/rt_socket.c @@ -424,7 +424,7 @@ void kernel_route_rib(struct route_node *rn, struct prefix *p, } int kernel_neigh_update(int add, int ifindex, uint32_t addr, char *lla, - int llalen) + int llalen, ns_id_t ns_id) { /* TODO */ return 0; From 2c7d402164cc027a62be8ed2be2ffb25b17da1bd Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Mon, 22 Jan 2018 11:30:05 +0100 Subject: [PATCH 61/98] zebra: fix static analysis issue with zvrf_id Using c-lang scan-build tool, fix a dereference of a null pointer. Signed-off-by: Philippe Guibert --- zebra/zebra_vrf.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/zebra/zebra_vrf.h b/zebra/zebra_vrf.h index 4d53eee093..3b9e930969 100644 --- a/zebra/zebra_vrf.h +++ b/zebra/zebra_vrf.h @@ -128,6 +128,8 @@ struct zebra_vrf { static inline vrf_id_t zvrf_id(struct zebra_vrf *zvrf) { + if (!zvrf || !zvrf->vrf) + return VRF_UNKNOWN; return zvrf->vrf->vrf_id; } From fbb65ff50428eefba0653e8f3f6f33afa003c4cd Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Fri, 22 Dec 2017 16:21:09 +0100 Subject: [PATCH 62/98] zebra: zns context is filled in when vrf is enabled This commit is also a fix that avoids a VRF to be attached to the wrong namespace context, at creation time. Because the VRF, at creation time does not know yet the namespace where it will get its information. Signed-off-by: Philippe Guibert --- zebra/zebra_vrf.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/zebra/zebra_vrf.c b/zebra/zebra_vrf.c index a3596a4263..276687ca83 100644 --- a/zebra/zebra_vrf.c +++ b/zebra/zebra_vrf.c @@ -91,12 +91,9 @@ static int zebra_vrf_new(struct vrf *vrf) zlog_info("VRF %s created, id %u", vrf->name, vrf->vrf_id); zvrf = zebra_vrf_alloc(); - zvrf->zns = zebra_ns_lookup( - NS_DEFAULT); /* Point to the global (single) NS */ - router_id_init(zvrf); vrf->info = zvrf; zvrf->vrf = vrf; - + router_id_init(zvrf); return 0; } @@ -117,6 +114,10 @@ static int zebra_vrf_enable(struct vrf *vrf) zlog_debug("VRF %s id %u is now active", zvrf_name(zvrf), zvrf_id(zvrf)); + if (vrf_is_backend_netns()) + zvrf->zns = zebra_ns_lookup((ns_id_t)vrf->vrf_id); + else + zvrf->zns = zebra_ns_lookup(NS_DEFAULT); /* Inform clients that the VRF is now active. This is an * add for the clients. */ From 697d3ec73157fde8a008738907fef94fdcd569bb Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Wed, 13 Dec 2017 11:04:31 +0100 Subject: [PATCH 63/98] lib: externalise vrf and ns creation In addition to have the possibility to create from vty vrf based on a netns backend, the API will be made accessible from external, especially for zebra that will handle the netns discovery part. This commit is externalising following functions: - netns_pathname - ns_handler_create - vrf_handler_create Also, the VRF initialisation case when under NETNS backend is changed, since the NS identifier may not be known at the configuration time,but may be known later, under discovery process. Signed-off-by: Philippe Guibert --- lib/ns.c | 105 ++++++++++++++++++++++++++++++++++++++++-------------- lib/ns.h | 5 +++ lib/vrf.c | 63 +++++++++++++++++++++++--------- lib/vrf.h | 10 ++++++ 4 files changed, 141 insertions(+), 42 deletions(-) diff --git a/lib/ns.c b/lib/ns.c index 170290a9e9..694a6dab9d 100644 --- a/lib/ns.c +++ b/lib/ns.c @@ -236,6 +236,7 @@ static int ns_is_enabled(struct ns *ns) */ static int ns_enable(struct ns *ns) { + int vrf_on = 0; if (!ns_is_enabled(ns)) { if (have_netns()) { @@ -252,13 +253,26 @@ static int ns_enable(struct ns *ns) return 0; } + /* Non default NS. leave */ + if (ns->ns_id == NS_UNKNOWN) { + zlog_err("Can not enable NS %s %u: Invalid NSID", + ns->name, ns->ns_id); + return 0; + } + vrf_on = vrf_update_vrf_id((vrf_id_t)ns->ns_id, + (struct vrf *)ns->vrf_ctxt); if (have_netns()) zlog_info("NS %u is associated with NETNS %s.", ns->ns_id, ns->name); zlog_info("NS %u is enabled.", ns->ns_id); + /* zebra first receives NS enable event, + * then VRF enable event + */ if (ns_master.ns_enable_hook) (*ns_master.ns_enable_hook)(ns->ns_id, &ns->info); + if (vrf_on == 1) + vrf_enable((struct vrf *)ns->vrf_ctxt); } return 1; @@ -310,7 +324,7 @@ void ns_add_hook(int type, int (*func)(ns_id_t, void **)) * NS realization with NETNS */ -static char *ns_netns_pathname(struct vty *vty, const char *name) +char *ns_netns_pathname(struct vty *vty, const char *name) { static char pathname[PATH_MAX]; char *result; @@ -325,7 +339,9 @@ static char *ns_netns_pathname(struct vty *vty, const char *name) } if (!result) { - vty_out(vty, "Invalid pathname: %s\n", safe_strerror(errno)); + if (vty) + vty_out(vty, "Invalid pathname: %s\n", + safe_strerror(errno)); return NULL; } return pathname; @@ -413,34 +429,34 @@ DEFUN (no_ns_logicalrouter, return CMD_SUCCESS; } -DEFUN_NOSH (ns_netns, - ns_netns_cmd, - "netns NAME", - "Attach VRF to a Namespace\n" - "The file name in " NS_RUN_DIR ", or a full pathname\n") +int ns_handler_create(struct vty *vty, struct vrf *vrf, + char *pathname, ns_id_t ns_id) { - int idx_name = 1; struct ns *ns = NULL; - char *pathname = ns_netns_pathname(vty, argv[idx_name]->arg); - - VTY_DECLVAR_CONTEXT(vrf, vrf); - - if (!pathname) - return CMD_WARNING_CONFIG_FAILED; if (!vrf) return CMD_WARNING_CONFIG_FAILED; if (vrf->vrf_id != VRF_UNKNOWN && vrf->ns_ctxt == NULL) { - vty_out(vty, "VRF %u is already configured with VRF %s\n", - vrf->vrf_id, vrf->name); + if (vty) + vty_out(vty, + "VRF %u is already configured with VRF %s\n", + vrf->vrf_id, vrf->name); + else + zlog_warn("VRF %u is already configured with VRF %s\n", + vrf->vrf_id, vrf->name); return CMD_WARNING_CONFIG_FAILED; } if (vrf->ns_ctxt != NULL) { ns = (struct ns *) vrf->ns_ctxt; if (ns && 0 != strcmp(ns->name, pathname)) { - vty_out(vty, "VRF %u is already configured" - " with NETNS %s\n", - vrf->vrf_id, ns->name); + if (vty) + vty_out(vty, + "VRF %u is already configured" + " with NETNS %s\n", + vrf->vrf_id, ns->name); + else + zlog_warn("VRF %u is already configured with NETNS %s", + vrf->vrf_id, ns->name); return CMD_WARNING_CONFIG_FAILED; } } @@ -448,24 +464,38 @@ DEFUN_NOSH (ns_netns, if (ns && ns->vrf_ctxt) { struct vrf *vrf2 = (struct vrf *)ns->vrf_ctxt; - vty_out(vty, "NS %s is already configured" - " with VRF %u(%s)\n", - ns->name, vrf2->vrf_id, vrf2->name); + if (vty) + vty_out(vty, "NS %s is already configured" + " with VRF %u(%s)\n", + ns->name, vrf2->vrf_id, vrf2->name); + else + zlog_warn("NS %s is already configured with VRF %u(%s)", + ns->name, vrf2->vrf_id, vrf2->name); return CMD_WARNING_CONFIG_FAILED; } else if (!ns) ns = ns_get_by_name(pathname); + if (ns_id != ns->ns_id) { + RB_REMOVE(ns_head, &ns_tree, ns); + ns->ns_id = ns_id; + RB_INSERT(ns_head, &ns_tree, ns); + } + ns->vrf_ctxt = (void *)vrf; + vrf->ns_ctxt = (void *)ns; if (!ns_enable(ns)) { - vty_out(vty, "Can not associate NS %u with NETNS %s\n", - ns->ns_id, ns->name); + if (vty) + vty_out(vty, "Can not associate NS %u with NETNS %s\n", + ns->ns_id, ns->name); + else + zlog_warn("Can not associate NS %u with NETNS %s", + ns->ns_id, ns->name); return CMD_WARNING_CONFIG_FAILED; } - vrf->ns_ctxt = (void *)ns; - ns->vrf_ctxt = (void *)vrf; return CMD_SUCCESS; } + static int ns_logicalrouter_config_write(struct vty *vty) { struct ns *ns; @@ -481,6 +511,22 @@ static int ns_logicalrouter_config_write(struct vty *vty) return write; } +DEFUN_NOSH (ns_netns, + ns_netns_cmd, + "netns NAME", + "Attach VRF to a Namespace\n" + "The file name in " NS_RUN_DIR ", or a full pathname\n") +{ + int idx_name = 1; + char *pathname = ns_netns_pathname(vty, argv[idx_name]->arg); + + VTY_DECLVAR_CONTEXT(vrf, vrf); + + if (!pathname) + return CMD_WARNING_CONFIG_FAILED; + return ns_handler_create(vty, vrf, pathname, NS_UNKNOWN); +} + DEFUN (no_ns_netns, no_ns_netns_cmd, "no netns [NAME]", @@ -505,6 +551,13 @@ DEFUN (no_ns_netns, ns = (struct ns *)vrf->ns_ctxt; ns->vrf_ctxt = NULL; + vrf_disable(vrf); + /* vrf ID from VRF is necessary for Zebra + * so that propagate to other clients is done + */ + RB_REMOVE(ns_head, &ns_tree, ns); + ns->ns_id = NS_UNKNOWN; + RB_INSERT(ns_head, &ns_tree, ns); ns_delete(ns); vrf->ns_ctxt = NULL; return CMD_SUCCESS; diff --git a/lib/ns.h b/lib/ns.h index fab3e19368..fda062e65f 100644 --- a/lib/ns.h +++ b/lib/ns.h @@ -24,6 +24,8 @@ #include "openbsd-tree.h" #include "linklist.h" +#include "vty.h" +#include "vrf.h" typedef u_int32_t ns_id_t; @@ -93,5 +95,8 @@ extern void ns_terminate(void); /* Create a socket serving for the given NS */ extern int ns_socket(int, int, int, ns_id_t); extern void ns_cmd_init(void); +extern int ns_handler_create(struct vty *vty, struct vrf *vrf, + char *pathname, ns_id_t ns_id); +extern char *ns_netns_pathname(struct vty *vty, const char *name); #endif /*_ZEBRA_NS_H*/ diff --git a/lib/vrf.c b/lib/vrf.c index 81ff6d56fd..e855f3f83b 100644 --- a/lib/vrf.c +++ b/lib/vrf.c @@ -64,7 +64,6 @@ struct vrf_master { }; static int vrf_is_enabled(struct vrf *vrf); -static void vrf_disable(struct vrf *vrf); /* VRF list existance check by name. */ struct vrf *vrf_lookup_by_name(const char *name) @@ -84,6 +83,25 @@ static int vrf_name_compare(const struct vrf *a, const struct vrf *b) return strcmp(a->name, b->name); } +/* return 1 if vrf can be enabled */ +int vrf_update_vrf_id(vrf_id_t vrf_id, struct vrf *vrf) +{ + vrf_id_t old_vrf_id; + + if (!vrf) + return 0; + old_vrf_id = vrf->vrf_id; + if (vrf_id == vrf->vrf_id) + return 0; + if (vrf->vrf_id != VRF_UNKNOWN) + RB_REMOVE(vrf_id_head, &vrfs_by_id, vrf); + vrf->vrf_id = vrf_id; + RB_INSERT(vrf_id_head, &vrfs_by_id, vrf); + if (old_vrf_id == VRF_UNKNOWN) + return 1; + return 0; +} + /* Get a VRF. If not found, create one. * Arg: * name - The name of the vrf. May be NULL if unknown. @@ -223,7 +241,7 @@ int vrf_enable(struct vrf *vrf) * The VRF_DELETE_HOOK callback will be called to inform * that they must release the resources in the VRF. */ -static void vrf_disable(struct vrf *vrf) +void vrf_disable(struct vrf *vrf) { if (!vrf_is_enabled(vrf)) return; @@ -465,6 +483,32 @@ void vrf_configure_backend(int vrf_backend_netns) vrf_backend = vrf_backend_netns; } +int vrf_handler_create(struct vty *vty, const char *vrfname, struct vrf **vrf) +{ + struct vrf *vrfp; + + if (strlen(vrfname) > VRF_NAMSIZ) { + if (vty) + vty_out(vty, + "%% VRF name %s invalid: length exceeds %d bytes\n", + vrfname, VRF_NAMSIZ); + else + zlog_warn( + "%% VRF name %s invalid: length exceeds %d bytes\n", + vrfname, VRF_NAMSIZ); + return CMD_WARNING_CONFIG_FAILED; + } + + vrfp = vrf_get(VRF_UNKNOWN, vrfname); + + if (vty) + VTY_PUSH_CONTEXT(VRF_NODE, vrfp); + + if (vrf) + *vrf = vrfp; + return CMD_SUCCESS; +} + /* vrf CLI commands */ DEFUN_NOSH (vrf, vrf_cmd, @@ -474,21 +518,8 @@ DEFUN_NOSH (vrf, { int idx_name = 1; const char *vrfname = argv[idx_name]->arg; - struct vrf *vrfp; - if (strlen(vrfname) > VRF_NAMSIZ) { - vty_out(vty, - "%% VRF name %s is invalid: length exceeds " - "%d characters\n", - vrfname, VRF_NAMSIZ); - return CMD_WARNING_CONFIG_FAILED; - } - - vrfp = vrf_get(VRF_UNKNOWN, vrfname); - - VTY_PUSH_CONTEXT(VRF_NODE, vrfp); - - return CMD_SUCCESS; + return vrf_handler_create(vty, vrfname, NULL); } DEFUN_NOSH (no_vrf, diff --git a/lib/vrf.h b/lib/vrf.h index 40e6ab6cd6..16d75c3efb 100644 --- a/lib/vrf.h +++ b/lib/vrf.h @@ -212,6 +212,16 @@ extern int vrf_socket(int, int, int, vrf_id_t); extern void vrf_configure_backend(int vrf_backend_netns); extern int vrf_get_backend(void); extern int vrf_is_backend_netns(void); +extern int vrf_handler_create(struct vty *vty, + const char *name, + struct vrf **vrf); + +/* used by NS when vrf backend is NS. + * Notify a change in the VRF ID of the VRF + */ +extern int vrf_update_vrf_id(vrf_id_t vrf_id, struct vrf *vrf); +extern void vrf_disable(struct vrf *vrf); +extern int vrf_enable(struct vrf *vrf); /* * VRF Debugging From 3347430b12ecccc4f03fb29111e9621a6e123b3c Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Thu, 7 Dec 2017 18:27:31 +0100 Subject: [PATCH 64/98] zebra: add the registration mechanism for netns If vrf backend is netns, then the zebra will create its own zebra_ns context for each new netns discovered. As consequence, a routing table, and other contexts will be created for each new namespace discovered. When it is enabled, a populate process will be done, consisting in learning new interfaces and routes, and addresses from other NETNS. Signed-off-by: Philippe Guibert --- lib/ns.c | 21 ++++++++-------- lib/ns.h | 5 ++-- zebra/zebra_ns.c | 63 +++++++++++++++++++++++++++++++++++++++++++++++- zebra/zebra_ns.h | 3 +++ 4 files changed, 78 insertions(+), 14 deletions(-) diff --git a/lib/ns.c b/lib/ns.c index 694a6dab9d..1929104eeb 100644 --- a/lib/ns.c +++ b/lib/ns.c @@ -96,10 +96,10 @@ static int have_netns(void) /* Holding NS hooks */ struct ns_master { - int (*ns_new_hook)(ns_id_t, void **); - int (*ns_delete_hook)(ns_id_t, void **); - int (*ns_enable_hook)(ns_id_t, void **); - int (*ns_disable_hook)(ns_id_t, void **); + int (*ns_new_hook)(struct ns *ns); + int (*ns_delete_hook)(struct ns *ns); + int (*ns_enable_hook)(struct ns *ns); + int (*ns_disable_hook)(struct ns *ns); } ns_master = { 0, }; @@ -129,7 +129,8 @@ static void ns_get_created(struct ns *ns) else zlog_info("NS %s is created.", ns->name); if (ns_master.ns_new_hook) - (*ns_master.ns_new_hook)(ns->ns_id, &ns->info); + (*ns_master.ns_new_hook) (ns); + return; } /* Get a NS. If not found, create one. */ @@ -177,7 +178,7 @@ static void ns_delete(struct ns *ns) ns_disable(ns); if (ns_master.ns_delete_hook) - (*ns_master.ns_delete_hook)(ns->ns_id, &ns->info); + (*ns_master.ns_delete_hook)(ns); /* * I'm not entirely sure if the vrf->iflist @@ -270,7 +271,7 @@ static int ns_enable(struct ns *ns) * then VRF enable event */ if (ns_master.ns_enable_hook) - (*ns_master.ns_enable_hook)(ns->ns_id, &ns->info); + (*ns_master.ns_enable_hook)(ns); if (vrf_on == 1) vrf_enable((struct vrf *)ns->vrf_ctxt); } @@ -289,7 +290,7 @@ static void ns_disable(struct ns *ns) zlog_info("NS %u is to be disabled.", ns->ns_id); if (ns_master.ns_disable_hook) - (*ns_master.ns_disable_hook)(ns->ns_id, &ns->info); + (*ns_master.ns_disable_hook)(ns); if (have_netns()) close(ns->fd); @@ -300,7 +301,7 @@ static void ns_disable(struct ns *ns) /* Add a NS hook. Please add hooks before calling ns_init(). */ -void ns_add_hook(int type, int (*func)(ns_id_t, void **)) +void ns_add_hook(int type, int (*func)(struct ns *)) { switch (type) { case NS_NEW_HOOK: @@ -564,7 +565,7 @@ DEFUN (no_ns_netns, } /* Initialize NS module. */ -void ns_init(void) +void ns_init_zebra(void) { struct ns *default_ns; diff --git a/lib/ns.h b/lib/ns.h index fda062e65f..590e1f2c20 100644 --- a/lib/ns.h +++ b/lib/ns.h @@ -79,13 +79,12 @@ extern struct ns_head ns_tree; * - param 2: the address of the user data pointer (the user data * can be stored in or freed from there) */ -extern void ns_add_hook(int, int (*)(ns_id_t, void **)); +extern void ns_add_hook(int type, int (*)(struct ns *)); /* * NS initializer/destructor */ -/* Please add hooks before calling ns_init(). */ -extern void ns_init(void); +extern void ns_init_zebra(void); extern void ns_terminate(void); /* diff --git a/zebra/zebra_ns.c b/zebra/zebra_ns.c index 80847518a7..6ce64b3a33 100644 --- a/zebra/zebra_ns.c +++ b/zebra/zebra_ns.c @@ -32,6 +32,7 @@ #include "zebra_memory.h" #include "rt.h" #include "zebra_vxlan.h" +#include "debug.h" DEFINE_MTYPE(ZEBRA, ZEBRA_NS, "Zebra Name Space") @@ -59,6 +60,58 @@ struct zebra_ns *zebra_ns_lookup(ns_id_t ns_id) return dzns; } +static struct zebra_ns *zebra_ns_alloc(void) +{ + return XCALLOC(MTYPE_ZEBRA_NS, sizeof(struct zebra_ns)); +} + +static int zebra_ns_new(struct ns *ns) +{ + struct zebra_ns *zns; + + if (IS_ZEBRA_DEBUG_EVENT) + zlog_info("ZNS %s with id %u (created)", ns->name, ns->ns_id); + + zns = zebra_ns_alloc(); + ns->info = zns; + zns->ns = ns; + return 0; +} + +static int zebra_ns_delete(struct ns *ns) +{ + struct zebra_ns *zns = (struct zebra_ns *) ns->info; + + if (IS_ZEBRA_DEBUG_EVENT) + zlog_info("ZNS %s with id %u (deleted)", ns->name, ns->ns_id); + if (!zns) + return 0; + XFREE(MTYPE_ZEBRA_NS, zns); + return 0; +} + +static int zebra_ns_enabled(struct ns *ns) +{ + struct zebra_ns *zns = ns->info; + + if (IS_ZEBRA_DEBUG_EVENT) + zlog_info("ZNS %s with id %u (enabled)", ns->name, ns->ns_id); + if (!zns) + return 0; + return zebra_ns_enable(ns->ns_id, (void **)&zns); +} + +static int zebra_ns_disabled(struct ns *ns) +{ + struct zebra_ns *zns = ns->info; + + if (IS_ZEBRA_DEBUG_EVENT) + zlog_info("ZNS %s with id %u (disabled)", ns->name, ns->ns_id); + if (!zns) + return 0; + return zebra_ns_disable(ns->ns_id, (void **)&zns); +} + /* Do global enable actions - open sockets, read kernel config etc. */ int zebra_ns_enable(ns_id_t ns_id, void **info) { @@ -162,7 +215,9 @@ int zebra_ns_disable(ns_id_t ns_id, void **info) int zebra_ns_init(void) { - dzns = XCALLOC(MTYPE_ZEBRA_NS, sizeof(struct zebra_ns)); + dzns = zebra_ns_alloc(); + + ns_init_zebra(); ns_init(); @@ -176,6 +231,12 @@ int zebra_ns_init(void) /* Default NS is activated */ zebra_ns_enable(NS_DEFAULT, (void **)&dzns); + if (vrf_is_backend_netns()) { + ns_add_hook(NS_NEW_HOOK, zebra_ns_new); + ns_add_hook(NS_ENABLE_HOOK, zebra_ns_enabled); + ns_add_hook(NS_DISABLE_HOOK, zebra_ns_disabled); + ns_add_hook(NS_DELETE_HOOK, zebra_ns_delete); + } return 0; } diff --git a/zebra/zebra_ns.h b/zebra/zebra_ns.h index 99e4984164..aaf5abaa26 100644 --- a/zebra/zebra_ns.h +++ b/zebra/zebra_ns.h @@ -69,6 +69,9 @@ struct zebra_ns { #endif /* HAVE_RTADV */ struct zebra_ns_table_head ns_tables; + + /* Back pointer */ + struct ns *ns; }; struct zebra_ns *zebra_ns_lookup(ns_id_t ns_id); From ff705b15dd5e191e727662412a8433d718887a08 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Fri, 8 Dec 2017 14:32:38 +0100 Subject: [PATCH 65/98] zebra: handle the zns init/destroy The zebra netnamespace contexts are initialised, based on the callback coming from the NS. Reversely, the list of ns is parsed to disable the ns contexts. Signed-off-by: Philippe Guibert --- lib/ns.c | 17 +++++++++++++++++ lib/ns.h | 2 ++ zebra/main.c | 4 +--- zebra/zebra_ns.c | 17 +++++++++++++++-- zebra/zebra_ns.h | 1 + 5 files changed, 36 insertions(+), 5 deletions(-) diff --git a/lib/ns.c b/lib/ns.c index 1929104eeb..5e6bddf0d8 100644 --- a/lib/ns.c +++ b/lib/ns.c @@ -201,6 +201,23 @@ static struct ns *ns_lookup(ns_id_t ns_id) return (RB_FIND(ns_head, &ns_tree, &ns)); } +/* Look up the data pointer of the specified VRF. */ +void * +ns_info_lookup(ns_id_t ns_id) +{ + struct ns *ns = ns_lookup(ns_id); + + return ns ? ns->info : NULL; +} + +void ns_walk_func(int (*func)(struct ns *)) +{ + struct ns *ns = NULL; + + RB_FOREACH(ns, ns_head, &ns_tree) + func(ns); +} + /* Look up a NS by name */ static struct ns *ns_lookup_name(const char *name) { diff --git a/lib/ns.h b/lib/ns.h index 590e1f2c20..6aebc44259 100644 --- a/lib/ns.h +++ b/lib/ns.h @@ -97,5 +97,7 @@ extern void ns_cmd_init(void); extern int ns_handler_create(struct vty *vty, struct vrf *vrf, char *pathname, ns_id_t ns_id); extern char *ns_netns_pathname(struct vty *vty, const char *name); +extern void *ns_info_lookup(ns_id_t ns_id); +extern void ns_walk_func(int (*func)(struct ns *)); #endif /*_ZEBRA_NS_H*/ diff --git a/zebra/main.c b/zebra/main.c index a881fcb9c6..73e5f1290d 100644 --- a/zebra/main.c +++ b/zebra/main.c @@ -123,7 +123,6 @@ static void sigint(void) { struct vrf *vrf; struct zebra_vrf *zvrf; - struct zebra_ns *zns; zlog_notice("Terminating on signal"); @@ -140,8 +139,7 @@ static void sigint(void) } vrf_terminate(); - zns = zebra_ns_lookup(NS_DEFAULT); - zebra_ns_disable(0, (void **)&zns); + ns_walk_func(zebra_ns_disabled); access_list_reset(); prefix_list_reset(); diff --git a/zebra/zebra_ns.c b/zebra/zebra_ns.c index 6ce64b3a33..02fc2b1844 100644 --- a/zebra/zebra_ns.c +++ b/zebra/zebra_ns.c @@ -57,7 +57,11 @@ zebra_ns_table_entry_compare(const struct zebra_ns_table *e1, struct zebra_ns *zebra_ns_lookup(ns_id_t ns_id) { - return dzns; + if (ns_id == NS_DEFAULT) + return dzns; + struct zebra_ns *info = (struct zebra_ns *)ns_info_lookup(ns_id); + + return (info == NULL) ? dzns : info; } static struct zebra_ns *zebra_ns_alloc(void) @@ -75,6 +79,11 @@ static int zebra_ns_new(struct ns *ns) zns = zebra_ns_alloc(); ns->info = zns; zns->ns = ns; + + /* Do any needed per-NS data structure allocation. */ + zns->if_table = route_table_init(); + zebra_vxlan_ns_init(zns); + return 0; } @@ -101,7 +110,7 @@ static int zebra_ns_enabled(struct ns *ns) return zebra_ns_enable(ns->ns_id, (void **)&zns); } -static int zebra_ns_disabled(struct ns *ns) +int zebra_ns_disabled(struct ns *ns) { struct zebra_ns *zns = ns->info; @@ -117,6 +126,8 @@ int zebra_ns_enable(ns_id_t ns_id, void **info) { struct zebra_ns *zns = (struct zebra_ns *)(*info); + zns->ns_id = ns_id; + #if defined(HAVE_RTADV) rtadv_init(zns); #endif @@ -209,6 +220,8 @@ int zebra_ns_disable(ns_id_t ns_id, void **info) kernel_terminate(zns); + zns->ns_id = NS_DEFAULT; + return 0; } diff --git a/zebra/zebra_ns.h b/zebra/zebra_ns.h index aaf5abaa26..3a998a49ff 100644 --- a/zebra/zebra_ns.h +++ b/zebra/zebra_ns.h @@ -78,6 +78,7 @@ struct zebra_ns *zebra_ns_lookup(ns_id_t ns_id); int zebra_ns_init(void); int zebra_ns_enable(ns_id_t ns_id, void **info); +int zebra_ns_disabled(struct ns *ns); int zebra_ns_disable(ns_id_t ns_id, void **info); extern struct route_table *zebra_ns_find_table(struct zebra_ns *zns, From 81c9005ff6edd2294ec945b93d49f03470b3b827 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Thu, 7 Dec 2017 15:58:48 +0100 Subject: [PATCH 66/98] zebra: enhance show vrf for netns and fixing Show vrf command displays information on the vrf, if it is related to vrf kernel or if it is related to netns. When a vrf from kernel is detected, before creating a new vrf, a check is done against an already present vrf, and if that vrf is not a vrf mapped with a netns. If that is that case, then the creation is rejected. Signed-off-by: Philippe Guibert --- lib/ns.c | 7 +++++++ lib/ns.h | 1 + zebra/zebra_vrf.h | 13 +++++++++++++ zebra/zebra_vty.c | 6 +++++- 4 files changed, 26 insertions(+), 1 deletion(-) diff --git a/lib/ns.c b/lib/ns.c index 5e6bddf0d8..5af896632c 100644 --- a/lib/ns.c +++ b/lib/ns.c @@ -218,6 +218,13 @@ void ns_walk_func(int (*func)(struct ns *)) func(ns); } +const char *ns_get_name(struct ns *ns) +{ + if (!ns) + return NULL; + return ns->name; +} + /* Look up a NS by name */ static struct ns *ns_lookup_name(const char *name) { diff --git a/lib/ns.h b/lib/ns.h index 6aebc44259..fca5becd7a 100644 --- a/lib/ns.h +++ b/lib/ns.h @@ -99,5 +99,6 @@ extern int ns_handler_create(struct vty *vty, struct vrf *vrf, extern char *ns_netns_pathname(struct vty *vty, const char *name); extern void *ns_info_lookup(ns_id_t ns_id); extern void ns_walk_func(int (*func)(struct ns *)); +extern const char *ns_get_name(struct ns *ns); #endif /*_ZEBRA_NS_H*/ diff --git a/zebra/zebra_vrf.h b/zebra/zebra_vrf.h index 3b9e930969..ae5a174116 100644 --- a/zebra/zebra_vrf.h +++ b/zebra/zebra_vrf.h @@ -22,6 +22,7 @@ #if !defined(__ZEBRA_RIB_H__) #define __ZEBRA_RIB_H__ +#include #include #include #include @@ -133,11 +134,23 @@ static inline vrf_id_t zvrf_id(struct zebra_vrf *zvrf) return zvrf->vrf->vrf_id; } +static inline const char *zvrf_ns_name(struct zebra_vrf *zvrf) +{ + if (!zvrf->vrf || !zvrf->vrf->ns_ctxt) + return NULL; + return ns_get_name((struct ns *)zvrf->vrf->ns_ctxt); +} + static inline const char *zvrf_name(struct zebra_vrf *zvrf) { return zvrf->vrf->name; } +static inline bool zvrf_is_active(struct zebra_vrf *zvrf) +{ + return zvrf->vrf->status & VRF_ACTIVE; +} + struct route_table *zebra_vrf_table_with_table_id(afi_t afi, safi_t safi, vrf_id_t vrf_id, u_int32_t table_id); diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index ccc7cb30c3..4824c09f3d 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -2374,8 +2374,12 @@ DEFUN (show_vrf, continue; vty_out(vty, "vrf %s ", zvrf_name(zvrf)); - if (zvrf_id(zvrf) == VRF_UNKNOWN) + if (zvrf_id(zvrf) == VRF_UNKNOWN + || !zvrf_is_active(zvrf)) vty_out(vty, "inactive"); + else if (zvrf_ns_name(zvrf)) + vty_out(vty, "id %u netns %s", + zvrf_id(zvrf), zvrf_ns_name(zvrf)); else vty_out(vty, "id %u table %u", zvrf_id(zvrf), zvrf->table_id); From 4691b65ae4c3e50c295dce4fc007738080826b49 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Fri, 22 Dec 2017 16:02:51 +0100 Subject: [PATCH 67/98] lib: add namespace name structure in zebra message The addition of the name of the netns in the vrf message introduces also a limitation when the size of the netns is bigger than 15 bytes. Then the netns are ignored by the library. In addition to this, some sanity checks have been introduced. some functions to create the netns from a call not coming from the vty is being added with traces. Also, the ns vty function is reentrant, if the context is already created. Signed-off-by: Philippe Guibert Signed-off-by: Renato Westphal --- lib/ns.c | 25 +++++++++++++++++++++++++ lib/vrf.c | 6 +++--- lib/vrf.h | 2 ++ lib/zclient.c | 4 ++-- zebra/zebra_vrf.c | 2 +- zebra/zserv.c | 12 ++++++++++-- 6 files changed, 43 insertions(+), 8 deletions(-) diff --git a/lib/ns.c b/lib/ns.c index 5af896632c..e2c042d16c 100644 --- a/lib/ns.c +++ b/lib/ns.c @@ -28,6 +28,9 @@ #include #endif +/* for basename */ +#include + #include "if.h" #include "ns.h" #include "log.h" @@ -353,6 +356,7 @@ char *ns_netns_pathname(struct vty *vty, const char *name) { static char pathname[PATH_MAX]; char *result; + char *check_base; if (name[0] == '/') /* absolute pathname */ result = realpath(name, pathname); @@ -367,6 +371,21 @@ char *ns_netns_pathname(struct vty *vty, const char *name) if (vty) vty_out(vty, "Invalid pathname: %s\n", safe_strerror(errno)); + else + zlog_warn("Invalid pathname: %s", + safe_strerror(errno)); + return NULL; + } + check_base = basename(pathname); + if (check_base != NULL && strlen(check_base) + 1 > NS_NAMSIZ) { + if (vty) + vty_out(vty, "NS name (%s) invalid:" + " too long( %d needed)\n", + check_base, NS_NAMSIZ-1); + else + zlog_warn("NS name (%s) invalid:" + " too long ( %d needed)", + check_base, NS_NAMSIZ-1); return NULL; } return pathname; @@ -489,6 +508,8 @@ int ns_handler_create(struct vty *vty, struct vrf *vrf, if (ns && ns->vrf_ctxt) { struct vrf *vrf2 = (struct vrf *)ns->vrf_ctxt; + if (vrf2 == vrf) + return CMD_SUCCESS; if (vty) vty_out(vty, "NS %s is already configured" " with VRF %u(%s)\n", @@ -507,6 +528,10 @@ int ns_handler_create(struct vty *vty, struct vrf *vrf, } ns->vrf_ctxt = (void *)vrf; vrf->ns_ctxt = (void *)ns; + /* update VRF netns NAME */ + if (vrf) + strlcpy(vrf->data.l.netns_name, basename(pathname), NS_NAMSIZ); + if (!ns_enable(ns)) { if (vty) vty_out(vty, "Can not associate NS %u with NETNS %s\n", diff --git a/lib/vrf.c b/lib/vrf.c index e855f3f83b..5b85effabd 100644 --- a/lib/vrf.c +++ b/lib/vrf.c @@ -21,6 +21,9 @@ #include +/* for basename */ +#include + #include "if.h" #include "vrf.h" #include "vrf_int.h" @@ -131,8 +134,6 @@ struct vrf *vrf_get(vrf_id_t vrf_id, const char *name) if (vrf == NULL) { vrf = XCALLOC(MTYPE_VRF, sizeof(struct vrf)); vrf->vrf_id = VRF_UNKNOWN; - RB_INIT(if_name_head, &vrf->ifaces_by_name); - RB_INIT(if_index_head, &vrf->ifaces_by_index); QOBJ_REG(vrf, vrf); new = 1; @@ -156,7 +157,6 @@ struct vrf *vrf_get(vrf_id_t vrf_id, const char *name) strlcpy(vrf->name, name, sizeof(vrf->name)); RB_INSERT(vrf_name_head, &vrfs_by_name, vrf); } - if (new &&vrf_master.vrf_new_hook) (*vrf_master.vrf_new_hook)(vrf); diff --git a/lib/vrf.h b/lib/vrf.h index 16d75c3efb..c1da4e8bbc 100644 --- a/lib/vrf.h +++ b/lib/vrf.h @@ -42,6 +42,7 @@ enum { IFLA_VRF_UNSPEC, IFLA_VRF_TABLE, __IFLA_VRF_MAX }; #endif #define VRF_NAMSIZ 36 +#define NS_NAMSIZ 16 #define VRF_DEFAULT_NAME "Default-IP-Routing-Table" @@ -60,6 +61,7 @@ struct vrf_data { union { struct { uint32_t table_id; + char netns_name[NS_NAMSIZ]; } l; }; }; diff --git a/lib/zclient.c b/lib/zclient.c index 714888a3f3..9260e0b3ba 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -1402,8 +1402,8 @@ static void zclient_vrf_add(struct zclient *zclient, vrf_id_t vrf_id) /* Lookup/create vrf by vrf_id. */ vrf = vrf_get(vrf_id, vrfname_tmp); - vrf->data = data; - + vrf->data.l.table_id = data.l.table_id; + memcpy(vrf->data.l.netns_name, data.l.netns_name, NS_NAMSIZ); vrf_enable(vrf); } diff --git a/zebra/zebra_vrf.c b/zebra/zebra_vrf.c index 276687ca83..874a9c74e7 100644 --- a/zebra/zebra_vrf.c +++ b/zebra/zebra_vrf.c @@ -77,7 +77,7 @@ void zebra_vrf_update_all(struct zserv *client) struct vrf *vrf; RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id) { - if (vrf->vrf_id) + if (vrf->vrf_id != VRF_UNKNOWN) zsend_vrf_add(client, vrf_info_lookup(vrf->vrf_id)); } } diff --git a/zebra/zserv.c b/zebra/zserv.c index b3b1fa79e9..f269422986 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -20,6 +20,8 @@ #include #include +/* for basename */ +#include #include "prefix.h" #include "command.h" @@ -182,13 +184,19 @@ static void zserv_encode_interface(struct stream *s, struct interface *ifp) static void zserv_encode_vrf(struct stream *s, struct zebra_vrf *zvrf) { struct vrf_data data; + const char *netns_name = zvrf_ns_name(zvrf); data.l.table_id = zvrf->table_id; - /* Pass the tableid */ + + if (netns_name) + strlcpy(data.l.netns_name, + basename((char *)netns_name), NS_NAMSIZ); + else + memset(data.l.netns_name, 0, NS_NAMSIZ); + /* Pass the tableid and the netns NAME */ stream_put(s, &data, sizeof(struct vrf_data)); /* Interface information. */ stream_put(s, zvrf_name(zvrf), VRF_NAMSIZ); - /* Write packet size. */ stream_putw_at(s, 0, stream_get_endp(s)); } From ce1be3692f809cfa4d533d484a75653f91c24c4e Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Wed, 20 Dec 2017 12:29:21 +0100 Subject: [PATCH 68/98] lib: provide an API to switch from one netns to an other Two apis are provided so that the switch from one netns to an other one is taken care. Also an other API to know if the VRF has a NETNS backend or a VRF Lite backend. Signed-off-by: Philippe Guibert --- lib/ns.c | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++++- lib/ns.h | 6 ++++++ lib/vrf.c | 37 +++++++++++++++++++++++++++++++++++++ lib/vrf.h | 7 +++++++ 4 files changed, 103 insertions(+), 1 deletion(-) diff --git a/lib/ns.c b/lib/ns.c index e2c042d16c..9aa3509923 100644 --- a/lib/ns.c +++ b/lib/ns.c @@ -51,6 +51,9 @@ RB_GENERATE(ns_head, ns, entry, ns_compare) struct ns_head ns_tree = RB_INITIALIZER(&ns_tree); +static int ns_current_ns_fd; +static int ns_default_ns_fd; + #ifndef CLONE_NEWNET #define CLONE_NEWNET 0x40000000 /* New network namespace (lo, device, names sockets, etc) */ #endif @@ -613,13 +616,26 @@ DEFUN (no_ns_netns, return CMD_SUCCESS; } +void ns_init(void) +{ +#ifdef HAVE_NETNS + if (have_netns_enabled < 0) { + ns_default_ns_fd = open(NS_DEFAULT_NAME, O_RDONLY); + return; + } +#endif /* HAVE_NETNS */ + ns_default_ns_fd = -1; +} + /* Initialize NS module. */ void ns_init_zebra(void) { struct ns *default_ns; + ns_init(); /* The default NS always exists. */ default_ns = ns_get(NS_DEFAULT); + ns_current_ns_fd = -1; if (!default_ns) { zlog_err("ns_init: failed to create the default NS!"); exit(1); @@ -664,6 +680,40 @@ void ns_terminate(void) } } +int ns_switch_to_netns(const char *name) +{ + int ret; + int fd; + + if (name == NULL) + return -1; + fd = open(name, O_RDONLY); + if (fd == -1) { + errno = ENOSYS; + return -1; + } + ret = setns(fd, CLONE_NEWNET); + ns_current_ns_fd = fd; + close(fd); + return ret; +} + +/* returns 1 if switch() was not called before + * return status of setns() otherwise + */ +int ns_switchback_to_initial(void) +{ + if (ns_current_ns_fd != -1) { + int ret; + + ret = setns(ns_default_ns_fd, CLONE_NEWNET); + ns_current_ns_fd = -1; + return ret; + } + /* silently ignore if setns() is not called */ + return 1; +} + /* Create a socket for the NS. */ int ns_socket(int domain, int type, int protocol, ns_id_t ns_id) { @@ -679,8 +729,10 @@ int ns_socket(int domain, int type, int protocol, ns_id_t ns_id) ret = (ns_id != NS_DEFAULT) ? setns(ns->fd, CLONE_NEWNET) : 0; if (ret >= 0) { ret = socket(domain, type, protocol); - if (ns_id != NS_DEFAULT) + if (ns_id != NS_DEFAULT) { setns(ns_lookup(NS_DEFAULT)->fd, CLONE_NEWNET); + ns_current_ns_fd = ns_id; + } } } else ret = socket(domain, type, protocol); diff --git a/lib/ns.h b/lib/ns.h index fca5becd7a..73482d4d56 100644 --- a/lib/ns.h +++ b/lib/ns.h @@ -84,6 +84,7 @@ extern void ns_add_hook(int type, int (*)(struct ns *)); /* * NS initializer/destructor */ +extern void ns_init(void); extern void ns_init_zebra(void); extern void ns_terminate(void); @@ -101,4 +102,9 @@ extern void *ns_info_lookup(ns_id_t ns_id); extern void ns_walk_func(int (*func)(struct ns *)); extern const char *ns_get_name(struct ns *ns); +/* API that can be used by all daemons */ +extern int ns_switchback_to_initial(void); +extern int ns_switch_to_netns(const char *netns_name); +extern void ns_init(void); + #endif /*_ZEBRA_NS_H*/ diff --git a/lib/vrf.c b/lib/vrf.c index 5b85effabd..7871052352 100644 --- a/lib/vrf.c +++ b/lib/vrf.c @@ -86,6 +86,32 @@ static int vrf_name_compare(const struct vrf *a, const struct vrf *b) return strcmp(a->name, b->name); } +int vrf_switch_to_netns(vrf_id_t vrf_id) +{ + char *name; + struct vrf *vrf = vrf_lookup_by_id(vrf_id); + + /* VRF has no NETNS backend. silently ignore */ + if (!vrf || vrf->data.l.netns_name[0] == '\0') + return 0; + /* VRF is default VRF. silently ignore */ + if (vrf->vrf_id == VRF_DEFAULT) + return 0; + name = ns_netns_pathname(NULL, vrf->data.l.netns_name); + if (debug_vrf) + zlog_debug("VRF_SWITCH: %s(%u)", name, vrf->vrf_id); + return ns_switch_to_netns(name); +} + +int vrf_switchback_to_initial(void) +{ + int ret = ns_switchback_to_initial(); + + if (ret == 0 && debug_vrf) + zlog_debug("VRF_SWITCHBACK"); + return ret; +} + /* return 1 if vrf can be enabled */ int vrf_update_vrf_id(vrf_id_t vrf_id, struct vrf *vrf) { @@ -509,6 +535,17 @@ int vrf_handler_create(struct vty *vty, const char *vrfname, struct vrf **vrf) return CMD_SUCCESS; } +int vrf_is_mapped_on_netns(vrf_id_t vrf_id) +{ + struct vrf *vrf = vrf_lookup_by_id(vrf_id); + + if (!vrf || vrf->data.l.netns_name[0] == '\0') + return 0; + if (vrf->vrf_id == VRF_DEFAULT) + return 0; + return 1; +} + /* vrf CLI commands */ DEFUN_NOSH (vrf, vrf_cmd, diff --git a/lib/vrf.h b/lib/vrf.h index c1da4e8bbc..4bdc183b5c 100644 --- a/lib/vrf.h +++ b/lib/vrf.h @@ -218,6 +218,13 @@ extern int vrf_handler_create(struct vty *vty, const char *name, struct vrf **vrf); +/* VRF is mapped on netns or not ? */ +int vrf_is_mapped_on_netns(vrf_id_t vrf_id); + +/* VRF switch from NETNS */ +extern int vrf_switch_to_netns(vrf_id_t vrf_id); +extern int vrf_switchback_to_initial(void); + /* used by NS when vrf backend is NS. * Notify a change in the VRF ID of the VRF */ From 05895ad0be6c8fadcd895c3b1aa137b89791fbde Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Thu, 7 Dec 2017 18:13:54 +0100 Subject: [PATCH 69/98] zebra: upon NS creation, collect the NSID via netlink A NS identifier is collected by netlink. This identifier is a 32 bit identifier that is either generated by the kernel (if not set) or manually set by a set netlink command. The commit here is getting the NSID from the newly created NS. If the linux option to create or get a new NSID from the kernel does not exist, then the NSID is locally genrated. Signed-off-by: Philippe Guibert --- include/linux/net_namespace.h | 23 +++ include/subdir.am | 1 + zebra/subdir.am | 2 + zebra/zebra_netns_id.c | 314 ++++++++++++++++++++++++++++++++++ zebra/zebra_netns_id.h | 25 +++ 5 files changed, 365 insertions(+) create mode 100644 include/linux/net_namespace.h create mode 100644 zebra/zebra_netns_id.c create mode 100644 zebra/zebra_netns_id.h diff --git a/include/linux/net_namespace.h b/include/linux/net_namespace.h new file mode 100644 index 0000000000..9a92b7e14a --- /dev/null +++ b/include/linux/net_namespace.h @@ -0,0 +1,23 @@ +/* Copyright (c) 2015 6WIND S.A. + * Author: Nicolas Dichtel + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + */ +#ifndef _LINUX_NET_NAMESPACE_H_ +#define _LINUX_NET_NAMESPACE_H_ + +/* Attributes of RTM_NEWNSID/RTM_GETNSID messages */ +enum { + NETNSA_NONE, +#define NETNSA_NSID_NOT_ASSIGNED -1 + NETNSA_NSID, + NETNSA_PID, + NETNSA_FD, + __NETNSA_MAX, +}; + +#define NETNSA_MAX (__NETNSA_MAX - 1) + +#endif /* _LINUX_NET_NAMESPACE_H_ */ diff --git a/include/subdir.am b/include/subdir.am index 98bd148002..7a12b2ffae 100644 --- a/include/subdir.am +++ b/include/subdir.am @@ -6,4 +6,5 @@ noinst_HEADERS += \ include/linux/neighbour.h \ include/linux/rtnetlink.h \ include/linux/socket.h \ + include/linux/net_namespace.h \ # end diff --git a/zebra/subdir.am b/zebra/subdir.am index 3474823623..fcfb0cc203 100644 --- a/zebra/subdir.am +++ b/zebra/subdir.am @@ -65,6 +65,7 @@ zebra_zebra_SOURCES = \ zebra/zebra_vty.c \ zebra/zebra_vxlan.c \ zebra/zserv.c \ + zebra/zebra_netns_id.c \ # end zebra/zebra_vty_clippy.c: $(CLIPPY_DEPS) @@ -104,6 +105,7 @@ noinst_HEADERS += \ zebra/zebra_vxlan.h \ zebra/zebra_vxlan_private.h \ zebra/zserv.h \ + zebra/zebra_netns_id.h \ # end zebra_zebra_irdp_la_SOURCES = \ diff --git a/zebra/zebra_netns_id.c b/zebra/zebra_netns_id.c new file mode 100644 index 0000000000..c5e792bd77 --- /dev/null +++ b/zebra/zebra_netns_id.c @@ -0,0 +1,314 @@ +/* zebra NETNS ID handling routines + * those routines are implemented locally to avoid having external dependencies. + * Copyright (C) 2018 6WIND + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "ns.h" +#include "log.h" + +#if defined(HAVE_NETLINK) + +#include +#include +#include + +#include "rib.h" +#include "zebra_ns.h" +#include "kernel_netlink.h" +#endif /* defined(HAVE_NETLINK) */ + +#include "zebra_netns_id.h" + +/* in case NEWNSID not available, the NSID will be locally obtained + */ +#define NS_BASE_NSID 0 + +#if defined(HAVE_NETLINK) + +#define NETLINK_SOCKET_BUFFER_SIZE 512 +#define NETLINK_ALIGNTO 4 +#define NETLINK_ALIGN(len) (((len)+NETLINK_ALIGNTO-1) \ + & ~(NETLINK_ALIGNTO-1)) +#define NETLINK_NLATTR_LEN(_a, _b) (unsigned int)((char *)_a - (char *)_b) + +#endif /* defined(HAVE_NETLINK) */ + +static ns_id_t zebra_ns_id_get_fallback(const char *netnspath) +{ + static int zebra_ns_id_local; + + return zebra_ns_id_local++; +} + +#if defined(HAVE_NETLINK) + +static struct nlmsghdr *initiate_nlh(char *buf, unsigned int *seq, int type) +{ + struct nlmsghdr *nlh; + + nlh = (struct nlmsghdr *)buf; + nlh->nlmsg_len = NETLINK_ALIGN(sizeof(struct nlmsghdr)); + + nlh->nlmsg_type = type; + nlh->nlmsg_flags = NLM_F_REQUEST; + if (type == RTM_NEWNSID) + nlh->nlmsg_flags |= NLM_F_ACK; + nlh->nlmsg_seq = *seq = time(NULL); + return nlh; +} + +static int send_receive(int sock, struct nlmsghdr *nlh, + unsigned int seq, char *buf) +{ + int ret; + static const struct sockaddr_nl snl = { + .nl_family = AF_NETLINK + }; + + ret = sendto(sock, (const void *)nlh, (size_t)nlh->nlmsg_len, 0, + (struct sockaddr *) &snl, (socklen_t)sizeof(snl)); + if (ret < 0) { + zlog_err("netlink( %u) sendmsg() error: %s", + sock, safe_strerror(errno)); + return -1; + } + + /* reception */ + struct sockaddr_nl addr; + struct iovec iov = { + .iov_base = buf, + .iov_len = NETLINK_SOCKET_BUFFER_SIZE, + }; + struct msghdr msg = { + .msg_name = &addr, + .msg_namelen = sizeof(struct sockaddr_nl), + .msg_iov = &iov, + .msg_iovlen = 1, + .msg_control = NULL, + .msg_controllen = 0, + .msg_flags = 0, + }; + ret = recvmsg(sock, &msg, 0); + if (ret < 0) { + zlog_err("netlink recvmsg: error %d (errno %u)", ret, errno); + return -1; + } + if (msg.msg_flags & MSG_TRUNC) { + zlog_err("netlink recvmsg : error message truncated"); + return -1; + } + /* nlh already points to buf */ + if (nlh->nlmsg_seq != seq) { + zlog_err("netlink recvmsg: bad sequence number %x (expected %x)", + seq, nlh->nlmsg_seq); + return -1; + } + return ret; +} + +/* extract on a valid nlmsg the nsid + * valid nlmsghdr - not a nlmsgerr + */ +static ns_id_t extract_nsid(struct nlmsghdr *nlh, char *buf) +{ + ns_id_t ns_id = NS_UNKNOWN; + int offset = NETLINK_ALIGN(sizeof(struct nlmsghdr)) + + NETLINK_ALIGN(sizeof(struct rtgenmsg)); + int curr_length = offset; + void *tail = (void *)((char *)nlh + NETLINK_ALIGN(nlh->nlmsg_len)); + struct nlattr *attr; + + for (attr = (struct nlattr *)((char *)buf + offset); + NETLINK_NLATTR_LEN(tail, attr) >= sizeof(struct nlattr) && + attr->nla_len >= sizeof(struct nlattr) && + attr->nla_len <= NETLINK_NLATTR_LEN(tail, attr); + attr += NETLINK_ALIGN(attr->nla_len)) { + curr_length += attr->nla_len; + if ((attr->nla_type & NLA_TYPE_MASK) == NETNSA_NSID) { + u_int32_t *ptr = (u_int32_t *)(attr); + + ns_id = ptr[1]; + break; + } + } + return ns_id; +} + +ns_id_t zebra_ns_id_get(const char *netnspath) +{ + int ns_id = -1; + struct sockaddr_nl snl; + int fd, sock, ret; + unsigned int seq; + ns_id_t return_nsid = NS_UNKNOWN; + + /* netns path check */ + if (!netnspath) + return NS_UNKNOWN; + fd = open(netnspath, O_RDONLY); + if (fd == -1) + return NS_UNKNOWN; + + /* netlink socket */ + sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (sock < 0) { + zlog_err("netlink( %u) socket() error: %s", + sock, safe_strerror(errno)); + return NS_UNKNOWN; + } + memset(&snl, 0, sizeof(snl)); + snl.nl_family = AF_NETLINK; + snl.nl_groups = RTNLGRP_NSID; + snl.nl_pid = 0; /* AUTO PID */ + ret = bind(sock, (struct sockaddr *)&snl, sizeof(snl)); + if (ret < 0) { + zlog_err("netlink( %u) socket() bind error: %s", + sock, safe_strerror(errno)); + close(sock); + close(fd); + return NS_UNKNOWN; + } + + /* message to send to netlink,and response : NEWNSID */ + char buf[NETLINK_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + struct rtgenmsg *rt; + int len; + + memset(buf, 0, NETLINK_SOCKET_BUFFER_SIZE); + nlh = initiate_nlh(buf, &seq, RTM_NEWNSID); + rt = (struct rtgenmsg *)(buf + nlh->nlmsg_len); + nlh->nlmsg_len += NETLINK_ALIGN(sizeof(struct rtgenmsg)); + rt->rtgen_family = AF_UNSPEC; + + addattr32(nlh, NETLINK_SOCKET_BUFFER_SIZE, NETNSA_FD, fd); + addattr32(nlh, NETLINK_SOCKET_BUFFER_SIZE, NETNSA_NSID, ns_id); + + ret = send_receive(sock, nlh, seq, buf); + if (ret < 0) { + close(sock); + close(fd); + return NS_UNKNOWN; + } + nlh = (struct nlmsghdr *)buf; + + /* message to analyse : NEWNSID response */ + len = ret; + ret = 0; + do { + if (nlh->nlmsg_type >= NLMSG_MIN_TYPE) { + return_nsid = extract_nsid(nlh, buf); + if (return_nsid != NS_UNKNOWN) + break; + } else { + if (nlh->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err = (struct nlmsgerr *) + ((char *)nlh + + NETLINK_ALIGN(sizeof( + struct nlmsghdr))); + + ret = -1; + if (err->error < 0) + errno = -err->error; + else + errno = err->error; + if (errno == 0) { + /* request NEWNSID was successfull + * return EEXIST error to get GETNSID + */ + errno = EEXIST; + } + } else { + /* other errors ignored + * attempt to get nsid + */ + ret = -1; + errno = EEXIST; + break; + } + } + len = len - NETLINK_ALIGN(nlh->nlmsg_len); + nlh = (struct nlmsghdr *)((char *)nlh + + NETLINK_ALIGN(nlh->nlmsg_len)); + } while (len != 0 && return_nsid != NS_UNKNOWN && ret == 0); + + if (ret <= 0) { + if (errno != EEXIST && ret != 0) { + zlog_err("netlink( %u) recvfrom() error 2 when reading: %s", + fd, safe_strerror(errno)); + close(sock); + close(fd); + if (errno == ENOTSUP) { + zlog_warn("NEWNSID locally generated"); + return zebra_ns_id_get_fallback(netnspath); + } + return NS_UNKNOWN; + } + /* message to send to netlink : GETNSID */ + memset(buf, 0, NETLINK_SOCKET_BUFFER_SIZE); + nlh = initiate_nlh(buf, &seq, RTM_GETNSID); + rt = (struct rtgenmsg *)(buf + nlh->nlmsg_len); + nlh->nlmsg_len += NETLINK_ALIGN(sizeof(struct rtgenmsg)); + rt->rtgen_family = AF_UNSPEC; + + addattr32(nlh, NETLINK_SOCKET_BUFFER_SIZE, NETNSA_FD, fd); + addattr32(nlh, NETLINK_SOCKET_BUFFER_SIZE, NETNSA_NSID, ns_id); + + ret = send_receive(sock, nlh, seq, buf); + if (ret < 0) { + close(sock); + close(fd); + return NS_UNKNOWN; + } + nlh = (struct nlmsghdr *)buf; + len = ret; + ret = 0; + do { + if (nlh->nlmsg_type >= NLMSG_MIN_TYPE) { + return_nsid = extract_nsid(nlh, buf); + if (return_nsid != NS_UNKNOWN) + break; + } else if (nlh->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err = (struct nlmsgerr *) + ((char *)nlh + + NETLINK_ALIGN(sizeof( + struct nlmsghdr))); + if (err->error < 0) + errno = -err->error; + else + errno = err->error; + break; + } + len = len - NETLINK_ALIGN(nlh->nlmsg_len); + nlh = (struct nlmsghdr *)((char *)nlh + + NETLINK_ALIGN(nlh->nlmsg_len)); + } while (len != 0 && return_nsid != NS_UNKNOWN && ret == 0); + } + + close(fd); + close(sock); + return return_nsid; +} + +#else +ns_id_t zebra_ns_id_get(const char *netnspath) +{ + return zebra_ns_id_get_fallback(netnspath); +} +#endif /* ! defined(HAVE_NETLINK) */ diff --git a/zebra/zebra_netns_id.h b/zebra/zebra_netns_id.h new file mode 100644 index 0000000000..18fdf50cf1 --- /dev/null +++ b/zebra/zebra_netns_id.h @@ -0,0 +1,25 @@ +/* zebra NETNS ID handling routines + * Copyright (C) 2018 6WIND + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#if !defined(__ZEBRA_NS_ID_H__) +#define __ZEBRA_NS_ID_H__ +#include "zebra.h" +#include "ns.h" + +extern ns_id_t zebra_ns_id_get(const char *netnspath); + +#endif /* __ZEBRA_NS_ID_H__ */ From e27dec3cf15d627cc26c07f40ded138b0bcc6a0a Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Wed, 13 Dec 2017 11:04:31 +0100 Subject: [PATCH 70/98] zebra: collect and get netnamespaces information upon zebra initialisation, and upon further netnamespace creation, the the netnamespaces are created and a vrf associated to the netnamespace is created. By convention, the name of the netns will be the same as the VRF. Add a stub routine that returns a fake ns identifier, in case netlink ( linux machines) is not available. Also, upon each newly discovered NETNS, a NSID id being generated, either by relying on kernel NSID feature, or by generating locally the NSID ( see previous commit for more information). Signed-off-by: Philippe Guibert --- zebra/main.c | 2 + zebra/subdir.am | 2 + zebra/zebra_netns_notify.c | 264 +++++++++++++++++++++++++++++++++++++ zebra/zebra_netns_notify.h | 29 ++++ zebra/zebra_ns.c | 3 + 5 files changed, 300 insertions(+) create mode 100644 zebra/zebra_netns_notify.c create mode 100644 zebra/zebra_netns_notify.h diff --git a/zebra/main.c b/zebra/main.c index 73e5f1290d..3353290816 100644 --- a/zebra/main.c +++ b/zebra/main.c @@ -47,6 +47,7 @@ #include "zebra/redistribute.h" #include "zebra/zebra_mpls.h" #include "zebra/label_manager.h" +#include "zebra/zebra_netns_notify.h" #define ZEBRA_PTM_SUPPORT @@ -140,6 +141,7 @@ static void sigint(void) vrf_terminate(); ns_walk_func(zebra_ns_disabled); + zebra_ns_notify_close(); access_list_reset(); prefix_list_reset(); diff --git a/zebra/subdir.am b/zebra/subdir.am index fcfb0cc203..bb7439c0f6 100644 --- a/zebra/subdir.am +++ b/zebra/subdir.am @@ -66,6 +66,7 @@ zebra_zebra_SOURCES = \ zebra/zebra_vxlan.c \ zebra/zserv.c \ zebra/zebra_netns_id.c \ + zebra/zebra_netns_notify.c \ # end zebra/zebra_vty_clippy.c: $(CLIPPY_DEPS) @@ -106,6 +107,7 @@ noinst_HEADERS += \ zebra/zebra_vxlan_private.h \ zebra/zserv.h \ zebra/zebra_netns_id.h \ + zebra/zebra_netns_notify.h \ # end zebra_zebra_irdp_la_SOURCES = \ diff --git a/zebra/zebra_netns_notify.c b/zebra/zebra_netns_notify.c new file mode 100644 index 0000000000..8940546f68 --- /dev/null +++ b/zebra/zebra_netns_notify.c @@ -0,0 +1,264 @@ +/* + * Zebra NS collector and notifier for Network NameSpaces + * Copyright (C) 2017 6WIND + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#ifdef HAVE_NETLINK +#ifdef HAVE_NETNS +#undef _GNU_SOURCE +#define _GNU_SOURCE + +#include +#endif +#include +#include +#include + +#include "thread.h" +#include "ns.h" +#include "command.h" +#include "memory.h" + +#include "zserv.h" +#include "zebra_memory.h" +#endif /* defined(HAVE_NETLINK) */ + +#include "zebra_netns_notify.h" +#include "zebra_netns_id.h" + +#ifdef HAVE_NETLINK + +/* upon creation of folder under /var/run/netns, + * wait that netns context is bound to + * that folder 10 seconds + */ +#define ZEBRA_NS_POLLING_INTERVAL_MSEC 1000 +#define ZEBRA_NS_POLLING_MAX_RETRIES 200 + +DEFINE_MTYPE_STATIC(ZEBRA, NETNS_MISC, "ZebraNetNSInfo") +static struct thread *zebra_netns_notify_current; + +struct zebra_netns_info { + const char *netnspath; + unsigned int retries; +}; + +static int zebra_ns_ready_read(struct thread *t); +static void zebra_ns_notify_create_context_from_entry_name(const char *name); +static int zebra_ns_continue_read(struct zebra_netns_info *zns_info, + int stop_retry); +static int zebra_ns_notify_read(struct thread *t); + +static void zebra_ns_notify_create_context_from_entry_name(const char *name) +{ + char *netnspath = ns_netns_pathname(NULL, name); + struct vrf *vrf; + int ret; + ns_id_t ns_id; + + if (netnspath == NULL) + return; + + if (vrf_handler_create(NULL, name, &vrf) != CMD_SUCCESS) { + zlog_warn("NS notify : failed to create VRF %s", name); + return; + } + if (zserv_privs.change(ZPRIVS_RAISE)) + zlog_err("Can't raise privileges"); + ns_id = zebra_ns_id_get(netnspath); + if (zserv_privs.change(ZPRIVS_LOWER)) + zlog_err("Can't lower privileges"); + ret = ns_handler_create(NULL, vrf, netnspath, ns_id); + if (ret != CMD_SUCCESS) { + zlog_warn("NS notify : failed to create NS %s", netnspath); + return; + } + zlog_info("NS notify : created VRF %s NS %s", + name, netnspath); +} + +static int zebra_ns_continue_read(struct zebra_netns_info *zns_info, + int stop_retry) +{ + void *ns_path_ptr = (void *)zns_info->netnspath; + + if (stop_retry) { + XFREE(MTYPE_NETNS_MISC, ns_path_ptr); + XFREE(MTYPE_NETNS_MISC, zns_info); + return 0; + } + thread_add_timer_msec(zebrad.master, zebra_ns_ready_read, + (void *)zns_info, + ZEBRA_NS_POLLING_INTERVAL_MSEC, NULL); + return 0; +} + +static int zebra_ns_ready_read(struct thread *t) +{ + struct zebra_netns_info *zns_info = THREAD_ARG(t); + const char *netnspath; + int err, stop_retry = 0; + + if (!zns_info) + return 0; + if (!zns_info->netnspath) { + XFREE(MTYPE_NETNS_MISC, zns_info); + return 0; + } + netnspath = zns_info->netnspath; + if (--zns_info->retries == 0) + stop_retry = 1; + if (zserv_privs.change(ZPRIVS_RAISE)) + zlog_err("Can't raise privileges"); + err = ns_switch_to_netns(netnspath); + if (zserv_privs.change(ZPRIVS_LOWER)) + zlog_err("Can't lower privileges"); + if (err < 0) + return zebra_ns_continue_read(zns_info, stop_retry); + + /* go back to default ns */ + if (zserv_privs.change(ZPRIVS_RAISE)) + zlog_err("Can't raise privileges"); + err = ns_switchback_to_initial(); + if (zserv_privs.change(ZPRIVS_LOWER)) + zlog_err("Can't lower privileges"); + if (err < 0) + return zebra_ns_continue_read(zns_info, stop_retry); + + /* success : close fd and create zns context */ + zebra_ns_notify_create_context_from_entry_name(basename(netnspath)); + return zebra_ns_continue_read(zns_info, 1); +} + +static int zebra_ns_notify_read(struct thread *t) +{ + int fd_monitor = THREAD_FD(t); + struct inotify_event *event; + char buf[BUFSIZ]; + ssize_t len; + + zebra_netns_notify_current = thread_add_read(zebrad.master, + zebra_ns_notify_read, + NULL, fd_monitor, NULL); + len = read(fd_monitor, buf, sizeof(buf)); + if (len < 0) { + zlog_warn("NS notify read: failed to read (%s)", + safe_strerror(errno)); + return 0; + } + for (event = (struct inotify_event *)buf; + (char *)event < &buf[len]; + event = (struct inotify_event *)((char *)event + + sizeof(*event) + event->len)) { + char *netnspath; + struct zebra_netns_info *netnsinfo; + + if (!(event->mask & IN_CREATE)) + continue; + netnspath = ns_netns_pathname(NULL, event->name); + if (!netnspath) + continue; + netnspath = XSTRDUP(MTYPE_NETNS_MISC, netnspath); + netnsinfo = XCALLOC(MTYPE_NETNS_MISC, + sizeof(struct zebra_netns_info)); + netnsinfo->retries = ZEBRA_NS_POLLING_MAX_RETRIES; + netnsinfo->netnspath = netnspath; + thread_add_timer_msec(zebrad.master, zebra_ns_ready_read, + (void *)netnsinfo, 0, NULL); + } + return 0; +} + +void zebra_ns_notify_parse(void) +{ + struct dirent *dent; + DIR *srcdir = opendir(NS_RUN_DIR); + + if (srcdir == NULL) { + zlog_warn("NS parsing init: failed to parse %s", NS_RUN_DIR); + return; + } + while ((dent = readdir(srcdir)) != NULL) { + struct stat st; + + if (strcmp(dent->d_name, ".") == 0 + || strcmp(dent->d_name, "..") == 0) + continue; + if (fstatat(dirfd(srcdir), dent->d_name, &st, 0) < 0) { + zlog_warn("NS parsing init: failed to parse entry %s", + dent->d_name); + continue; + } + if (S_ISDIR(st.st_mode)) { + zlog_warn("NS parsing init: %s is not a NS", + dent->d_name); + continue; + } + zebra_ns_notify_create_context_from_entry_name(dent->d_name); + } + closedir(srcdir); +} + +void zebra_ns_notify_init(void) +{ + int fd_monitor; + + zebra_netns_notify_current = NULL; + fd_monitor = inotify_init(); + if (fd_monitor < 0) { + zlog_warn("NS notify init: failed to initialize inotify (%s)", + safe_strerror(errno)); + } + if (inotify_add_watch(fd_monitor, NS_RUN_DIR, IN_CREATE) < 0) { + zlog_warn("NS notify watch: failed to add watch (%s)", + safe_strerror(errno)); + } + zebra_netns_notify_current = thread_add_read(zebrad.master, + zebra_ns_notify_read, + NULL, fd_monitor, NULL); +} + +void zebra_ns_notify_close(void) +{ + if (zebra_netns_notify_current == NULL) + return; + + int fd = 0; + + if (zebra_netns_notify_current->u.fd > 0) + fd = zebra_netns_notify_current->u.fd; + thread_cancel(zebra_netns_notify_current); + /* auto-removal of inotify items */ + if (fd > 0) + close(fd); +} + +#else +void zebra_ns_notify_parse(void) +{ +} + +void zebra_ns_notify_init(void) +{ +} + +void zebra_ns_notify_close(void) +{ +} +#endif /* !HAVE_NETLINK */ diff --git a/zebra/zebra_netns_notify.h b/zebra/zebra_netns_notify.h new file mode 100644 index 0000000000..0ced749ae8 --- /dev/null +++ b/zebra/zebra_netns_notify.h @@ -0,0 +1,29 @@ +/* + * Zebra NS collector and notifier for Network NameSpaces + * Copyright (C) 2017 6WIND + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _NETNS_NOTIFY_H +#define _NETNS_NOTIFY_H + +extern void zebra_ns_notify_init(void); +extern void zebra_ns_notify_parse(void); +extern void zebra_ns_notify_close(void); + +extern struct zebra_privs_t zserv_privs; + +#endif /* NETNS_NOTIFY_H */ diff --git a/zebra/zebra_ns.c b/zebra/zebra_ns.c index 02fc2b1844..50551c9b35 100644 --- a/zebra/zebra_ns.c +++ b/zebra/zebra_ns.c @@ -33,6 +33,7 @@ #include "rt.h" #include "zebra_vxlan.h" #include "debug.h" +#include "zebra_netns_notify.h" DEFINE_MTYPE(ZEBRA, ZEBRA_NS, "Zebra Name Space") @@ -249,6 +250,8 @@ int zebra_ns_init(void) ns_add_hook(NS_ENABLE_HOOK, zebra_ns_enabled); ns_add_hook(NS_DISABLE_HOOK, zebra_ns_disabled); ns_add_hook(NS_DELETE_HOOK, zebra_ns_delete); + zebra_ns_notify_parse(); + zebra_ns_notify_init(); } return 0; } From ec31f30d28b65a4820a44ac658e677840ed6f88b Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Tue, 16 Jan 2018 13:59:58 +0100 Subject: [PATCH 71/98] zebra: upon startup, a NSID is assigned to default netns when the netns backend is selected for VRF, the default VRF is being assigned a NSID. This avoids the need to handle the case where if the incoming NSID was 0 for a non default VRF, then a specific handling had to be done to keep 0 value for default VRF. In most cases, as the first NETNS to get a NSID will be the default VRF, most probably the default VRF will be assigned to 0, while the other ones will have their value incremented. On some cases, where the NSID is already assigned for NETNS, including default VRF, then the default VRF value will be the one derived from the NSID of default VRF, thus keeping consistency between VRF IDs and NETNS IDs. Default NS is attempted to be created. Actually, some VMs may have the netns feature, but the NS initialisation fails because that folder is not present. Signed-off-by: Philippe Guibert --- lib/ns.c | 35 ++++++++++++++++++++++------------- lib/ns.h | 14 ++++++++++++-- lib/vrf.c | 15 +++++++++++++++ lib/vrf.h | 10 ++++++---- zebra/zebra_netns_id.c | 41 +++++++++++++++++++++++++++++++++++++++++ zebra/zebra_netns_id.h | 1 + zebra/zebra_ns.c | 12 +++++++++++- 7 files changed, 108 insertions(+), 20 deletions(-) diff --git a/lib/ns.c b/lib/ns.c index 9aa3509923..17d70a12fe 100644 --- a/lib/ns.c +++ b/lib/ns.c @@ -51,6 +51,7 @@ RB_GENERATE(ns_head, ns, entry, ns_compare) struct ns_head ns_tree = RB_INITIALIZER(&ns_tree); +static struct ns *default_ns; static int ns_current_ns_fd; static int ns_default_ns_fd; @@ -71,16 +72,12 @@ static inline int setns(int fd, int nstype) #endif /* HAVE_SETNS */ #ifdef HAVE_NETNS - -#define NS_DEFAULT_NAME "/proc/self/ns/net" static int have_netns_enabled = -1; - -#else /* !HAVE_NETNS */ - -#define NS_DEFAULT_NAME "Default-logical-router" - #endif /* HAVE_NETNS */ +/* default NS ID value used when VRF backend is not NETNS */ +#define NS_DEFAULT_INTERNAL 0 + static int have_netns(void) { #ifdef HAVE_NETNS @@ -625,24 +622,28 @@ void ns_init(void) } #endif /* HAVE_NETNS */ ns_default_ns_fd = -1; + default_ns = NULL; } /* Initialize NS module. */ -void ns_init_zebra(void) +void ns_init_zebra(ns_id_t default_ns_id) { - struct ns *default_ns; + int fd; ns_init(); - /* The default NS always exists. */ - default_ns = ns_get(NS_DEFAULT); - ns_current_ns_fd = -1; + default_ns = ns_get(default_ns_id); if (!default_ns) { zlog_err("ns_init: failed to create the default NS!"); exit(1); } - + if (have_netns()) { + fd = open(NS_DEFAULT_NAME, O_RDONLY); + default_ns->fd = fd; + } + ns_current_ns_fd = -1; /* Set the default NS name. */ default_ns->name = XSTRDUP(MTYPE_NS_NAME, NS_DEFAULT_NAME); + zlog_info("ns_init: default NSID is %u", default_ns->ns_id); /* Enable the default NS. */ if (!ns_enable(default_ns)) { @@ -739,3 +740,11 @@ int ns_socket(int domain, int type, int protocol, ns_id_t ns_id) return ret; } + +ns_id_t ns_get_default_id(void) +{ + if (default_ns) + return default_ns->ns_id; + return NS_UNKNOWN; +} + diff --git a/lib/ns.h b/lib/ns.h index 73482d4d56..44257ab0c0 100644 --- a/lib/ns.h +++ b/lib/ns.h @@ -30,12 +30,17 @@ typedef u_int32_t ns_id_t; /* the default NS ID */ -#define NS_DEFAULT 0 #define NS_UNKNOWN UINT32_MAX /* Default netns directory (Linux) */ #define NS_RUN_DIR "/var/run/netns" +#ifdef HAVE_NETNS +#define NS_DEFAULT_NAME "/proc/self/ns/net" +#else /* !HAVE_NETNS */ +#define NS_DEFAULT_NAME "Default-logical-router" +#endif /* HAVE_NETNS */ + struct ns { RB_ENTRY(ns) entry; @@ -85,7 +90,7 @@ extern void ns_add_hook(int type, int (*)(struct ns *)); * NS initializer/destructor */ extern void ns_init(void); -extern void ns_init_zebra(void); +extern void ns_init_zebra(ns_id_t ns_id); extern void ns_terminate(void); /* @@ -101,10 +106,15 @@ extern char *ns_netns_pathname(struct vty *vty, const char *name); extern void *ns_info_lookup(ns_id_t ns_id); extern void ns_walk_func(int (*func)(struct ns *)); extern const char *ns_get_name(struct ns *ns); +extern ns_id_t ns_get_default_id(void); /* API that can be used by all daemons */ extern int ns_switchback_to_initial(void); extern int ns_switch_to_netns(const char *netns_name); extern void ns_init(void); + +/* The default NS ID */ +#define NS_DEFAULT ns_get_default_id() + #endif /*_ZEBRA_NS_H*/ diff --git a/lib/vrf.c b/lib/vrf.c index 7871052352..f4dc237eb3 100644 --- a/lib/vrf.c +++ b/lib/vrf.c @@ -34,6 +34,9 @@ #include "command.h" #include "ns.h" +/* default VRF ID value used when VRF backend is not NETNS */ +#define VRF_DEFAULT_INTERNAL 0 + DEFINE_MTYPE_STATIC(LIB, VRF, "VRF") DEFINE_MTYPE_STATIC(LIB, VRF_BITMAP, "VRF bit-map") @@ -646,3 +649,15 @@ void vrf_cmd_init(int (*writefunc)(struct vty *vty)) install_default(VRF_NODE); ns_cmd_init(); } + +vrf_id_t vrf_get_default_id(void) +{ + struct vrf *vrf = vrf_lookup_by_name(VRF_DEFAULT_NAME); + + if (vrf) + return vrf->vrf_id; + if (vrf_is_backend_netns()) + return ns_get_default_id(); + else + return VRF_DEFAULT_INTERNAL; +} diff --git a/lib/vrf.h b/lib/vrf.h index 4bdc183b5c..9553d43808 100644 --- a/lib/vrf.h +++ b/lib/vrf.h @@ -27,11 +27,7 @@ #include "qobj.h" #include "vty.h" -/* The default NS ID */ -#define NS_DEFAULT 0 - /* The default VRF ID */ -#define VRF_DEFAULT 0 #define VRF_UNKNOWN UINT32_MAX /* Pending: May need to refine this. */ @@ -208,6 +204,7 @@ extern void vrf_cmd_init(int (*writefunc)(struct vty *vty)); /* * VRF utilities */ +extern vrf_id_t vrf_get_default_id(void); /* Create a socket serving for the given VRF */ extern int vrf_socket(int, int, int, vrf_id_t); @@ -236,4 +233,9 @@ extern int vrf_enable(struct vrf *vrf); * VRF Debugging */ extern void vrf_install_commands(void); + + +/* The default VRF ID */ +#define VRF_DEFAULT vrf_get_default_id() + #endif /*_ZEBRA_VRF_H*/ diff --git a/zebra/zebra_netns_id.c b/zebra/zebra_netns_id.c index c5e792bd77..966d6ed0d2 100644 --- a/zebra/zebra_netns_id.c +++ b/zebra/zebra_netns_id.c @@ -20,6 +20,7 @@ #include #include "ns.h" +#include "vrf.h" #include "log.h" #if defined(HAVE_NETLINK) @@ -35,6 +36,9 @@ #include "zebra_netns_id.h" +/* default NS ID value used when VRF backend is not NETNS */ +#define NS_DEFAULT_INTERNAL 0 + /* in case NEWNSID not available, the NSID will be locally obtained */ #define NS_BASE_NSID 0 @@ -312,3 +316,40 @@ ns_id_t zebra_ns_id_get(const char *netnspath) return zebra_ns_id_get_fallback(netnspath); } #endif /* ! defined(HAVE_NETLINK) */ + +#ifdef HAVE_NETNS +static void zebra_ns_create_netns_directory(void) +{ + /* check that /var/run/netns is created */ + /* S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH */ + if (mkdir(NS_RUN_DIR, 0755)) { + if (errno != EEXIST) { + zlog_warn("NS check: failed to access %s", NS_RUN_DIR); + return; + } + } +} +#endif + +ns_id_t zebra_ns_id_get_default(void) +{ +#ifdef HAVE_NETNS + int fd; +#endif /* !HAVE_NETNS */ + +#ifdef HAVE_NETNS + if (vrf_is_backend_netns()) + zebra_ns_create_netns_directory(); + fd = open(NS_DEFAULT_NAME, O_RDONLY); + + if (fd == -1) + return NS_DEFAULT_INTERNAL; + if (!vrf_is_backend_netns()) + return NS_DEFAULT_INTERNAL; + close(fd); + return zebra_ns_id_get((char *)NS_DEFAULT_NAME); +#else /* HAVE_NETNS */ + return NS_DEFAULT_INTERNAL; +#endif /* !HAVE_NETNS */ +} + diff --git a/zebra/zebra_netns_id.h b/zebra/zebra_netns_id.h index 18fdf50cf1..d6530e6694 100644 --- a/zebra/zebra_netns_id.h +++ b/zebra/zebra_netns_id.h @@ -21,5 +21,6 @@ #include "ns.h" extern ns_id_t zebra_ns_id_get(const char *netnspath); +extern ns_id_t zebra_ns_id_get_default(void); #endif /* __ZEBRA_NS_ID_H__ */ diff --git a/zebra/zebra_ns.c b/zebra/zebra_ns.c index 50551c9b35..da5b22def2 100644 --- a/zebra/zebra_ns.c +++ b/zebra/zebra_ns.c @@ -34,6 +34,9 @@ #include "zebra_vxlan.h" #include "debug.h" #include "zebra_netns_notify.h" +#include "zebra_netns_id.h" + +extern struct zebra_privs_t zserv_privs; DEFINE_MTYPE(ZEBRA, ZEBRA_NS, "Zebra Name Space") @@ -229,9 +232,16 @@ int zebra_ns_disable(ns_id_t ns_id, void **info) int zebra_ns_init(void) { + ns_id_t ns_id; + dzns = zebra_ns_alloc(); - ns_init_zebra(); + if (zserv_privs.change(ZPRIVS_RAISE)) + zlog_err("Can't raise privileges"); + ns_id = zebra_ns_id_get_default(); + if (zserv_privs.change(ZPRIVS_LOWER)) + zlog_err("Can't lower privileges"); + ns_init_zebra(ns_id); ns_init(); From 0439cb9d9e15053181f0bf13e49c39d74ca37e50 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Mon, 22 Jan 2018 13:46:20 +0100 Subject: [PATCH 72/98] zebra: fix initialised vrf_id value never read this is a static analysis performed by c-lang scan-build tool that demonstrated this issue. This commit is handling the fix. Signed-off-by: Philippe Guibert --- zebra/interface.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zebra/interface.c b/zebra/interface.c index 74ffdee31f..e919d9f08f 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -1414,7 +1414,7 @@ DEFUN (show_interface_name_vrf, int idx_ifname = 2; int idx_name = 4; struct interface *ifp; - vrf_id_t vrf_id = VRF_DEFAULT; + vrf_id_t vrf_id; interface_update_stats(); From e9e9b1150f0cea709ffffba1121ae216fbdbc2ca Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Tue, 19 Dec 2017 12:44:44 +0100 Subject: [PATCH 73/98] lib: create interface even if name is the same For supporting vrf based on namespaces, it is possible that an interface with the same index is present. This is the case for loopback interfaces. For that, for each query, if the interface is not found , matching the vrf identifier, then a new interface is created, when the backens for VRF is NETNS. Signed-off-by: Philippe Guibert --- lib/if.c | 46 ++++++++++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/lib/if.c b/lib/if.c index 12d123a8fa..3a83de46ae 100644 --- a/lib/if.c +++ b/lib/if.c @@ -384,29 +384,35 @@ struct interface *if_get_by_name(const char *name, vrf_id_t vrf_id, int vty) { struct interface *ifp; + ifp = if_lookup_by_name(name, vrf_id); + if (ifp) + return ifp; + /* Not Found on same VRF. If the interface command + * was entered in vty without a VRF (passed as VRF_DEFAULT), + * accept the ifp we found. If a vrf was entered and there is + * a mismatch, reject it if from vty. + */ ifp = if_lookup_by_name_all_vrf(name); - if (ifp) { - if (ifp->vrf_id == vrf_id) + if (!ifp) + return if_create(name, vrf_id); + if (vty) { + if (vrf_id == VRF_DEFAULT) return ifp; - - /* Found a match on a different VRF. If the interface command - * was entered in vty without a VRF (passed as VRF_DEFAULT), - * accept the ifp we found. If a vrf was entered and there is - * a mismatch, reject it if from vty. If it came from the kernel - * or by way of zclient, believe it and update the ifp - * accordingly. - */ - if (vty) { - if (vrf_id == VRF_DEFAULT) - return ifp; - return NULL; - } else { - if_update_to_new_vrf(ifp, vrf_id); - return ifp; - } + return NULL; } - - return if_create(name, vrf_id); + /* if vrf backend uses NETNS, then + * this should not be considered as an update + * then create the new interface + */ + if (ifp->vrf_id != vrf_id && + vrf_is_mapped_on_netns(vrf_id)) + return if_create(name, vrf_id); + /* If it came from the kernel + * or by way of zclient, believe it and update + * the ifp accordingly. + */ + if_update_to_new_vrf(ifp, vrf_id); + return ifp; } void if_set_index(struct interface *ifp, ifindex_t ifindex) From 7d206035d9a6dd1e66d5349f76d9c614fbac65f6 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Mon, 22 Jan 2018 16:06:58 +0100 Subject: [PATCH 74/98] ospfd: fix static analysis with variable initialised never read the vrf identifier in the ospf_vrf_enable routine is never read, then does not need to be initialised. Signed-off-by: Philippe Guibert --- ospfd/ospfd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ospfd/ospfd.c b/ospfd/ospfd.c index 86a3293d71..68c682c6c4 100644 --- a/ospfd/ospfd.c +++ b/ospfd/ospfd.c @@ -2049,7 +2049,7 @@ static int ospf_vrf_delete(struct vrf *vrf) static int ospf_vrf_enable(struct vrf *vrf) { struct ospf *ospf = NULL; - vrf_id_t old_vrf_id = VRF_DEFAULT; + vrf_id_t old_vrf_id; if (IS_DEBUG_OSPF_EVENT) zlog_debug("%s: VRF %s id %u enabled", From 40289934f1b07acf934f26aec63ed523e6889a1d Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Wed, 24 Jan 2018 19:06:06 +0100 Subject: [PATCH 75/98] zebra: fix assert mpls when terminating zebra The assert appears in zebra_mpls.c when checking default zebra_vrf. It appears that when the mpls entries are flushed, it gets the default vrf which is already flushed by vrf_terminate() function. In order to avoid that assert to trigger a crash, the mpls flush is called before vrf termination. Signed-off-by: Philippe Guibert --- zebra/main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zebra/main.c b/zebra/main.c index 3353290816..9cad913f4b 100644 --- a/zebra/main.c +++ b/zebra/main.c @@ -138,6 +138,8 @@ static void sigint(void) if (zvrf) SET_FLAG(zvrf->flags, ZEBRA_VRF_RETAIN); } + if (zebrad.lsp_process_q) + work_queue_free(zebrad.lsp_process_q); vrf_terminate(); ns_walk_func(zebra_ns_disabled); @@ -149,8 +151,6 @@ static void sigint(void) list_delete_and_null(&zebrad.client_list); work_queue_free(zebrad.ribq); - if (zebrad.lsp_process_q) - work_queue_free(zebrad.lsp_process_q); meta_queue_free(zebrad.mq); frr_fini(); From 2e0d2b3d9c4f2681d00ec2607319f3bb8078e31d Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Fri, 26 Jan 2018 12:28:27 +0100 Subject: [PATCH 76/98] lib: add two APIs to handle socket operations with VRF NETNS The vrf_sockunion_socket() wraps sockunion_socket() with vrf_id as additional parameter. The creation of socket forces the user to transparently move to new NETNS for doing the operation. The vrf_getaddr_info() wraps getaddr_info() with vrf_id as additional parameter. That API relies on the underlying system. Then there may be need to switch to an other netns in that case too. Also, the vrf_socket() implementation is simplified. Signed-off-by: Philippe Guibert --- lib/vrf.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++-- lib/vrf.h | 9 +++++++++ 2 files changed, 58 insertions(+), 2 deletions(-) diff --git a/lib/vrf.c b/lib/vrf.c index f4dc237eb3..ade1895bd4 100644 --- a/lib/vrf.c +++ b/lib/vrf.c @@ -490,10 +490,19 @@ void vrf_terminate(void) /* Create a socket for the VRF. */ int vrf_socket(int domain, int type, int protocol, vrf_id_t vrf_id) { - int ret = -1; + int ret, save_errno, ret2; + ret = vrf_switch_to_netns(vrf_id); + if (ret < 0) + zlog_err("%s: Can't switch to VRF %u (%s)", + __func__, vrf_id, safe_strerror(errno)); ret = socket(domain, type, protocol); - + save_errno = errno; + ret2 = vrf_switchback_to_initial(); + if (ret2 < 0) + zlog_err("%s: Can't switchback from VRF %u (%s)", + __func__, vrf_id, safe_strerror(errno)); + errno = save_errno; return ret; } @@ -661,3 +670,41 @@ vrf_id_t vrf_get_default_id(void) else return VRF_DEFAULT_INTERNAL; } + +int vrf_getaddrinfo(const char *node, const char *service, + const struct addrinfo *hints, + struct addrinfo **res, vrf_id_t vrf_id) +{ + int ret, ret2, save_errno; + + ret = vrf_switch_to_netns(vrf_id); + if (ret < 0) + zlog_err("%s: Can't switch to VRF %u (%s)", + __func__, vrf_id, safe_strerror(errno)); + ret = getaddrinfo(node, service, hints, res); + save_errno = errno; + ret2 = vrf_switchback_to_initial(); + if (ret2 < 0) + zlog_err("%s: Can't switchback from VRF %u (%s)", + __func__, vrf_id, safe_strerror(errno)); + errno = save_errno; + return ret; +} + +int vrf_sockunion_socket(const union sockunion *su, vrf_id_t vrf_id) +{ + int ret, save_errno, ret2; + + ret = vrf_switch_to_netns(vrf_id); + if (ret < 0) + zlog_err("%s: Can't switch to VRF %u (%s)", + __func__, vrf_id, safe_strerror(errno)); + ret = sockunion_socket(su); + save_errno = errno; + ret2 = vrf_switchback_to_initial(); + if (ret2 < 0) + zlog_err("%s: Can't switchback from VRF %u (%s)", + __func__, vrf_id, safe_strerror(errno)); + errno = save_errno; + return ret; +} diff --git a/lib/vrf.h b/lib/vrf.h index 9553d43808..08c53484ee 100644 --- a/lib/vrf.h +++ b/lib/vrf.h @@ -222,6 +222,15 @@ int vrf_is_mapped_on_netns(vrf_id_t vrf_id); extern int vrf_switch_to_netns(vrf_id_t vrf_id); extern int vrf_switchback_to_initial(void); +/* VRF ioctl operations */ +extern int vrf_getaddrinfo(const char *node, const char *service, + const struct addrinfo *hints, + struct addrinfo **res, vrf_id_t vrf_id); +extern int vrf_sockunion_socket(const union sockunion *su, vrf_id_t vrf_id); +/* VRF switch from NETNS */ +extern int vrf_switch_to_netns(vrf_id_t vrf_id); +extern int vrf_switchback_to_initial(void); + /* used by NS when vrf backend is NS. * Notify a change in the VRF ID of the VRF */ From 61cf4b371572363106c489b6f716b2b111d68ef9 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Wed, 20 Dec 2017 12:37:18 +0100 Subject: [PATCH 77/98] bgpd: bgp support for netns The change contained in this commit does the following: - discovery of vrf id from zebra daemon, and adaptation of bgp contexts with BGP. The list of network addresses contain a reference to the bgp context supporting the vrf. The bgp context contains a vrf pointer that gives information about the netns path in case the vrf is a netns path. Only some contexts are impacted, namely socket creation, and retrieval of local IP settings. ( this requires vrf identifier). Signed-off-by: Philippe Guibert --- bgpd/bgp_fsm.c | 9 +++++++ bgpd/bgp_main.c | 3 +++ bgpd/bgp_network.c | 58 +++++++++++++++++++++++++++++++++++----------- bgpd/bgp_network.h | 3 ++- bgpd/bgpd.c | 9 +++---- 5 files changed, 63 insertions(+), 19 deletions(-) diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c index de453de0c8..79f4b1c91a 100644 --- a/bgpd/bgp_fsm.c +++ b/bgpd/bgp_fsm.c @@ -1376,6 +1376,15 @@ int bgp_start(struct peer *peer) return 0; } + if (peer->bgp && + peer->bgp->vrf_id == VRF_UNKNOWN) { + if (bgp_debug_neighbor_events(peer)) + zlog_err( + "%s [FSM] In a VRF that is not initialised yet", + peer->host); + return -1; + } + /* Register to be notified on peer up */ if (peer->sort == BGP_PEER_EBGP && peer->ttl == 1 && !CHECK_FLAG(peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK) diff --git a/bgpd/bgp_main.c b/bgpd/bgp_main.c index 0508f4846d..b5448b694e 100644 --- a/bgpd/bgp_main.c +++ b/bgpd/bgp_main.c @@ -41,6 +41,7 @@ #include "vrf.h" #include "bfd.h" #include "libfrr.h" +#include "ns.h" #include "bgpd/bgpd.h" #include "bgpd/bgp_attr.h" @@ -57,6 +58,7 @@ #include "bgpd/bgp_zebra.h" #include "bgpd/bgp_packet.h" #include "bgpd/bgp_keepalives.h" +#include "bgpd/bgp_network.h" #ifdef ENABLE_BGP_VNC #include "bgpd/rfapi/rfapi_backend.h" @@ -297,6 +299,7 @@ static int bgp_vrf_disable(struct vrf *vrf) static void bgp_vrf_init(void) { + ns_init(); vrf_init(bgp_vrf_new, bgp_vrf_enable, bgp_vrf_disable, bgp_vrf_delete); } diff --git a/bgpd/bgp_network.c b/bgpd/bgp_network.c index bf39cbe1fc..e3cca63905 100644 --- a/bgpd/bgp_network.c +++ b/bgpd/bgp_network.c @@ -34,6 +34,7 @@ #include "queue.h" #include "hash.h" #include "filter.h" +#include "ns.h" #include "bgpd/bgpd.h" #include "bgpd/bgp_open.h" @@ -51,6 +52,7 @@ struct bgp_listener { int fd; union sockunion su; struct thread *thread; + struct bgp *bgp; }; /* @@ -284,6 +286,7 @@ static int bgp_accept(struct thread *thread) return -1; } listener->thread = NULL; + thread_add_read(bm->master, bgp_accept, listener, accept_sock, &listener->thread); @@ -296,8 +299,13 @@ static int bgp_accept(struct thread *thread) } set_nonblocking(bgp_sock); - /* Obtain BGP instance this connection is meant for. */ - if (bgp_get_instance_for_inc_conn(bgp_sock, &bgp)) { + /* Obtain BGP instance this connection is meant for. + * - if it is a VRF netns sock, then BGP is in listener structure + * - otherwise, the bgp instance need to be demultiplexed + */ + if (listener->bgp) + bgp = listener->bgp; + else if (bgp_get_instance_for_inc_conn(bgp_sock, &bgp)) { if (bgp_debug_neighbor_events(NULL)) zlog_debug( "[Event] Could not get instance for incoming conn from %s", @@ -442,10 +450,12 @@ static int bgp_bind(struct peer *peer) int myerrno; char *name = NULL; - /* If not bound to an interface or part of a VRF, we don't care. */ - if (!peer->bgp->vrf_id && !peer->ifname && !peer->conf_if) + /* If not bound to an interface or part of a VRF lite, we don't care. */ + if ((peer->bgp->vrf_id == VRF_DEFAULT) && + !peer->ifname && !peer->conf_if) + return 0; + if (vrf_is_mapped_on_netns(peer->bgp->vrf_id)) return 0; - if (peer->su.sa.sa_family != AF_INET && peer->su.sa.sa_family != AF_INET6) return 0; // unexpected @@ -558,8 +568,12 @@ int bgp_connect(struct peer *peer) zlog_debug("Peer address not learnt: Returning from connect"); return 0; } + if (bgpd_privs.change(ZPRIVS_RAISE)) + zlog_err("Can't raise privileges"); /* Make socket for the peer. */ - peer->fd = sockunion_socket(&peer->su); + peer->fd = vrf_sockunion_socket(&peer->su, peer->bgp->vrf_id); + if (bgpd_privs.change(ZPRIVS_LOWER)) + zlog_err("Can't lower privileges"); if (peer->fd < 0) return -1; @@ -642,12 +656,12 @@ int bgp_getsockname(struct peer *peer) return -1; #endif } - return 0; } -static int bgp_listener(int sock, struct sockaddr *sa, socklen_t salen) +static int bgp_listener(int sock, struct sockaddr *sa, socklen_t salen, + struct bgp *bgp) { struct bgp_listener *listener; int ret, en; @@ -683,8 +697,14 @@ static int bgp_listener(int sock, struct sockaddr *sa, socklen_t salen) return ret; } - listener = XMALLOC(MTYPE_BGP_LISTENER, sizeof(*listener)); + listener = XCALLOC(MTYPE_BGP_LISTENER, sizeof(*listener)); listener->fd = sock; + + /* this socket needs a change of ns. record bgp back pointer */ + if (bgp->vrf_id != VRF_DEFAULT && + vrf_is_mapped_on_netns(bgp->vrf_id)) + listener->bgp = bgp; + memcpy(&listener->su, sa, salen); listener->thread = NULL; thread_add_read(bm->master, bgp_accept, listener, sock, @@ -695,7 +715,7 @@ static int bgp_listener(int sock, struct sockaddr *sa, socklen_t salen) } /* IPv6 supported version of BGP server socket setup. */ -int bgp_socket(unsigned short port, const char *address) +int bgp_socket(struct bgp *bgp, unsigned short port, const char *address) { struct addrinfo *ainfo; struct addrinfo *ainfo_save; @@ -710,7 +730,12 @@ int bgp_socket(unsigned short port, const char *address) snprintf(port_str, sizeof(port_str), "%d", port); port_str[sizeof(port_str) - 1] = '\0'; - ret = getaddrinfo(address, port_str, &req, &ainfo_save); + if (bgpd_privs.change(ZPRIVS_RAISE)) + zlog_err("Can't raise privileges"); + ret = vrf_getaddrinfo(address, port_str, &req, + &ainfo_save, bgp->vrf_id); + if (bgpd_privs.change(ZPRIVS_LOWER)) + zlog_err("Can't lower privileges"); if (ret != 0) { zlog_err("getaddrinfo: %s", gai_strerror(ret)); return -1; @@ -723,8 +748,12 @@ int bgp_socket(unsigned short port, const char *address) if (ainfo->ai_family != AF_INET && ainfo->ai_family != AF_INET6) continue; - sock = socket(ainfo->ai_family, ainfo->ai_socktype, - ainfo->ai_protocol); + if (bgpd_privs.change(ZPRIVS_RAISE)) + zlog_err("Can't raise privileges"); + sock = vrf_socket(ainfo->ai_family, ainfo->ai_socktype, + ainfo->ai_protocol, bgp->vrf_id); + if (bgpd_privs.change(ZPRIVS_LOWER)) + zlog_err("Can't lower privileges"); if (sock < 0) { zlog_err("socket: %s", safe_strerror(errno)); continue; @@ -734,7 +763,8 @@ int bgp_socket(unsigned short port, const char *address) * ttl=255 */ sockopt_ttl(ainfo->ai_family, sock, MAXTTL); - ret = bgp_listener(sock, ainfo->ai_addr, ainfo->ai_addrlen); + ret = bgp_listener(sock, ainfo->ai_addr, + ainfo->ai_addrlen, bgp); if (ret == 0) ++count; else diff --git a/bgpd/bgp_network.h b/bgpd/bgp_network.h index 75ff1305c2..5691b73e22 100644 --- a/bgpd/bgp_network.h +++ b/bgpd/bgp_network.h @@ -23,7 +23,8 @@ #define BGP_SOCKET_SNDBUF_SIZE 65536 -extern int bgp_socket(unsigned short, const char *); +extern int bgp_socket(struct bgp *bgp, unsigned short port, + const char *address); extern void bgp_close(void); extern int bgp_connect(struct peer *); extern int bgp_getsockname(struct peer *); diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 78e748fb6c..1d44eb76cd 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -3035,7 +3035,7 @@ int bgp_get(struct bgp **bgp_val, as_t *as, const char *name, /* Create BGP server socket, if first instance. */ if (list_isempty(bm->bgp) && !bgp_option_check(BGP_OPT_NO_LISTEN)) { - if (bgp_socket(bm->port, bm->address) < 0) + if (bgp_socket(bgp, bm->port, bm->address) < 0) return BGP_ERR_INVALID_VALUE; } @@ -3337,11 +3337,12 @@ struct peer *peer_lookup(struct bgp *bgp, union sockunion *su) struct listnode *bgpnode, *nbgpnode; for (ALL_LIST_ELEMENTS(bm->bgp, bgpnode, nbgpnode, bgp)) { - /* Skip VRFs, this function will not be invoked without - * an instance + /* Skip VRFs Lite only, this function will not be + * invoked without an instance * when examining VRFs. */ - if (bgp->inst_type == BGP_INSTANCE_TYPE_VRF) + if ((bgp->inst_type == BGP_INSTANCE_TYPE_VRF) && + !vrf_is_mapped_on_netns(bgp->vrf_id)) continue; peer = hash_lookup(bgp->peerhash, &tmp_peer); From e5619c289af97beefefa36067afdef542e86bb0d Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Fri, 26 Jan 2018 12:25:34 +0100 Subject: [PATCH 78/98] bgpd: server socket is created for all enabled VRF Upon creation of BGP instances, server socket may or may not be created. In the case of VRF instances, if the VRF backend relies on NETNS, then a new server socket will be created for each BGP VRF instance. If the VRF backend relies on VRF LITE, then only one server socket will be enough. Moreover, At startup, with BGP VRF configuration, a server socket may not be created if VRF is not the default one or VRF is not recognized yet. Signed-off-by: Philippe Guibert --- bgpd/bgp_main.c | 2 + bgpd/bgp_network.c | 28 ++++++++++++ bgpd/bgp_network.h | 1 + bgpd/bgpd.c | 111 +++++++++++++++++++++++++++++++++++++++------ bgpd/bgpd.h | 3 ++ 5 files changed, 132 insertions(+), 13 deletions(-) diff --git a/bgpd/bgp_main.c b/bgpd/bgp_main.c index b5448b694e..717fe09762 100644 --- a/bgpd/bgp_main.c +++ b/bgpd/bgp_main.c @@ -261,6 +261,7 @@ static int bgp_vrf_enable(struct vrf *vrf) /* We have instance configured, link to VRF and make it "up". */ bgp_vrf_link(bgp, vrf); + bgp_handle_socket(bgp, vrf, old_vrf_id, true); /* Update any redistribute vrf bitmaps if the vrf_id changed */ if (old_vrf_id != bgp->vrf_id) bgp_update_redist_vrf_bitmaps(bgp, old_vrf_id); @@ -284,6 +285,7 @@ static int bgp_vrf_disable(struct vrf *vrf) bgp = bgp_lookup_by_name(vrf->name); if (bgp) { old_vrf_id = bgp->vrf_id; + bgp_handle_socket(bgp, vrf, VRF_UNKNOWN, false); /* We have instance configured, unlink from VRF and make it * "down". */ bgp_vrf_unlink(bgp, vrf); diff --git a/bgpd/bgp_network.c b/bgpd/bgp_network.c index e3cca63905..59c59f924e 100644 --- a/bgpd/bgp_network.c +++ b/bgpd/bgp_network.c @@ -781,6 +781,32 @@ int bgp_socket(struct bgp *bgp, unsigned short port, const char *address) return 0; } +/* this function closes vrf socket + * this should be called only for vrf socket with netns backend + */ +void bgp_close_vrf_socket(struct bgp *bgp) +{ + struct listnode *node, *next; + struct bgp_listener *listener; + + if (!bgp) + return; + + if (bm->listen_sockets == NULL) + return; + + for (ALL_LIST_ELEMENTS(bm->listen_sockets, node, next, listener)) { + if (listener->bgp == bgp) { + thread_cancel(listener->thread); + close(listener->fd); + listnode_delete(bm->listen_sockets, listener); + XFREE(MTYPE_BGP_LISTENER, listener); + } + } +} + +/* this function closes main socket + */ void bgp_close(void) { struct listnode *node, *next; @@ -790,6 +816,8 @@ void bgp_close(void) return; for (ALL_LIST_ELEMENTS(bm->listen_sockets, node, next, listener)) { + if (listener->bgp) + continue; thread_cancel(listener->thread); close(listener->fd); listnode_delete(bm->listen_sockets, listener); diff --git a/bgpd/bgp_network.h b/bgpd/bgp_network.h index 5691b73e22..f18484e000 100644 --- a/bgpd/bgp_network.h +++ b/bgpd/bgp_network.h @@ -25,6 +25,7 @@ extern int bgp_socket(struct bgp *bgp, unsigned short port, const char *address); +extern void bgp_close_vrf_socket(struct bgp *bgp); extern void bgp_close(void); extern int bgp_connect(struct peer *); extern int bgp_getsockname(struct peer *); diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 1d44eb76cd..07b9df31f2 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -101,6 +101,42 @@ static void bgp_if_finish(struct bgp *bgp); extern struct zclient *zclient; +/* handle main socket creation or deletion */ +static int bgp_check_main_socket(bool create, struct bgp *bgp) +{ + static int bgp_server_main_created; + struct listnode *bgpnode, *nbgpnode; + struct bgp *bgp_temp; + + if (bgp->inst_type == BGP_INSTANCE_TYPE_VRF) + return 0; + if (create == true) { + if (bgp_server_main_created) + return 0; + if (bgp_socket(bgp, bm->port, bm->address) < 0) + return BGP_ERR_INVALID_VALUE; + bgp_server_main_created = 1; + return 0; + } + if (!bgp_server_main_created) + return 0; + /* only delete socket on some cases */ + for (ALL_LIST_ELEMENTS(bm->bgp, bgpnode, nbgpnode, bgp_temp)) { + /* do not count with current bgp */ + if (bgp_temp == bgp) + continue; + /* if other instance non VRF, do not delete socket */ + if (bgp_temp->inst_type == BGP_INSTANCE_TYPE_DEFAULT) + return 0; + /* vrf lite, do not delete socket */ + if (!vrf_is_mapped_on_netns(bgp_temp->vrf_id)) + return 0; + } + bgp_close(); + bgp_server_main_created = 0; + return 0; +} + void bgp_session_reset(struct peer *peer) { if (peer->doppelganger && (peer->doppelganger->status != Deleted) @@ -2981,11 +3017,67 @@ struct bgp *bgp_lookup_by_vrf_id(vrf_id_t vrf_id) return (vrf->info) ? (struct bgp *)vrf->info : NULL; } +/* handle socket creation or deletion, if necessary + * this is called for all new BGP instances + */ +int bgp_handle_socket(struct bgp *bgp, struct vrf *vrf, + vrf_id_t old_vrf_id, bool create) +{ + int ret = 0; + + /* Create BGP server socket, if listen mode not disabled */ + if (!bgp || bgp_option_check(BGP_OPT_NO_LISTEN)) + return 0; + if (bgp->name + && bgp->inst_type == BGP_INSTANCE_TYPE_VRF + && vrf) { + /* + * suppress vrf socket + */ + if (create == FALSE) { + if (vrf_is_mapped_on_netns(vrf->vrf_id)) + bgp_close_vrf_socket(bgp); + else + ret = bgp_check_main_socket(create, bgp); + return ret; + } + /* do nothing + * if vrf_id did not change + */ + if (vrf->vrf_id == old_vrf_id) + return 0; + if (old_vrf_id != VRF_UNKNOWN) { + /* look for old socket. close it. */ + bgp_close_vrf_socket(bgp); + } + /* if backend is not yet identified ( VRF_UNKNOWN) then + * creation will be done later + */ + if (vrf->vrf_id == VRF_UNKNOWN) + return 0; + /* if BGP VRF instance requested + * if backend is NETNS, create BGP server socket in the NETNS + */ + if (vrf_is_mapped_on_netns(bgp->vrf_id)) { + ret = bgp_socket(bgp, bm->port, bm->address); + if (ret < 0) + return BGP_ERR_INVALID_VALUE; + return 0; + } + } + /* if BGP VRF instance requested or VRF lite backend + * if BGP non VRF instance, create it + * if not already done + */ + return bgp_check_main_socket(create, bgp); +} + /* Called from VTY commands. */ int bgp_get(struct bgp **bgp_val, as_t *as, const char *name, enum bgp_instance_type inst_type) { struct bgp *bgp; + struct vrf *vrf = NULL; /* Multiple instance check. */ if (bgp_option_check(BGP_OPT_MULTIPLE_INSTANCE)) { @@ -3033,25 +3125,19 @@ int bgp_get(struct bgp **bgp_val, as_t *as, const char *name, bgp->t_rmap_def_originate_eval = NULL; - /* Create BGP server socket, if first instance. */ - if (list_isempty(bm->bgp) && !bgp_option_check(BGP_OPT_NO_LISTEN)) { - if (bgp_socket(bgp, bm->port, bm->address) < 0) - return BGP_ERR_INVALID_VALUE; - } - - listnode_add(bm->bgp, bgp); - /* If Default instance or VRF, link to the VRF structure, if present. */ if (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT || bgp->inst_type == BGP_INSTANCE_TYPE_VRF) { - struct vrf *vrf; - vrf = bgp_vrf_lookup_by_instance_type(bgp); if (vrf) bgp_vrf_link(bgp, vrf); } + /* BGP server socket already processed if BGP instance + * already part of the list + */ + bgp_handle_socket(bgp, vrf, VRF_UNKNOWN, true); + listnode_add(bm->bgp, bgp); - /* Register with Zebra, if needed */ if (IS_BGP_INST_KNOWN_TO_ZEBRA(bgp)) bgp_zebra_instance_register(bgp); @@ -3188,8 +3274,6 @@ int bgp_delete(struct bgp *bgp) * routes to be processed still referencing the struct bgp. */ listnode_delete(bm->bgp, bgp); - if (list_isempty(bm->bgp)) - bgp_close(); /* Deregister from Zebra, if needed */ if (IS_BGP_INST_KNOWN_TO_ZEBRA(bgp)) @@ -3199,6 +3283,7 @@ int bgp_delete(struct bgp *bgp) bgp_if_finish(bgp); vrf = bgp_vrf_lookup_by_instance_type(bgp); + bgp_handle_socket(bgp, vrf, VRF_UNKNOWN, false); if (vrf) bgp_vrf_unlink(bgp, vrf); diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 91a9f11620..7f55b753ab 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -1355,6 +1355,9 @@ extern void bgp_instance_up(struct bgp *); extern void bgp_instance_down(struct bgp *); extern int bgp_delete(struct bgp *); +extern int bgp_handle_socket(struct bgp *bgp, struct vrf *vrf, + vrf_id_t old_vrf_id, bool create); + extern int bgp_flag_set(struct bgp *, int); extern int bgp_flag_unset(struct bgp *, int); extern int bgp_flag_check(struct bgp *, int); From f62abc7d6577a44a67111b25a70d40170e2790af Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Tue, 30 Jan 2018 15:30:10 +0100 Subject: [PATCH 79/98] bgpd: do not start BGP VRF peer connection, if VRF not unknown Upon starting a BGP VRF instance, the server socket is not created, because the VRF ID is not known, and then underlying VRF backend is not ready yet. Because of that, the peer connection attempt will not be started before. Signed-off-by: Philippe Guibert --- bgpd/bgp_fsm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c index 79f4b1c91a..de11a98a20 100644 --- a/bgpd/bgp_fsm.c +++ b/bgpd/bgp_fsm.c @@ -289,7 +289,8 @@ void bgp_timer_set(struct peer *peer) /* First entry point of peer's finite state machine. In Idle status start timer is on unless peer is shutdown or peer is inactive. All other timer must be turned off */ - if (BGP_PEER_START_SUPPRESSED(peer) || !peer_active(peer)) { + if (BGP_PEER_START_SUPPRESSED(peer) || !peer_active(peer) + || peer->bgp->vrf_id == VRF_UNKNOWN) { BGP_TIMER_OFF(peer->t_start); } else { BGP_TIMER_ON(peer->t_start, bgp_start_timer, From 0268f30e3cab287bb10e84f7663098ed9cbd50c7 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Mon, 29 Jan 2018 16:14:46 +0100 Subject: [PATCH 80/98] zebra: speed ioctl read() with interfaces from various NETNS When interfaces are located on different NETNS ( different VRF), then a switch from netns context is necessary when calling setns(). The VRF apis to switch and switch back are called, so that the ioctl will work accordingly. Signed-off-by: Philippe Guibert --- zebra/if_netlink.c | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c index 6897bd4ee2..a02533c1fe 100644 --- a/zebra/if_netlink.c +++ b/zebra/if_netlink.c @@ -66,6 +66,7 @@ #include "zebra/kernel_netlink.h" #include "zebra/if_netlink.h" +extern struct zebra_privs_t zserv_privs; /* Note: on netlink systems, there should be a 1-to-1 mapping between interface names and ifindex values. */ @@ -344,12 +345,14 @@ static void netlink_vrf_change(struct nlmsghdr *h, struct rtattr *tb, } } -static int get_iflink_speed(const char *ifname) +static int get_iflink_speed(struct interface *interface) { struct ifreq ifdata; struct ethtool_cmd ecmd; int sd; int rc; + int ret, saved_errno; + const char *ifname = interface->name; /* initialize struct */ memset(&ifdata, 0, sizeof(ifdata)); @@ -363,16 +366,29 @@ static int get_iflink_speed(const char *ifname) ifdata.ifr_data = (__caddr_t)&ecmd; /* use ioctl to get IP address of an interface */ - sd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP); + if (zserv_privs.change(ZPRIVS_RAISE)) + zlog_err("Can't raise privileges"); + sd = vrf_socket(PF_INET, SOCK_DGRAM, IPPROTO_IP, interface->vrf_id); if (sd < 0) { if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug("Failure to read interface %s speed: %d %s", ifname, errno, safe_strerror(errno)); return 0; } - /* Get the current link state for the interface */ + ret = vrf_switch_to_netns(interface->vrf_id); + if (ret < 0) + zlog_err("%s: Can't switch to VRF %u (%s)", + __func__, interface->vrf_id, safe_strerror(errno)); rc = ioctl(sd, SIOCETHTOOL, (char *)&ifdata); + saved_errno = errno; + ret = vrf_switchback_to_initial(); + if (ret < 0) + zlog_err("%s: Can't switchback from VRF %u (%s)", + __func__, interface->vrf_id, safe_strerror(errno)); + errno = saved_errno; + if (zserv_privs.change(ZPRIVS_LOWER)) + zlog_err("Can't lower privileges"); if (rc < 0) { if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug( @@ -389,7 +405,7 @@ static int get_iflink_speed(const char *ifname) uint32_t kernel_get_speed(struct interface *ifp) { - return get_iflink_speed(ifp->name); + return get_iflink_speed(ifp); } static int netlink_extract_bridge_info(struct rtattr *link_data, @@ -647,7 +663,7 @@ static int netlink_interface(struct sockaddr_nl *snl, struct nlmsghdr *h, SET_FLAG(ifp->status, ZEBRA_INTERFACE_VRF_LOOPBACK); ifp->mtu6 = ifp->mtu = *(uint32_t *)RTA_DATA(tb[IFLA_MTU]); ifp->metric = 0; - ifp->speed = get_iflink_speed(name); + ifp->speed = get_iflink_speed(ifp); ifp->ptm_status = ZEBRA_PTM_STATUS_UNKNOWN; if (desc) From 4db21619552b3f6823bf1ae8103eac82aab628f7 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Thu, 22 Feb 2018 19:10:32 +0100 Subject: [PATCH 81/98] zebra: handle some ioctl operations for VRF A new API is available for interface ioctl operations on Linux: vrf_if_ioctl. This is the unified API that permits doing ioctl operations on a per interface basis. Signed-off-by: Philippe Guibert --- zebra/if_ioctl.c | 2 +- zebra/ioctl.c | 47 +++++++++++++++++++++++++++++++++++++------ zebra/ioctl.h | 1 + zebra/ioctl_solaris.c | 6 ++++++ 4 files changed, 49 insertions(+), 7 deletions(-) diff --git a/zebra/if_ioctl.c b/zebra/if_ioctl.c index 1d108886de..09fc085018 100644 --- a/zebra/if_ioctl.c +++ b/zebra/if_ioctl.c @@ -146,7 +146,7 @@ static int if_get_hwaddr(struct interface *ifp) ifreq.ifr_addr.sa_family = AF_INET; /* Fetch Hardware address if available. */ - ret = if_ioctl(SIOCGIFHWADDR, (caddr_t)&ifreq); + ret = vrf_if_ioctl(SIOCGIFHWADDR, (caddr_t)&ifreq, ifp->vrf_id); if (ret < 0) ifp->hw_addr_len = 0; else { diff --git a/zebra/ioctl.c b/zebra/ioctl.c index 8e3a1d1a03..d07d37056e 100644 --- a/zebra/ioctl.c +++ b/zebra/ioctl.c @@ -59,6 +59,7 @@ int if_ioctl(u_long request, caddr_t buffer) sock = socket(AF_INET, SOCK_DGRAM, 0); if (sock < 0) { int save_errno = errno; + if (zserv_privs.change(ZPRIVS_LOWER)) zlog_err("Can't lower privileges"); zlog_err("Cannot create UDP socket: %s", @@ -78,6 +79,39 @@ int if_ioctl(u_long request, caddr_t buffer) return 0; } +/* call ioctl system call */ +int vrf_if_ioctl(u_long request, caddr_t buffer, vrf_id_t vrf_id) +{ + int sock; + int ret; + int err = 0; + + if (zserv_privs.change(ZPRIVS_RAISE)) + zlog_err("Can't raise privileges"); + sock = vrf_socket(AF_INET, SOCK_DGRAM, 0, vrf_id, NULL); + if (sock < 0) { + int save_errno = errno; + + if (zserv_privs.change(ZPRIVS_LOWER)) + zlog_err("Can't lower privileges"); + zlog_err("Cannot create UDP socket: %s", + safe_strerror(save_errno)); + exit(1); + } + ret = vrf_ioctl(vrf_id, sock, request, buffer); + if (ret < 0) + err = errno; + if (zserv_privs.change(ZPRIVS_LOWER)) + zlog_err("Can't lower privileges"); + close(sock); + + if (ret < 0) { + errno = err; + return ret; + } + return 0; +} + #ifndef HAVE_NETLINK static int if_ioctl_ipv6(u_long request, caddr_t buffer) { @@ -90,6 +124,7 @@ static int if_ioctl_ipv6(u_long request, caddr_t buffer) sock = socket(AF_INET6, SOCK_DGRAM, 0); if (sock < 0) { int save_errno = errno; + if (zserv_privs.change(ZPRIVS_LOWER)) zlog_err("Can't lower privileges"); zlog_err("Cannot create IPv6 datagram socket: %s", @@ -122,7 +157,7 @@ void if_get_metric(struct interface *ifp) ifreq_set_name(&ifreq, ifp); - if (if_ioctl(SIOCGIFMETRIC, (caddr_t)&ifreq) < 0) + if (vrf_if_ioctl(SIOCGIFMETRIC, (caddr_t)&ifreq, ifp->vrf_id) < 0) return; ifp->metric = ifreq.ifr_metric; if (ifp->metric == 0) @@ -140,7 +175,7 @@ void if_get_mtu(struct interface *ifp) ifreq_set_name(&ifreq, ifp); #if defined(SIOCGIFMTU) - if (if_ioctl(SIOCGIFMTU, (caddr_t)&ifreq) < 0) { + if (vrf_if_ioctl(SIOCGIFMTU, (caddr_t)&ifreq, ifp->vrf_id) < 0) { zlog_info("Can't lookup mtu by ioctl(SIOCGIFMTU)"); ifp->mtu6 = ifp->mtu = -1; return; @@ -376,9 +411,9 @@ void if_get_flags(struct interface *ifp) ifreq_set_name(&ifreq, ifp); - ret = if_ioctl(SIOCGIFFLAGS, (caddr_t)&ifreq); + ret = vrf_if_ioctl(SIOCGIFFLAGS, (caddr_t)&ifreq, ifp->vrf_id); if (ret < 0) { - zlog_err("if_ioctl(SIOCGIFFLAGS) failed: %s", + zlog_err("vrf_if_ioctl(SIOCGIFFLAGS) failed: %s", safe_strerror(errno)); return; } @@ -423,7 +458,7 @@ int if_set_flags(struct interface *ifp, uint64_t flags) ifreq.ifr_flags = ifp->flags; ifreq.ifr_flags |= flags; - ret = if_ioctl(SIOCSIFFLAGS, (caddr_t)&ifreq); + ret = vrf_if_ioctl(SIOCSIFFLAGS, (caddr_t)&ifreq, ifp->vrf_id); if (ret < 0) { zlog_info("can't set interface flags"); @@ -444,7 +479,7 @@ int if_unset_flags(struct interface *ifp, uint64_t flags) ifreq.ifr_flags = ifp->flags; ifreq.ifr_flags &= ~flags; - ret = if_ioctl(SIOCSIFFLAGS, (caddr_t)&ifreq); + ret = vrf_if_ioctl(SIOCSIFFLAGS, (caddr_t)&ifreq, ifp->vrf_id); if (ret < 0) { zlog_info("can't unset interface flags"); diff --git a/zebra/ioctl.h b/zebra/ioctl.h index 02f8e6b880..1a6e14ed4d 100644 --- a/zebra/ioctl.h +++ b/zebra/ioctl.h @@ -25,6 +25,7 @@ /* Prototypes. */ extern void ifreq_set_name(struct ifreq *, struct interface *); extern int if_ioctl(u_long, caddr_t); +extern int vrf_if_ioctl(u_long request, caddr_t buffer, vrf_id_t vrf_id); extern int if_set_flags(struct interface *, uint64_t); extern int if_unset_flags(struct interface *, uint64_t); diff --git a/zebra/ioctl_solaris.c b/zebra/ioctl_solaris.c index e8b65925f8..f429c42440 100644 --- a/zebra/ioctl_solaris.c +++ b/zebra/ioctl_solaris.c @@ -30,6 +30,7 @@ #include "log.h" #include "privs.h" #include "vty.h" +#include "vrf.h" #include "zebra/rib.h" #include "zebra/rt.h" @@ -44,6 +45,11 @@ void lifreq_set_name(struct lifreq *lifreq, const char *ifname) strncpy(lifreq->lifr_name, ifname, IFNAMSIZ); } +int vrf_if_ioctl(u_long request, caddr_t buffer, vrf_id_t vrf_id) +{ + return if_ioctl(request, buffer); +} + /* call ioctl system call */ int if_ioctl(u_long request, caddr_t buffer) { From 822160dbe6f293a3c94487b4d0abe0f1d9883cca Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Thu, 1 Feb 2018 18:47:21 +0100 Subject: [PATCH 82/98] doc: add vrfwnetns keyword for zebra in doc The option to enable VRF backend is documented. Signed-off-by: Philippe Guibert --- doc/zebra.8.in | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/doc/zebra.8.in b/doc/zebra.8.in index 7f4a81b1a0..3e1d10e068 100644 --- a/doc/zebra.8.in +++ b/doc/zebra.8.in @@ -92,6 +92,11 @@ maximum before starting zebra. Note that this affects Linux only. .TP +\fB\-n\fR, \fB\-\-vrfwnetns \fR\fIEnable namespace VRF\fR +Enable namespace VRF backend. By default, the VRF backend relies on VRF-lite +support from Linux devices. This option permits discovering using Linux named +Netns and map it to FRR VRF contexts. +.TP \fB\-M\fR, \fB\-\-module \fR\fImodule:options\fR Load a module at startup. May be specified more than once. The \fBsnmp\fR and \fBfpm\fR modules may be From e26aedbe0b569b3e88718c457210051ba2eed437 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Mon, 5 Feb 2018 16:23:42 +0100 Subject: [PATCH 83/98] lib: split logicalrouter and vrf netns feature This split is introducing logicalrouter.[ch] as the file that contains the vty commands to configure logical router feature. The split has as consequence that the backend of logical router is linux_netns.c formerly called ns.c. The same relationship exists between VRF and its backend which may be linux_netns.c file. The split is adapting ns and vrf fiels so as to : - clarify header - ensure that the daemon persepctive, the feature VRF or logical router is called instead of calling directly ns. - this implies that VRF will call NS apis, as logical router does. Also, like it is done for default NS and default VRF, the associated VRF is enabled first, before NETNS is enabled, so that zvrf->zns pointer is valid when NETNS discovery applies. Also, other_netns.c file is a stub handler that will be used for non linux systems. As NETNS feature is only used by Linux, some BSD systems may want to use the same backend API to benefit from NETNS. This is what that file has been done. Signed-off-by: Philippe Guibert --- lib/command.c | 6 +- lib/command.h | 2 +- lib/logicalrouter.c | 159 ++++++++++ lib/logicalrouter.h | 41 +++ lib/{ns.c => netns_linux.c} | 606 ++++++++++++------------------------ lib/netns_other.c | 165 ++++++++++ lib/ns.h | 83 ++++- lib/subdir.am | 5 +- lib/vrf.c | 172 ++++++++-- lib/vrf.h | 88 ++++-- 10 files changed, 837 insertions(+), 490 deletions(-) create mode 100644 lib/logicalrouter.c create mode 100644 lib/logicalrouter.h rename lib/{ns.c => netns_linux.c} (51%) create mode 100644 lib/netns_other.c diff --git a/lib/command.c b/lib/command.c index d17f2c3d48..10996d5dd4 100644 --- a/lib/command.c +++ b/lib/command.c @@ -62,7 +62,7 @@ const char *node_names[] = { "aaa", // AAA_NODE, "keychain", // KEYCHAIN_NODE, "keychain key", // KEYCHAIN_KEY_NODE, - "logical-router", // NS_NODE, + "logical-router", // LOGICALROUTER_NODE, "vrf", // VRF_NODE, "interface", // INTERFACE_NODE, "zebra", // ZEBRA_NODE, @@ -1291,7 +1291,7 @@ void cmd_exit(struct vty *vty) break; case INTERFACE_NODE: case PW_NODE: - case NS_NODE: + case LOGICALROUTER_NODE: case VRF_NODE: case ZEBRA_NODE: case BGP_NODE: @@ -1376,7 +1376,7 @@ DEFUN (config_end, case CONFIG_NODE: case INTERFACE_NODE: case PW_NODE: - case NS_NODE: + case LOGICALROUTER_NODE: case VRF_NODE: case ZEBRA_NODE: case RIP_NODE: diff --git a/lib/command.h b/lib/command.h index e1edc1ef32..269318989f 100644 --- a/lib/command.h +++ b/lib/command.h @@ -85,7 +85,7 @@ enum node_type { AAA_NODE, /* AAA node. */ KEYCHAIN_NODE, /* Key-chain node. */ KEYCHAIN_KEY_NODE, /* Key-chain key node. */ - NS_NODE, /* Logical-Router node. */ + LOGICALROUTER_NODE, /* Logical-Router node. */ VRF_NODE, /* VRF mode node. */ INTERFACE_NODE, /* Interface mode node. */ ZEBRA_NODE, /* zebra connection node. */ diff --git a/lib/logicalrouter.c b/lib/logicalrouter.c new file mode 100644 index 0000000000..4dc99d304f --- /dev/null +++ b/lib/logicalrouter.c @@ -0,0 +1,159 @@ +/* + * Logical Router functions. + * Copyright (C) 2018 6WIND S.A. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "ns.h" +#include "log.h" +#include "memory.h" + +#include "command.h" +#include "vty.h" +#include "logicalrouter.h" + +/* Comment that useless define to avoid compilation error + * in order to use it, one could provide the kind of NETNS to NS backend + * so that the allocation will match the logical router + * DEFINE_MTYPE_STATIC(LIB, LOGICALROUTER, "LogicalRouter Context") + */ +DEFINE_MTYPE_STATIC(LIB, LOGICALROUTER_NAME, "Logical Router Name") + +/* Logical Router node has no interface. */ +static struct cmd_node logicalrouter_node = {LOGICALROUTER_NODE, "", + 1}; + +static int logicalrouter_backend; + +/* Get a NS. If not found, create one. */ +static struct ns *logicalrouter_get(ns_id_t ns_id) +{ + struct ns *ns; + + ns = ns_lookup(ns_id); + if (ns) + return (ns); + ns = ns_get_created(ns, NULL, ns_id); + return ns; +} + +static int logicalrouter_is_backend_netns(void) +{ + return (logicalrouter_backend == LOGICALROUTER_BACKEND_NETNS); +} + + +DEFUN_NOSH (logicalrouter, + logicalrouter_cmd, + "logical-router (1-65535) ns NAME", + "Enable a logical-router\n" + "Specify the logical-router indentifier\n" + "The Name Space\n" + "The file name in " NS_RUN_DIR ", or a full pathname\n") +{ + int idx_number = 1; + int idx_name = 3; + ns_id_t ns_id; + struct ns *ns = NULL; + char *pathname = ns_netns_pathname(vty, argv[idx_name]->arg); + + if (!pathname) + return CMD_WARNING_CONFIG_FAILED; + + ns_id = strtoul(argv[idx_number]->arg, NULL, 10); + ns = logicalrouter_get(ns_id); + + if (ns->name && strcmp(ns->name, pathname) != 0) { + vty_out(vty, "NS %u is already configured with NETNS %s\n", + ns->ns_id, ns->name); + return CMD_WARNING; + } + + if (!ns->name) + ns->name = XSTRDUP(MTYPE_LOGICALROUTER_NAME, pathname); + + if (!ns_enable(ns, NULL)) { + vty_out(vty, "Can not associate NS %u with NETNS %s\n", + ns->ns_id, ns->name); + return CMD_WARNING_CONFIG_FAILED; + } + + return CMD_SUCCESS; +} + +DEFUN (no_logicalrouter, + no_logicalrouter_cmd, + "no logical-router (1-65535) ns NAME", + NO_STR + "Enable a Logical-Router\n" + "Specify the Logical-Router identifier\n" + "The Name Space\n" + "The file name in " NS_RUN_DIR ", or a full pathname\n") +{ + int idx_number = 2; + int idx_name = 4; + ns_id_t ns_id; + struct ns *ns = NULL; + char *pathname = ns_netns_pathname(vty, argv[idx_name]->arg); + + if (!pathname) + return CMD_WARNING_CONFIG_FAILED; + + ns_id = strtoul(argv[idx_number]->arg, NULL, 10); + ns = ns_lookup(ns_id); + + if (!ns) { + vty_out(vty, "NS %u is not found\n", ns_id); + return CMD_SUCCESS; + } + + if (ns->name && strcmp(ns->name, pathname) != 0) { + vty_out(vty, "Incorrect NETNS file name\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + ns_disable(ns); + + if (ns->name) { + XFREE(MTYPE_LOGICALROUTER_NAME, ns->name); + ns->name = NULL; + } + + return CMD_SUCCESS; +} + +/* Initialize NS module. */ +void logicalrouter_init(int (*writefunc)(struct vty *vty)) +{ + if (ns_have_netns() && logicalrouter_is_backend_netns()) { + /* Install LogicalRouter commands. */ + install_node(&logicalrouter_node, writefunc); + install_element(CONFIG_NODE, &logicalrouter_cmd); + install_element(CONFIG_NODE, &no_logicalrouter_cmd); + } +} + +void logicalrouter_terminate(void) +{ + ns_terminate(); +} + +void logicalrouter_configure_backend(int backend_netns) +{ + logicalrouter_backend = backend_netns; +} diff --git a/lib/logicalrouter.h b/lib/logicalrouter.h new file mode 100644 index 0000000000..5a0780c009 --- /dev/null +++ b/lib/logicalrouter.h @@ -0,0 +1,41 @@ +/* + * Logical Router related header. + * Copyright (C) 2018 6WIND S.A. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _ZEBRA_LOGICAL_ROUTER_H +#define _ZEBRA_LOGICAL_ROUTER_H + +/* Logical Router Backend defines */ +#define LOGICALROUTER_BACKEND_OFF 0 +#define LOGICALROUTER_BACKEND_NETNS 1 + +/* + * Logical Router initializer/destructor + */ +extern void logicalrouter_init(int (*writefunc)(struct vty *vty)); +extern void logicalrouter_terminate(void); + +/* used to configure backend for logical router + * Currently, the whole NETNS feature is exclusively shared + * between logical router and VRF backend NETNS + * However, when logical router feature will be available, + * one can think of having exclusivity only per NETNS + */ +extern void logicalrouter_configure_backend(int backend_netns); + +#endif /*_ZEBRA_LOGICAL_ROUTER_H*/ diff --git a/lib/ns.c b/lib/netns_linux.c similarity index 51% rename from lib/ns.c rename to lib/netns_linux.c index 17d70a12fe..025862440a 100644 --- a/lib/ns.c +++ b/lib/netns_linux.c @@ -43,9 +43,8 @@ DEFINE_MTYPE_STATIC(LIB, NS, "NetNS Context") DEFINE_MTYPE_STATIC(LIB, NS_NAME, "NetNS Name") -static __inline int ns_compare(const struct ns *, const struct ns *); -static struct ns *ns_lookup(ns_id_t); -static struct ns *ns_lookup_name(const char *); +static inline int ns_compare(const struct ns *ns, const struct ns *ns2); +static struct ns *ns_lookup_name_internal(const char *name); RB_GENERATE(ns_head, ns, entry, ns_compare) @@ -56,7 +55,8 @@ static int ns_current_ns_fd; static int ns_default_ns_fd; #ifndef CLONE_NEWNET -#define CLONE_NEWNET 0x40000000 /* New network namespace (lo, device, names sockets, etc) */ +#define CLONE_NEWNET 0x40000000 +/* New network namespace (lo, device, names sockets, etc) */ #endif #ifndef HAVE_SETNS @@ -69,7 +69,7 @@ static inline int setns(int fd, int nstype) return -1; #endif } -#endif /* HAVE_SETNS */ +#endif /* !HAVE_SETNS */ #ifdef HAVE_NETNS static int have_netns_enabled = -1; @@ -108,73 +108,163 @@ struct ns_master { }; static int ns_is_enabled(struct ns *ns); -static int ns_enable(struct ns *ns); -static void ns_disable(struct ns *ns); -static void ns_get_created(struct ns *ns); -static __inline int ns_compare(const struct ns *a, const struct ns *b) +static inline int ns_compare(const struct ns *a, const struct ns *b) { return (a->ns_id - b->ns_id); } -static void ns_get_created(struct ns *ns) +/* Look up a NS by identifier. */ +static struct ns *ns_lookup_internal(ns_id_t ns_id) { + struct ns ns; + + ns.ns_id = ns_id; + return RB_FIND(ns_head, &ns_tree, &ns); +} + +/* Look up a NS by name */ +static struct ns *ns_lookup_name_internal(const char *name) +{ + struct ns *ns = NULL; + + RB_FOREACH(ns, ns_head, &ns_tree) { + if (ns->name != NULL) { + if (strcmp(name, ns->name) == 0) + return ns; + } + } + return NULL; +} + +static struct ns *ns_get_created_internal(struct ns *ns, char *name, + ns_id_t ns_id) +{ + int created = 0; /* * Initialize interfaces. - * - * I'm not sure if this belongs here or in - * the vrf code. */ - // if_init (&ns->iflist); - + if (!ns && !name && ns_id != NS_UNKNOWN) + ns = ns_lookup_internal(ns_id); + if (!ns && name) + ns = ns_lookup_name_internal(name); + if (!ns) { + ns = XCALLOC(MTYPE_NS, sizeof(struct ns)); + ns->ns_id = ns_id; + if (name) + ns->name = XSTRDUP(MTYPE_NS_NAME, name); + ns->fd = -1; + RB_INSERT(ns_head, &ns_tree, ns); + created = 1; + } + if (ns_id != ns->ns_id) { + RB_REMOVE(ns_head, &ns_tree, ns); + ns->ns_id = ns_id; + RB_INSERT(ns_head, &ns_tree, ns); + } + if (!created) + return ns; if (ns->ns_id != NS_UNKNOWN) zlog_info("NS %u is created.", ns->ns_id); else zlog_info("NS %s is created.", ns->name); if (ns_master.ns_new_hook) (*ns_master.ns_new_hook) (ns); - return; -} - -/* Get a NS. If not found, create one. */ -static struct ns *ns_get(ns_id_t ns_id) -{ - struct ns *ns; - - ns = ns_lookup(ns_id); - if (ns) - return (ns); - - ns = XCALLOC(MTYPE_NS, sizeof(struct ns)); - ns->ns_id = ns_id; - ns->fd = -1; - RB_INSERT(ns_head, &ns_tree, ns); - ns_get_created(ns); return ns; } -/* Get a NS. If not found, create one. */ -static struct ns *ns_get_by_name(char *ns_name) +/* + * Enable a NS - that is, let the NS be ready to use. + * The NS_ENABLE_HOOK callback will be called to inform + * that they can allocate resources in this NS. + * + * RETURN: 1 - enabled successfully; otherwise, 0. + */ +static int ns_enable_internal(struct ns *ns, void (*func)(ns_id_t, void *)) { - struct ns *ns; + if (!ns_is_enabled(ns)) { + if (have_netns()) { + ns->fd = open(ns->name, O_RDONLY); + } else { + ns->fd = -2; + /* Remember ns_enable_hook has been called */ + errno = -ENOTSUP; + } - ns = ns_lookup_name(ns_name); - if (ns) - return (ns); + if (!ns_is_enabled(ns)) { + zlog_err("Can not enable NS %u: %s!", ns->ns_id, + safe_strerror(errno)); + return 0; + } - ns = XCALLOC(MTYPE_NS, sizeof(struct ns)); - ns->ns_id = NS_UNKNOWN; - ns->name = XSTRDUP(MTYPE_NS_NAME, ns_name); - ns->fd = -1; - RB_INSERT(ns_head, &ns_tree, ns); + /* Non default NS. leave */ + if (ns->ns_id == NS_UNKNOWN) { + zlog_err("Can not enable NS %s %u: Invalid NSID", + ns->name, ns->ns_id); + return 0; + } + if (func) + func(ns->ns_id, (void *)ns->vrf_ctxt); + if (have_netns()) + zlog_info("NS %u is associated with NETNS %s.", + ns->ns_id, ns->name); - /* ns_id not initialised */ - ns_get_created(ns); - return ns; + zlog_info("NS %u is enabled.", ns->ns_id); + /* zebra first receives NS enable event, + * then VRF enable event + */ + if (ns_master.ns_enable_hook) + (*ns_master.ns_enable_hook)(ns); + } + + return 1; +} + +/* + * Check whether the NS is enabled - that is, whether the NS + * is ready to allocate resources. Currently there's only one + * type of resource: socket. + */ +static int ns_is_enabled(struct ns *ns) +{ + if (have_netns()) + return ns && ns->fd >= 0; + else + return ns && ns->fd == -2 && ns->ns_id == NS_DEFAULT; +} + +/* + * Disable a NS - that is, let the NS be unusable. + * The NS_DELETE_HOOK callback will be called to inform + * that they must release the resources in the NS. + */ +static void ns_disable_internal(struct ns *ns) +{ + if (ns_is_enabled(ns)) { + zlog_info("NS %u is to be disabled.", ns->ns_id); + + if (ns_master.ns_disable_hook) + (*ns_master.ns_disable_hook)(ns); + + if (have_netns()) + close(ns->fd); + + ns->fd = -1; + } +} + +struct ns *ns_get_created(struct ns *ns, char *name, ns_id_t ns_id) +{ + return ns_get_created_internal(ns, name, ns_id); +} + +int ns_have_netns(void) +{ + return have_netns(); } /* Delete a NS. This is called in ns_terminate(). */ -static void ns_delete(struct ns *ns) +void ns_delete(struct ns *ns) { zlog_info("NS %u is to be deleted.", ns->ns_id); @@ -196,23 +286,36 @@ static void ns_delete(struct ns *ns) XFREE(MTYPE_NS, ns); } -/* Look up a NS by identifier. */ -static struct ns *ns_lookup(ns_id_t ns_id) -{ - struct ns ns; - ns.ns_id = ns_id; - return (RB_FIND(ns_head, &ns_tree, &ns)); -} - /* Look up the data pointer of the specified VRF. */ void * ns_info_lookup(ns_id_t ns_id) { - struct ns *ns = ns_lookup(ns_id); + struct ns *ns = ns_lookup_internal(ns_id); return ns ? ns->info : NULL; } +/* Look up a NS by name */ +struct ns *ns_lookup_name(const char *name) +{ + return ns_lookup_name_internal(name); +} + +int ns_enable(struct ns *ns, void (*func)(ns_id_t, void *)) +{ + return ns_enable_internal(ns, func); +} + +void ns_disable(struct ns *ns) +{ + return ns_disable_internal(ns); +} + +struct ns *ns_lookup(ns_id_t ns_id) +{ + return ns_lookup_internal(ns_id); +} + void ns_walk_func(int (*func)(struct ns *)) { struct ns *ns = NULL; @@ -228,105 +331,6 @@ const char *ns_get_name(struct ns *ns) return ns->name; } -/* Look up a NS by name */ -static struct ns *ns_lookup_name(const char *name) -{ - struct ns *ns = NULL; - - RB_FOREACH(ns, ns_head, &ns_tree) { - if (ns->name != NULL) { - if (strcmp(name, ns->name) == 0) - return ns; - } - } - return NULL; -} - -/* - * Check whether the NS is enabled - that is, whether the NS - * is ready to allocate resources. Currently there's only one - * type of resource: socket. - */ -static int ns_is_enabled(struct ns *ns) -{ - if (have_netns()) - return ns && ns->fd >= 0; - else - return ns && ns->fd == -2 && ns->ns_id == NS_DEFAULT; -} - -/* - * Enable a NS - that is, let the NS be ready to use. - * The NS_ENABLE_HOOK callback will be called to inform - * that they can allocate resources in this NS. - * - * RETURN: 1 - enabled successfully; otherwise, 0. - */ -static int ns_enable(struct ns *ns) -{ - int vrf_on = 0; - - if (!ns_is_enabled(ns)) { - if (have_netns()) { - ns->fd = open(ns->name, O_RDONLY); - } else { - ns->fd = -2; /* Remember that ns_enable_hook has been - called */ - errno = -ENOTSUP; - } - - if (!ns_is_enabled(ns)) { - zlog_err("Can not enable NS %u: %s!", ns->ns_id, - safe_strerror(errno)); - return 0; - } - - /* Non default NS. leave */ - if (ns->ns_id == NS_UNKNOWN) { - zlog_err("Can not enable NS %s %u: Invalid NSID", - ns->name, ns->ns_id); - return 0; - } - vrf_on = vrf_update_vrf_id((vrf_id_t)ns->ns_id, - (struct vrf *)ns->vrf_ctxt); - if (have_netns()) - zlog_info("NS %u is associated with NETNS %s.", - ns->ns_id, ns->name); - - zlog_info("NS %u is enabled.", ns->ns_id); - /* zebra first receives NS enable event, - * then VRF enable event - */ - if (ns_master.ns_enable_hook) - (*ns_master.ns_enable_hook)(ns); - if (vrf_on == 1) - vrf_enable((struct vrf *)ns->vrf_ctxt); - } - - return 1; -} - -/* - * Disable a NS - that is, let the NS be unusable. - * The NS_DELETE_HOOK callback will be called to inform - * that they must release the resources in the NS. - */ -static void ns_disable(struct ns *ns) -{ - if (ns_is_enabled(ns)) { - zlog_info("NS %u is to be disabled.", ns->ns_id); - - if (ns_master.ns_disable_hook) - (*ns_master.ns_disable_hook)(ns); - - if (have_netns()) - close(ns->fd); - - ns->fd = -1; - } -} - - /* Add a NS hook. Please add hooks before calling ns_init(). */ void ns_add_hook(int type, int (*func)(struct ns *)) { @@ -360,9 +364,10 @@ char *ns_netns_pathname(struct vty *vty, const char *name) if (name[0] == '/') /* absolute pathname */ result = realpath(name, pathname); - else /* relevant pathname */ - { + else { + /* relevant pathname */ char tmp_name[PATH_MAX]; + snprintf(tmp_name, PATH_MAX, "%s/%s", NS_RUN_DIR, name); result = realpath(tmp_name, pathname); } @@ -379,294 +384,66 @@ char *ns_netns_pathname(struct vty *vty, const char *name) check_base = basename(pathname); if (check_base != NULL && strlen(check_base) + 1 > NS_NAMSIZ) { if (vty) - vty_out(vty, "NS name (%s) invalid:" - " too long( %d needed)\n", + vty_out(vty, "NS name (%s) invalid: too long (>%d)\n", check_base, NS_NAMSIZ-1); else - zlog_warn("NS name (%s) invalid:" - " too long ( %d needed)", + zlog_warn("NS name (%s) invalid: too long (>%d)", check_base, NS_NAMSIZ-1); return NULL; } return pathname; } -DEFUN_NOSH (ns_logicalrouter, - ns_logicalrouter_cmd, - "logical-router (1-65535) ns NAME", - "Enable a logical-router\n" - "Specify the logical-router indentifier\n" - "The Name Space\n" - "The file name in " NS_RUN_DIR ", or a full pathname\n") -{ - int idx_number = 1; - int idx_name = 3; - ns_id_t ns_id; - struct ns *ns = NULL; - char *pathname = ns_netns_pathname(vty, argv[idx_name]->arg); - - if (!pathname) - return CMD_WARNING_CONFIG_FAILED; - - ns_id = strtoul(argv[idx_number]->arg, NULL, 10); - ns = ns_get(ns_id); - - if (ns->name && strcmp(ns->name, pathname) != 0) { - vty_out(vty, "NS %u is already configured with NETNS %s\n", - ns->ns_id, ns->name); - return CMD_WARNING; - } - - if (!ns->name) - ns->name = XSTRDUP(MTYPE_NS_NAME, pathname); - - if (!ns_enable(ns)) { - vty_out(vty, "Can not associate NS %u with NETNS %s\n", - ns->ns_id, ns->name); - return CMD_WARNING_CONFIG_FAILED; - } - - return CMD_SUCCESS; -} - -static struct cmd_node logicalrouter_node = {NS_NODE, "", /* NS node has no interface. */ - 1}; - -DEFUN (no_ns_logicalrouter, - no_ns_logicalrouter_cmd, - "no logical-router (1-65535) ns NAME", - NO_STR - "Enable a Logical-Router\n" - "Specify the Logical-Router identifier\n" - "The Name Space\n" - "The file name in " NS_RUN_DIR ", or a full pathname\n") -{ - int idx_number = 2; - int idx_name = 4; - ns_id_t ns_id; - struct ns *ns = NULL; - char *pathname = ns_netns_pathname(vty, argv[idx_name]->arg); - - if (!pathname) - return CMD_WARNING_CONFIG_FAILED; - - ns_id = strtoul(argv[idx_number]->arg, NULL, 10); - ns = ns_lookup(ns_id); - - if (!ns) { - vty_out(vty, "NS %u is not found\n", ns_id); - return CMD_SUCCESS; - } - - if (ns->name && strcmp(ns->name, pathname) != 0) { - vty_out(vty, "Incorrect NETNS file name\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - ns_disable(ns); - - if (ns->name) { - XFREE(MTYPE_NS_NAME, ns->name); - ns->name = NULL; - } - - return CMD_SUCCESS; -} - -int ns_handler_create(struct vty *vty, struct vrf *vrf, - char *pathname, ns_id_t ns_id) -{ - struct ns *ns = NULL; - - if (!vrf) - return CMD_WARNING_CONFIG_FAILED; - if (vrf->vrf_id != VRF_UNKNOWN && vrf->ns_ctxt == NULL) { - if (vty) - vty_out(vty, - "VRF %u is already configured with VRF %s\n", - vrf->vrf_id, vrf->name); - else - zlog_warn("VRF %u is already configured with VRF %s\n", - vrf->vrf_id, vrf->name); - return CMD_WARNING_CONFIG_FAILED; - } - if (vrf->ns_ctxt != NULL) { - ns = (struct ns *) vrf->ns_ctxt; - if (ns && 0 != strcmp(ns->name, pathname)) { - if (vty) - vty_out(vty, - "VRF %u is already configured" - " with NETNS %s\n", - vrf->vrf_id, ns->name); - else - zlog_warn("VRF %u is already configured with NETNS %s", - vrf->vrf_id, ns->name); - return CMD_WARNING_CONFIG_FAILED; - } - } - ns = ns_lookup_name(pathname); - if (ns && ns->vrf_ctxt) { - struct vrf *vrf2 = (struct vrf *)ns->vrf_ctxt; - - if (vrf2 == vrf) - return CMD_SUCCESS; - if (vty) - vty_out(vty, "NS %s is already configured" - " with VRF %u(%s)\n", - ns->name, vrf2->vrf_id, vrf2->name); - else - zlog_warn("NS %s is already configured with VRF %u(%s)", - ns->name, vrf2->vrf_id, vrf2->name); - return CMD_WARNING_CONFIG_FAILED; - } else if (!ns) - ns = ns_get_by_name(pathname); - - if (ns_id != ns->ns_id) { - RB_REMOVE(ns_head, &ns_tree, ns); - ns->ns_id = ns_id; - RB_INSERT(ns_head, &ns_tree, ns); - } - ns->vrf_ctxt = (void *)vrf; - vrf->ns_ctxt = (void *)ns; - /* update VRF netns NAME */ - if (vrf) - strlcpy(vrf->data.l.netns_name, basename(pathname), NS_NAMSIZ); - - if (!ns_enable(ns)) { - if (vty) - vty_out(vty, "Can not associate NS %u with NETNS %s\n", - ns->ns_id, ns->name); - else - zlog_warn("Can not associate NS %u with NETNS %s", - ns->ns_id, ns->name); - return CMD_WARNING_CONFIG_FAILED; - } - - return CMD_SUCCESS; -} - - -static int ns_logicalrouter_config_write(struct vty *vty) -{ - struct ns *ns; - int write = 0; - - RB_FOREACH(ns, ns_head, &ns_tree) { - if (ns->ns_id == NS_DEFAULT || ns->name == NULL) - continue; - vty_out(vty, "logical-router %u netns %s\n", ns->ns_id, - ns->name); - write = 1; - } - return write; -} - -DEFUN_NOSH (ns_netns, - ns_netns_cmd, - "netns NAME", - "Attach VRF to a Namespace\n" - "The file name in " NS_RUN_DIR ", or a full pathname\n") -{ - int idx_name = 1; - char *pathname = ns_netns_pathname(vty, argv[idx_name]->arg); - - VTY_DECLVAR_CONTEXT(vrf, vrf); - - if (!pathname) - return CMD_WARNING_CONFIG_FAILED; - return ns_handler_create(vty, vrf, pathname, NS_UNKNOWN); -} - -DEFUN (no_ns_netns, - no_ns_netns_cmd, - "no netns [NAME]", - NO_STR - "Detach VRF from a Namespace\n" - "The file name in " NS_RUN_DIR ", or a full pathname\n") -{ - struct ns *ns = NULL; - - VTY_DECLVAR_CONTEXT(vrf, vrf); - - if (!vrf_is_backend_netns()) { - vty_out(vty, "VRF backend is not Netns. Aborting\n"); - return CMD_WARNING_CONFIG_FAILED; - } - if (!vrf->ns_ctxt) { - vty_out(vty, "VRF %s(%u) is not configured with NetNS\n", - vrf->name, vrf->vrf_id); - return CMD_WARNING_CONFIG_FAILED; - } - - ns = (struct ns *)vrf->ns_ctxt; - - ns->vrf_ctxt = NULL; - vrf_disable(vrf); - /* vrf ID from VRF is necessary for Zebra - * so that propagate to other clients is done - */ - RB_REMOVE(ns_head, &ns_tree, ns); - ns->ns_id = NS_UNKNOWN; - RB_INSERT(ns_head, &ns_tree, ns); - ns_delete(ns); - vrf->ns_ctxt = NULL; - return CMD_SUCCESS; -} - void ns_init(void) { -#ifdef HAVE_NETNS - if (have_netns_enabled < 0) { - ns_default_ns_fd = open(NS_DEFAULT_NAME, O_RDONLY); + static int ns_initialised; + + /* silently return as initialisation done */ + if (ns_initialised == 1) return; - } -#endif /* HAVE_NETNS */ + errno = 0; +#ifdef HAVE_NETNS + if (have_netns_enabled < 0) + ns_default_ns_fd = open(NS_DEFAULT_NAME, O_RDONLY); + else + ns_default_ns_fd = -1; +#else ns_default_ns_fd = -1; default_ns = NULL; +#endif /* HAVE_NETNS */ + if (ns_default_ns_fd == -1) + zlog_err("NS initialisation failure (%s)", + safe_strerror(errno)); + ns_current_ns_fd = -1; + ns_initialised = 1; } /* Initialize NS module. */ -void ns_init_zebra(ns_id_t default_ns_id) +void ns_init_management(ns_id_t default_ns_id) { int fd; ns_init(); - default_ns = ns_get(default_ns_id); + default_ns = ns_get_created_internal(NULL, NULL, default_ns_id); if (!default_ns) { - zlog_err("ns_init: failed to create the default NS!"); + zlog_err("%s: failed to create the default NS!", + __func__); exit(1); } if (have_netns()) { fd = open(NS_DEFAULT_NAME, O_RDONLY); default_ns->fd = fd; } - ns_current_ns_fd = -1; /* Set the default NS name. */ default_ns->name = XSTRDUP(MTYPE_NS_NAME, NS_DEFAULT_NAME); - zlog_info("ns_init: default NSID is %u", default_ns->ns_id); + zlog_info("%s: default NSID is %u", __func__, default_ns->ns_id); /* Enable the default NS. */ - if (!ns_enable(default_ns)) { - zlog_err("ns_init: failed to enable the default NS!"); + if (!ns_enable(default_ns, NULL)) { + zlog_err("%s: failed to enable the default NS!", + __func__); exit(1); } - - if (have_netns() && !vrf_is_backend_netns()) { - /* Install NS commands. */ - install_node(&logicalrouter_node, - ns_logicalrouter_config_write); - install_element(CONFIG_NODE, &ns_logicalrouter_cmd); - install_element(CONFIG_NODE, &no_ns_logicalrouter_cmd); - } -} - -void ns_cmd_init(void) -{ - if (have_netns() && vrf_is_backend_netns()) { - /* Install NS commands. */ - install_element(VRF_NODE, &ns_netns_cmd); - install_element(VRF_NODE, &no_ns_netns_cmd); - } } /* Terminate NS module. */ @@ -688,6 +465,8 @@ int ns_switch_to_netns(const char *name) if (name == NULL) return -1; + if (ns_default_ns_fd == -1) + return -1; fd = open(name, O_RDONLY); if (fd == -1) { errno = ENOSYS; @@ -704,7 +483,7 @@ int ns_switch_to_netns(const char *name) */ int ns_switchback_to_initial(void) { - if (ns_current_ns_fd != -1) { + if (ns_current_ns_fd != -1 && ns_default_ns_fd != -1) { int ret; ret = setns(ns_default_ns_fd, CLONE_NEWNET); @@ -725,7 +504,6 @@ int ns_socket(int domain, int type, int protocol, ns_id_t ns_id) errno = ENOSYS; return -1; } - if (have_netns()) { ret = (ns_id != NS_DEFAULT) ? setns(ns->fd, CLONE_NEWNET) : 0; if (ret >= 0) { diff --git a/lib/netns_other.c b/lib/netns_other.c new file mode 100644 index 0000000000..2402dd17d6 --- /dev/null +++ b/lib/netns_other.c @@ -0,0 +1,165 @@ +/* + * NetNS backend for non Linux systems + * Copyright (C) 2018 6WIND S.A. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#if !defined(GNU_LINUX) && (defined(SUNOS_5) || defined(OPEN_BSD)) +/* SUNOS_5 or OPEN_BSD */ + +#include +#include "ns.h" +#include "log.h" +#include "memory.h" + +DEFINE_MTYPE_STATIC(LIB, NS, "NetNS Context") +DEFINE_MTYPE_STATIC(LIB, NS_NAME, "NetNS Name") + + +static inline int ns_compare(const struct ns *ns, const struct ns *ns2); + +RB_GENERATE(ns_head, ns, entry, ns_compare) + +struct ns_head ns_tree = RB_INITIALIZER(&ns_tree); + +static inline int ns_compare(const struct ns *a, const struct ns *b) +{ + return (a->ns_id - b->ns_id); +} + +void ns_terminate(void) +{ +} + +/* API to initialize NETNS managerment + * parameter is the default ns_id + */ +void ns_init_management(ns_id_t ns_id) +{ +} + + +/* + * NS utilities + */ + +/* Create a socket serving for the given NS + */ +int ns_socket(int domain, int type, int protocol, ns_id_t ns_id) +{ + return -1; +} + +/* return the path of the NETNS */ +char *ns_netns_pathname(struct vty *vty, const char *name) +{ + return NULL; +} + +/* Parse and execute a function on all the NETNS */ +void ns_walk_func(int (*func)(struct ns *)) +{ +} + +/* API to get the NETNS name, from the ns pointer */ +const char *ns_get_name(struct ns *ns) +{ + return NULL; +} + +/* only called from vrf ( when removing netns from vrf) + * or at VRF or logical router termination + */ +void ns_delete(struct ns *ns) +{ +} + +/* return > 0 if netns is available + * called by VRF to check netns backend is available for VRF + */ +int ns_have_netns(void) +{ + return 0; +} + +/* API to get context information of a NS */ +void *ns_info_lookup(ns_id_t ns_id) +{ + return NULL; +} + +/* + * NS init routine + * should be called from backendx + */ +void ns_init(void) +{ +} + +/* API to retrieve default NS */ +ns_id_t ns_get_default_id(void) +{ + return NS_UNKNOWN; +} + + +/* API that can be used to change from NS */ +int ns_switchback_to_initial(void) +{ + return 0; +} +int ns_switch_to_netns(const char *netns_name) +{ + return 0; +} + +/* + * NS handling routines. + * called by modules that use NS backend + */ + +/* API to search for already present NETNS */ +struct ns *ns_lookup(ns_id_t ns_id) +{ + return NULL; +} + +struct ns *ns_lookup_name(const char *name) +{ + return NULL; +} + +/* API to handle NS : creation, enable, disable + * for enable, a callback function is passed as parameter + * the callback belongs to the module that uses NS as backend + * upon enabling the NETNS, the upper layer is informed + */ +int ns_enable(struct ns *ns, int (*func)(ns_id_t, void *)) +{ + return 0; +} + +struct ns *ns_get_created(struct ns *ns, char *name, ns_id_t ns_id) +{ + return NULL; +} + +void ns_disable(struct ns *ns) +{ +} + +#endif /* !GNU_LINUX */ diff --git a/lib/ns.h b/lib/ns.h index 44257ab0c0..83e5e1b907 100644 --- a/lib/ns.h +++ b/lib/ns.h @@ -25,7 +25,6 @@ #include "openbsd-tree.h" #include "linklist.h" #include "vty.h" -#include "vrf.h" typedef u_int32_t ns_id_t; @@ -67,6 +66,11 @@ RB_PROTOTYPE(ns_head, ns, entry, ns_compare) extern struct ns_head ns_tree; +/* + * API for managing NETNS. eg from zebra daemon + * one want to manage the list of NETNS, etc... + */ + /* * NS hooks */ @@ -86,35 +90,80 @@ extern struct ns_head ns_tree; */ extern void ns_add_hook(int type, int (*)(struct ns *)); + /* * NS initializer/destructor */ -extern void ns_init(void); -extern void ns_init_zebra(ns_id_t ns_id); + extern void ns_terminate(void); +/* API to initialize NETNS managerment + * parameter is the default ns_id + */ +extern void ns_init_management(ns_id_t ns_id); + + /* * NS utilities */ -/* Create a socket serving for the given NS */ -extern int ns_socket(int, int, int, ns_id_t); -extern void ns_cmd_init(void); -extern int ns_handler_create(struct vty *vty, struct vrf *vrf, - char *pathname, ns_id_t ns_id); -extern char *ns_netns_pathname(struct vty *vty, const char *name); -extern void *ns_info_lookup(ns_id_t ns_id); -extern void ns_walk_func(int (*func)(struct ns *)); -extern const char *ns_get_name(struct ns *ns); -extern ns_id_t ns_get_default_id(void); +/* Create a socket serving for the given NS + */ +int ns_socket(int domain, int type, int protocol, ns_id_t ns_id); -/* API that can be used by all daemons */ -extern int ns_switchback_to_initial(void); -extern int ns_switch_to_netns(const char *netns_name); +/* return the path of the NETNS */ +extern char *ns_netns_pathname(struct vty *vty, const char *name); + +/* Parse and execute a function on all the NETNS */ +extern void ns_walk_func(int (*func)(struct ns *)); + +/* API to get the NETNS name, from the ns pointer */ +extern const char *ns_get_name(struct ns *ns); + +/* only called from vrf ( when removing netns from vrf) + * or at VRF or logical router termination + */ +extern void ns_delete(struct ns *ns); + +/* return > 0 if netns is available + * called by VRF to check netns backend is available for VRF + */ +extern int ns_have_netns(void); + +/* API to get context information of a NS */ +extern void *ns_info_lookup(ns_id_t ns_id); + +/* + * NS init routine + * should be called from backendx + */ extern void ns_init(void); +/* API to retrieve default NS */ +extern ns_id_t ns_get_default_id(void); -/* The default NS ID */ #define NS_DEFAULT ns_get_default_id() +/* API that can be used to change from NS */ +extern int ns_switchback_to_initial(void); +extern int ns_switch_to_netns(const char *netns_name); + +/* + * NS handling routines. + * called by modules that use NS backend + */ + +/* API to search for already present NETNS */ +extern struct ns *ns_lookup(ns_id_t ns_id); +extern struct ns *ns_lookup_name(const char *name); + +/* API to handle NS : creation, enable, disable + * for enable, a callback function is passed as parameter + * the callback belongs to the module that uses NS as backend + * upon enabling the NETNS, the upper layer is informed + */ +extern int ns_enable(struct ns *ns, void (*func)(ns_id_t, void *)); +extern struct ns *ns_get_created(struct ns *ns, char *name, ns_id_t ns_id); +extern void ns_disable(struct ns *ns); + #endif /*_ZEBRA_NS_H*/ diff --git a/lib/subdir.am b/lib/subdir.am index 44870917bd..e292d7a342 100644 --- a/lib/subdir.am +++ b/lib/subdir.am @@ -42,7 +42,8 @@ lib_libfrr_la_SOURCES = \ lib/module.c \ lib/network.c \ lib/nexthop.c \ - lib/ns.c \ + lib/netns_linux.c \ + lib/netns_other.c \ lib/openbsd-tree.c \ lib/pid_output.c \ lib/plist.c \ @@ -74,6 +75,7 @@ lib_libfrr_la_SOURCES = \ lib/wheel.c \ lib/workqueue.c \ lib/zclient.c \ + lib/logicalrouter.c \ # end lib/plist_clippy.c: $(CLIPPY_DEPS) @@ -158,6 +160,7 @@ pkginclude_HEADERS += \ lib/zassert.h \ lib/zclient.h \ lib/zebra.h \ + lib/logicalrouter.h \ # end nodist_pkginclude_HEADERS += \ diff --git a/lib/vrf.c b/lib/vrf.c index ade1895bd4..0ca517d051 100644 --- a/lib/vrf.c +++ b/lib/vrf.c @@ -89,16 +89,38 @@ static int vrf_name_compare(const struct vrf *a, const struct vrf *b) return strcmp(a->name, b->name); } +/* if ns_id is different and not VRF_UNKNOWN, + * then update vrf identifier, and enable VRF + */ +static void vrf_update_vrf_id(ns_id_t ns_id, void *opaqueptr) +{ + ns_id_t vrf_id = (vrf_id_t)ns_id; + vrf_id_t old_vrf_id; + struct vrf *vrf = (struct vrf *)opaqueptr; + + if (!vrf) + return; + old_vrf_id = vrf->vrf_id; + if (vrf_id == vrf->vrf_id) + return; + if (vrf->vrf_id != VRF_UNKNOWN) + RB_REMOVE(vrf_id_head, &vrfs_by_id, vrf); + vrf->vrf_id = vrf_id; + RB_INSERT(vrf_id_head, &vrfs_by_id, vrf); + if (old_vrf_id == VRF_UNKNOWN) + vrf_enable((struct vrf *)vrf); +} + int vrf_switch_to_netns(vrf_id_t vrf_id) { char *name; struct vrf *vrf = vrf_lookup_by_id(vrf_id); - /* VRF has no NETNS backend. silently ignore */ - if (!vrf || vrf->data.l.netns_name[0] == '\0') - return 0; /* VRF is default VRF. silently ignore */ - if (vrf->vrf_id == VRF_DEFAULT) + if (!vrf || vrf->vrf_id == VRF_DEFAULT) + return 0; + /* VRF has no NETNS backend. silently ignore */ + if (vrf->data.l.netns_name[0] == '\0') return 0; name = ns_netns_pathname(NULL, vrf->data.l.netns_name); if (debug_vrf) @@ -115,25 +137,6 @@ int vrf_switchback_to_initial(void) return ret; } -/* return 1 if vrf can be enabled */ -int vrf_update_vrf_id(vrf_id_t vrf_id, struct vrf *vrf) -{ - vrf_id_t old_vrf_id; - - if (!vrf) - return 0; - old_vrf_id = vrf->vrf_id; - if (vrf_id == vrf->vrf_id) - return 0; - if (vrf->vrf_id != VRF_UNKNOWN) - RB_REMOVE(vrf_id_head, &vrfs_by_id, vrf); - vrf->vrf_id = vrf_id; - RB_INSERT(vrf_id_head, &vrfs_by_id, vrf); - if (old_vrf_id == VRF_UNKNOWN) - return 1; - return 0; -} - /* Get a VRF. If not found, create one. * Arg: * name - The name of the vrf. May be NULL if unknown. @@ -436,6 +439,8 @@ void vrf_init(int (*create)(struct vrf *), int (*enable)(struct vrf *), { struct vrf *default_vrf; + /* initialise NS, in case VRF backend if NETNS */ + ns_init(); if (debug_vrf) zlog_debug("%s: Initializing VRF subsystem", __PRETTY_FUNCTION__); @@ -547,6 +552,72 @@ int vrf_handler_create(struct vty *vty, const char *vrfname, struct vrf **vrf) return CMD_SUCCESS; } +int vrf_netns_handler_create(struct vty *vty, struct vrf *vrf, + char *pathname, ns_id_t ns_id) +{ + struct ns *ns = NULL; + + if (!vrf) + return CMD_WARNING_CONFIG_FAILED; + if (vrf->vrf_id != VRF_UNKNOWN && vrf->ns_ctxt == NULL) { + if (vty) + vty_out(vty, + "VRF %u is already configured with VRF %s\n", + vrf->vrf_id, vrf->name); + else + zlog_warn("VRF %u is already configured with VRF %s\n", + vrf->vrf_id, vrf->name); + return CMD_WARNING_CONFIG_FAILED; + } + if (vrf->ns_ctxt != NULL) { + ns = (struct ns *) vrf->ns_ctxt; + if (ns && 0 != strcmp(ns->name, pathname)) { + if (vty) + vty_out(vty, + "VRF %u already configured with NETNS %s\n", + vrf->vrf_id, ns->name); + else + zlog_warn( + "VRF %u already configured with NETNS %s", + vrf->vrf_id, ns->name); + return CMD_WARNING_CONFIG_FAILED; + } + } + ns = ns_lookup_name(pathname); + if (ns && ns->vrf_ctxt) { + struct vrf *vrf2 = (struct vrf *)ns->vrf_ctxt; + + if (vrf2 == vrf) + return CMD_SUCCESS; + if (vty) + vty_out(vty, "NS %s is already configured" + " with VRF %u(%s)\n", + ns->name, vrf2->vrf_id, vrf2->name); + else + zlog_warn("NS %s is already configured with VRF %u(%s)", + ns->name, vrf2->vrf_id, vrf2->name); + return CMD_WARNING_CONFIG_FAILED; + } + ns = ns_get_created(ns, pathname, ns_id); + ns->vrf_ctxt = (void *)vrf; + vrf->ns_ctxt = (void *)ns; + /* update VRF netns NAME */ + if (vrf) + strlcpy(vrf->data.l.netns_name, basename(pathname), NS_NAMSIZ); + + if (!ns_enable(ns, vrf_update_vrf_id)) { + if (vty) + vty_out(vty, "Can not associate NS %u with NETNS %s\n", + ns->ns_id, ns->name); + else + zlog_warn("Can not associate NS %u with NETNS %s", + ns->ns_id, ns->name); + return CMD_WARNING_CONFIG_FAILED; + } + + return CMD_SUCCESS; +} + int vrf_is_mapped_on_netns(vrf_id_t vrf_id) { struct vrf *vrf = vrf_lookup_by_id(vrf_id); @@ -604,6 +675,55 @@ DEFUN_NOSH (no_vrf, struct cmd_node vrf_node = {VRF_NODE, "%s(config-vrf)# ", 1}; +DEFUN_NOSH (vrf_netns, + vrf_netns_cmd, + "netns NAME", + "Attach VRF to a Namespace\n" + "The file name in " NS_RUN_DIR ", or a full pathname\n") +{ + int idx_name = 1; + char *pathname = ns_netns_pathname(vty, argv[idx_name]->arg); + + VTY_DECLVAR_CONTEXT(vrf, vrf); + + if (!pathname) + return CMD_WARNING_CONFIG_FAILED; + return vrf_netns_handler_create(vty, vrf, pathname, NS_UNKNOWN); +} + +DEFUN (no_vrf_netns, + no_vrf_netns_cmd, + "no netns [NAME]", + NO_STR + "Detach VRF from a Namespace\n" + "The file name in " NS_RUN_DIR ", or a full pathname\n") +{ + struct ns *ns = NULL; + + VTY_DECLVAR_CONTEXT(vrf, vrf); + + if (!vrf_is_backend_netns()) { + vty_out(vty, "VRF backend is not Netns. Aborting\n"); + return CMD_WARNING_CONFIG_FAILED; + } + if (!vrf->ns_ctxt) { + vty_out(vty, "VRF %s(%u) is not configured with NetNS\n", + vrf->name, vrf->vrf_id); + return CMD_WARNING_CONFIG_FAILED; + } + + ns = (struct ns *)vrf->ns_ctxt; + + ns->vrf_ctxt = NULL; + vrf_disable(vrf); + /* vrf ID from VRF is necessary for Zebra + * so that propagate to other clients is done + */ + ns_delete(ns); + vrf->ns_ctxt = NULL; + return CMD_SUCCESS; +} + /* * Debug CLI for vrf's */ @@ -656,7 +776,11 @@ void vrf_cmd_init(int (*writefunc)(struct vty *vty)) install_element(CONFIG_NODE, &no_vrf_cmd); install_node(&vrf_node, writefunc); install_default(VRF_NODE); - ns_cmd_init(); + if (vrf_is_backend_netns() && ns_have_netns()) { + /* Install NS commands. */ + install_element(VRF_NODE, &vrf_netns_cmd); + install_element(VRF_NODE, &no_vrf_netns_cmd); + } } vrf_id_t vrf_get_default_id(void) diff --git a/lib/vrf.h b/lib/vrf.h index 08c53484ee..326418791d 100644 --- a/lib/vrf.h +++ b/lib/vrf.h @@ -26,6 +26,7 @@ #include "linklist.h" #include "qobj.h" #include "vty.h" +#include "ns.h" /* The default VRF ID */ #define VRF_UNKNOWN UINT32_MAX @@ -199,21 +200,28 @@ extern void vrf_init(int (*create)(struct vrf *), int (*enable)(struct vrf *), */ extern void vrf_terminate(void); -extern void vrf_cmd_init(int (*writefunc)(struct vty *vty)); - /* - * VRF utilities + * Utilities to create networks objects, + * or call network operations */ -extern vrf_id_t vrf_get_default_id(void); /* Create a socket serving for the given VRF */ -extern int vrf_socket(int, int, int, vrf_id_t); -extern void vrf_configure_backend(int vrf_backend_netns); -extern int vrf_get_backend(void); -extern int vrf_is_backend_netns(void); -extern int vrf_handler_create(struct vty *vty, - const char *name, - struct vrf **vrf); +extern int vrf_socket(int domain, int type, + int protocol, vrf_id_t vrf_id); +extern int vrf_sockunion_socket(const union sockunion *su, + vrf_id_t vrf_id); + +/* VRF ioctl operations */ +extern int vrf_getaddrinfo(const char *node, const char *service, + const struct addrinfo *hints, + struct addrinfo **res, vrf_id_t vrf_id); + +/* function called by macro VRF_DEFAULT + * to get the default VRF_ID + */ +extern vrf_id_t vrf_get_default_id(void); +/* The default VRF ID */ +#define VRF_DEFAULT vrf_get_default_id() /* VRF is mapped on netns or not ? */ int vrf_is_mapped_on_netns(vrf_id_t vrf_id); @@ -222,29 +230,49 @@ int vrf_is_mapped_on_netns(vrf_id_t vrf_id); extern int vrf_switch_to_netns(vrf_id_t vrf_id); extern int vrf_switchback_to_initial(void); -/* VRF ioctl operations */ -extern int vrf_getaddrinfo(const char *node, const char *service, - const struct addrinfo *hints, - struct addrinfo **res, vrf_id_t vrf_id); -extern int vrf_sockunion_socket(const union sockunion *su, vrf_id_t vrf_id); -/* VRF switch from NETNS */ -extern int vrf_switch_to_netns(vrf_id_t vrf_id); -extern int vrf_switchback_to_initial(void); - -/* used by NS when vrf backend is NS. - * Notify a change in the VRF ID of the VRF - */ -extern int vrf_update_vrf_id(vrf_id_t vrf_id, struct vrf *vrf); -extern void vrf_disable(struct vrf *vrf); -extern int vrf_enable(struct vrf *vrf); - /* - * VRF Debugging + * VRF backend routines + * should be called from zebra only + */ + +/* VRF vty command initialisation + */ +extern void vrf_cmd_init(int (*writefunc)(struct vty *vty)); + +/* VRF vty debugging */ extern void vrf_install_commands(void); +/* + * VRF utilities + */ -/* The default VRF ID */ -#define VRF_DEFAULT vrf_get_default_id() +/* API for configuring VRF backend + * should be called from zebra only + */ +extern void vrf_configure_backend(int vrf_backend_netns); +extern int vrf_get_backend(void); +extern int vrf_is_backend_netns(void); + + +/* API to create a VRF. either from vty + * or through discovery + */ +extern int vrf_handler_create(struct vty *vty, + const char *name, + struct vrf **vrf); + +/* API to associate a VRF with a NETNS. + * called either from vty or through discovery + * should be called from zebra only + */ +extern int vrf_netns_handler_create(struct vty *vty, struct vrf *vrf, + char *pathname, ns_id_t ns_id); + +/* used internally to enable or disable VRF. + * Notify a change in the VRF ID of the VRF + */ +extern void vrf_disable(struct vrf *vrf); +extern int vrf_enable(struct vrf *vrf); #endif /*_ZEBRA_VRF_H*/ From 736d41ad74f1135256ece64873fb261164ed03e0 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Mon, 5 Feb 2018 16:30:21 +0100 Subject: [PATCH 84/98] zebra: adapt the vrf and logical router initialisation The zebra daemon introduces the logical router initialisation. Because right now, the usage of logical router and vrf NETNS is exclusive, then the logical router and VRF are initialised accordingly. Signed-off-by: Philippe Guibert --- zebra/main.c | 5 +++++ zebra/zebra_netns_notify.c | 2 +- zebra/zebra_ns.c | 25 ++++++++++++++++++++++--- zebra/zebra_vrf.c | 4 ++-- zebra/zebra_vrf.h | 7 +++---- 5 files changed, 33 insertions(+), 10 deletions(-) diff --git a/zebra/main.c b/zebra/main.c index 9cad913f4b..749d509a86 100644 --- a/zebra/main.c +++ b/zebra/main.c @@ -34,6 +34,7 @@ #include "privs.h" #include "sigevent.h" #include "vrf.h" +#include "logicalrouter.h" #include "libfrr.h" #include "zebra/rib.h" @@ -207,6 +208,8 @@ int main(int argc, char **argv) #endif vrf_configure_backend(VRF_BACKEND_VRF_LITE); + logicalrouter_configure_backend( + LOGICALROUTER_BACKEND_NETNS); frr_preinit(&zebra_di, argc, argv); @@ -285,6 +288,8 @@ int main(int argc, char **argv) break; case 'n': vrf_configure_backend(VRF_BACKEND_NETNS); + logicalrouter_configure_backend( + LOGICALROUTER_BACKEND_OFF); break; #endif /* HAVE_NETLINK */ #if defined(HANDLE_ZAPI_FUZZING) diff --git a/zebra/zebra_netns_notify.c b/zebra/zebra_netns_notify.c index 8940546f68..b28998acf8 100644 --- a/zebra/zebra_netns_notify.c +++ b/zebra/zebra_netns_notify.c @@ -84,7 +84,7 @@ static void zebra_ns_notify_create_context_from_entry_name(const char *name) ns_id = zebra_ns_id_get(netnspath); if (zserv_privs.change(ZPRIVS_LOWER)) zlog_err("Can't lower privileges"); - ret = ns_handler_create(NULL, vrf, netnspath, ns_id); + ret = vrf_netns_handler_create(NULL, vrf, netnspath, ns_id); if (ret != CMD_SUCCESS) { zlog_warn("NS notify : failed to create NS %s", netnspath); return; diff --git a/zebra/zebra_ns.c b/zebra/zebra_ns.c index da5b22def2..cb302985c8 100644 --- a/zebra/zebra_ns.c +++ b/zebra/zebra_ns.c @@ -23,6 +23,7 @@ #include "lib/ns.h" #include "lib/vrf.h" +#include "lib/logicalrouter.h" #include "lib/prefix.h" #include "lib/memory.h" @@ -59,6 +60,8 @@ zebra_ns_table_entry_compare(const struct zebra_ns_table *e1, return e1->tableid - e2->tableid; } +static int logicalrouter_config_write(struct vty *vty); + struct zebra_ns *zebra_ns_lookup(ns_id_t ns_id) { if (ns_id == NS_DEFAULT) @@ -241,9 +244,10 @@ int zebra_ns_init(void) ns_id = zebra_ns_id_get_default(); if (zserv_privs.change(ZPRIVS_LOWER)) zlog_err("Can't lower privileges"); - ns_init_zebra(ns_id); - ns_init(); + ns_init_management(ns_id); + + logicalrouter_init(logicalrouter_config_write); /* Do any needed per-NS data structure allocation. */ dzns->if_table = route_table_init(); @@ -253,7 +257,7 @@ int zebra_ns_init(void) zebra_vrf_init(); /* Default NS is activated */ - zebra_ns_enable(NS_DEFAULT, (void **)&dzns); + zebra_ns_enable(ns_id, (void **)&dzns); if (vrf_is_backend_netns()) { ns_add_hook(NS_NEW_HOOK, zebra_ns_new); @@ -266,6 +270,21 @@ int zebra_ns_init(void) return 0; } +static int logicalrouter_config_write(struct vty *vty) +{ + struct ns *ns; + int write = 0; + + RB_FOREACH(ns, ns_head, &ns_tree) { + if (ns->ns_id == NS_DEFAULT || ns->name == NULL) + continue; + vty_out(vty, "logical-router %u netns %s\n", ns->ns_id, + ns->name); + write = 1; + } + return write; +} + int zebra_ns_config_write(struct vty *vty, struct ns *ns) { if (ns && ns->name != NULL) diff --git a/zebra/zebra_vrf.c b/zebra/zebra_vrf.c index 874a9c74e7..bb15fd04f3 100644 --- a/zebra/zebra_vrf.c +++ b/zebra/zebra_vrf.c @@ -581,8 +581,8 @@ static int vrf_config_write(struct vty *vty) /* Zebra VRF initialization. */ void zebra_vrf_init(void) { - vrf_init(zebra_vrf_new, zebra_vrf_enable, zebra_vrf_disable, - zebra_vrf_delete); + vrf_init(zebra_vrf_new, zebra_vrf_enable, + zebra_vrf_disable, zebra_vrf_delete); vrf_cmd_init(vrf_config_write); } diff --git a/zebra/zebra_vrf.h b/zebra/zebra_vrf.h index ae5a174116..4c12d7dee9 100644 --- a/zebra/zebra_vrf.h +++ b/zebra/zebra_vrf.h @@ -19,10 +19,9 @@ * with this program; see the file COPYING; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#if !defined(__ZEBRA_RIB_H__) -#define __ZEBRA_RIB_H__ +#if !defined(__ZEBRA_VRF_H__) +#define __ZEBRA_VRF_H__ -#include #include #include #include @@ -169,4 +168,4 @@ extern void zebra_vrf_init(void); extern void zebra_rtable_node_cleanup(struct route_table *table, struct route_node *node); -#endif +#endif /* ZEBRA_VRF_H */ From f5d20fdb4e1fae6ccdcd3efa9136663481a5e355 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Mon, 5 Feb 2018 16:36:13 +0100 Subject: [PATCH 85/98] vtysh: change logical router node name The logical router node goes from NS_NODE to LOGICALROUTER_NODE. Vty commands are renamed accordingly. Signed-off-by: Philippe Guibert --- vtysh/Makefile.am | 2 +- vtysh/extract.pl.in | 3 +++ vtysh/vtysh.c | 40 +++++++++++++++++++++++++++------------- vtysh/vtysh_config.c | 4 ++-- 4 files changed, 33 insertions(+), 16 deletions(-) diff --git a/vtysh/Makefile.am b/vtysh/Makefile.am index c9b6f50160..33d34fc0dd 100644 --- a/vtysh/Makefile.am +++ b/vtysh/Makefile.am @@ -147,7 +147,7 @@ vtysh_cmd_FILES = $(vtysh_scan) \ $(top_srcdir)/lib/distribute.c $(top_srcdir)/lib/if_rmap.c \ $(top_srcdir)/lib/vrf.c \ $(top_srcdir)/lib/vty.c $(top_srcdir)/zebra/debug.c \ - $(top_srcdir)/lib/ns.c \ + $(top_srcdir)/lib/logicalrouter.c \ $(top_srcdir)/zebra/interface.c \ $(top_srcdir)/zebra/irdp_interface.c \ $(top_srcdir)/zebra/rtadv.c $(top_srcdir)/zebra/zebra_vty.c \ diff --git a/vtysh/extract.pl.in b/vtysh/extract.pl.in index dedf3d1647..6cfb51b00f 100755 --- a/vtysh/extract.pl.in +++ b/vtysh/extract.pl.in @@ -87,6 +87,9 @@ foreach (@ARGV) { elsif ($file =~ /lib\/vrf\.c$/) { $protocol = "VTYSH_ALL"; } + elsif ($file =~ /lib\/logicalrouter\.c$/) { + $protocol = "VTYSH_ALL"; + } elsif ($file =~ /lib\/filter\.c$/) { $protocol = "VTYSH_ALL"; } diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index 65e9c9f8c5..e0a0dd585d 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -975,8 +975,8 @@ static struct cmd_node pw_node = { PW_NODE, "%s(config-pw)# ", }; -static struct cmd_node ns_node = { - NS_NODE, "%s(config-logical-router)# ", +static struct cmd_node logicalrouter_node = { + LOGICALROUTER_NODE, "%s(config-logical-router)# ", }; static struct cmd_node vrf_node = { @@ -1508,7 +1508,7 @@ static int vtysh_exit(struct vty *vty) break; case INTERFACE_NODE: case PW_NODE: - case NS_NODE: + case LOGICALROUTER_NODE: case VRF_NODE: case ZEBRA_NODE: case BGP_NODE: @@ -1782,16 +1782,25 @@ DEFSH(VTYSH_ZEBRA, vtysh_no_interface_vrf_cmd, "no interface IFNAME vrf NAME", "Delete a pseudo interface's configuration\n" "Interface's name\n" VRF_CMD_HELP_STR) -DEFUNSH(VTYSH_NS, vtysh_ns, vtysh_ns_cmd, "logical-router (1-65535) ns NAME", +DEFUNSH(VTYSH_ZEBRA, vtysh_logicalrouter, vtysh_logicalrouter_cmd, + "logical-router (1-65535) ns NAME", "Enable a logical-router\n" "Specify the logical-router indentifier\n" "The Name Space\n" "The file name in " NS_RUN_DIR ", or a full pathname\n") { - vty->node = NS_NODE; + vty->node = LOGICALROUTER_NODE; return CMD_SUCCESS; } +DEFSH(VTYSH_ZEBRA, vtysh_no_logicalrouter_cmd, + "no logical-router (1-65535) ns NAME", + NO_STR + "Enable a Logical-Router\n" + "Specify the Logical-Router identifier\n" + "The Name Space\n" + "The file name in " NS_RUN_DIR ", or a full pathname\n") + DEFUNSH(VTYSH_VRF, vtysh_vrf, vtysh_vrf_cmd, "vrf NAME", "Select a VRF to configure\n" "VRF's name\n") @@ -1804,16 +1813,18 @@ DEFSH(VTYSH_ZEBRA, vtysh_no_vrf_cmd, "no vrf NAME", NO_STR "Delete a pseudo vrf's configuration\n" "VRF's name\n") -DEFUNSH(VTYSH_NS, vtysh_exit_ns, vtysh_exit_ns_cmd, "exit", +DEFUNSH(VTYSH_NS, vtysh_exit_logicalrouter, + vtysh_exit_logicalrouter_cmd, "exit", "Exit current mode and down to previous mode\n") { return vtysh_exit(vty); } -DEFUNSH(VTYSH_NS, vtysh_quit_ns, vtysh_quit_ns_cmd, "quit", +DEFUNSH(VTYSH_NS, vtysh_quit_logicalrouter, + vtysh_quit_logicalrouter_cmd, "quit", "Exit current mode and down to previous mode\n") { - return vtysh_exit_ns(self, vty, argc, argv); + return vtysh_exit_logicalrouter(self, vty, argc, argv); } DEFUNSH(VTYSH_VRF, vtysh_exit_vrf, vtysh_exit_vrf_cmd, "exit", @@ -3055,7 +3066,7 @@ void vtysh_init_vty(void) install_node(&interface_node, NULL); install_node(&pw_node, NULL); install_node(&link_params_node, NULL); - install_node(&ns_node, NULL); + install_node(&logicalrouter_node, NULL); install_node(&vrf_node, NULL); install_node(&rmap_node, NULL); install_node(&zebra_node, NULL); @@ -3233,11 +3244,14 @@ void vtysh_init_vty(void) install_element(PW_NODE, &vtysh_exit_interface_cmd); install_element(PW_NODE, &vtysh_quit_interface_cmd); - install_element(NS_NODE, &vtysh_end_all_cmd); + install_element(LOGICALROUTER_NODE, &vtysh_end_all_cmd); - install_element(CONFIG_NODE, &vtysh_ns_cmd); - install_element(NS_NODE, &vtysh_exit_ns_cmd); - install_element(NS_NODE, &vtysh_quit_ns_cmd); + install_element(CONFIG_NODE, &vtysh_logicalrouter_cmd); + install_element(CONFIG_NODE, &vtysh_no_logicalrouter_cmd); + install_element(LOGICALROUTER_NODE, + &vtysh_exit_logicalrouter_cmd); + install_element(LOGICALROUTER_NODE, + &vtysh_quit_logicalrouter_cmd); install_element(VRF_NODE, &vtysh_end_all_cmd); install_element(VRF_NODE, &vtysh_exit_vrf_cmd); diff --git a/vtysh/vtysh_config.c b/vtysh/vtysh_config.c index 967f855fbc..aa1dd407eb 100644 --- a/vtysh/vtysh_config.c +++ b/vtysh/vtysh_config.c @@ -187,7 +187,7 @@ void vtysh_config_parse_line(void *arg, const char *line) config->index = INTERFACE_NODE; } else if (config->index == RMAP_NODE || config->index == INTERFACE_NODE - || config->index == NS_NODE + || config->index == LOGICALROUTER_NODE || config->index == VTY_NODE || config->index == VRF_NODE) config_add_line_uniq(config->line, line); @@ -202,7 +202,7 @@ void vtysh_config_parse_line(void *arg, const char *line) else if (strncmp(line, "pseudowire", strlen("pseudowire")) == 0) config = config_get(PW_NODE, line); else if (strncmp(line, "logical-router", strlen("ns")) == 0) - config = config_get(NS_NODE, line); + config = config_get(LOGICALROUTER_NODE, line); else if (strncmp(line, "vrf", strlen("vrf")) == 0) config = config_get(VRF_NODE, line); else if (strncmp(line, "router-id", strlen("router-id")) == 0) From 9dab51ac71bddf2b4e2a5e1c3a185cb50c935301 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Mon, 5 Feb 2018 16:37:49 +0100 Subject: [PATCH 86/98] bgpd: no need to initialise netns directly NETNS is initialised from the VRF, instead of being directly called, because this is not up to BGP daemon to initialise the various VRF backend. Signed-off-by: Philippe Guibert --- bgpd/bgp_main.c | 1 - 1 file changed, 1 deletion(-) diff --git a/bgpd/bgp_main.c b/bgpd/bgp_main.c index 717fe09762..82c74e4afa 100644 --- a/bgpd/bgp_main.c +++ b/bgpd/bgp_main.c @@ -301,7 +301,6 @@ static int bgp_vrf_disable(struct vrf *vrf) static void bgp_vrf_init(void) { - ns_init(); vrf_init(bgp_vrf_new, bgp_vrf_enable, bgp_vrf_disable, bgp_vrf_delete); } From 516d7591d68cf4537bb7f0603ff703e808381d03 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Mon, 5 Feb 2018 17:00:45 +0100 Subject: [PATCH 87/98] lib: add vrf_ioctl API That API can be used to wrap the ioctl call with various vrf instances. This permits transparently doing the ioctl() call without taking into consideration the vrf backend kind. Signed-off-by: Philippe Guibert --- lib/vrf.c | 20 ++++++++++++++++++++ lib/vrf.h | 2 ++ zebra/if_netlink.c | 13 +------------ 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/lib/vrf.c b/lib/vrf.c index 0ca517d051..890a7adca2 100644 --- a/lib/vrf.c +++ b/lib/vrf.c @@ -815,6 +815,26 @@ int vrf_getaddrinfo(const char *node, const char *service, return ret; } +int vrf_ioctl(vrf_id_t vrf_id, int d, unsigned long request, char *params) +{ + int ret, saved_errno, rc; + + ret = vrf_switch_to_netns(vrf_id); + if (ret < 0) { + zlog_err("%s: Can't switch to VRF %u (%s)", + __func__, vrf_id, safe_strerror(errno)); + return 0; + } + rc = ioctl(d, request, params); + saved_errno = errno; + ret = vrf_switchback_to_initial(); + if (ret < 0) + zlog_err("%s: Can't switchback from VRF %u (%s)", + __func__, vrf_id, safe_strerror(errno)); + errno = saved_errno; + return rc; +} + int vrf_sockunion_socket(const union sockunion *su, vrf_id_t vrf_id) { int ret, save_errno, ret2; diff --git a/lib/vrf.h b/lib/vrf.h index 326418791d..cb4159a0a3 100644 --- a/lib/vrf.h +++ b/lib/vrf.h @@ -216,6 +216,8 @@ extern int vrf_getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res, vrf_id_t vrf_id); +extern int vrf_ioctl(vrf_id_t vrf_id, int d, unsigned long request, char *args); + /* function called by macro VRF_DEFAULT * to get the default VRF_ID */ diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c index a02533c1fe..65f1e56587 100644 --- a/zebra/if_netlink.c +++ b/zebra/if_netlink.c @@ -351,7 +351,6 @@ static int get_iflink_speed(struct interface *interface) struct ethtool_cmd ecmd; int sd; int rc; - int ret, saved_errno; const char *ifname = interface->name; /* initialize struct */ @@ -376,17 +375,7 @@ static int get_iflink_speed(struct interface *interface) return 0; } /* Get the current link state for the interface */ - ret = vrf_switch_to_netns(interface->vrf_id); - if (ret < 0) - zlog_err("%s: Can't switch to VRF %u (%s)", - __func__, interface->vrf_id, safe_strerror(errno)); - rc = ioctl(sd, SIOCETHTOOL, (char *)&ifdata); - saved_errno = errno; - ret = vrf_switchback_to_initial(); - if (ret < 0) - zlog_err("%s: Can't switchback from VRF %u (%s)", - __func__, interface->vrf_id, safe_strerror(errno)); - errno = saved_errno; + rc = vrf_ioctl(interface->vrf_id, sd, SIOCETHTOOL, (char *)&ifdata); if (zserv_privs.change(ZPRIVS_LOWER)) zlog_err("Can't lower privileges"); if (rc < 0) { From 0f4977c6689441e7b3075fc7a82c1ddc9ffdfa1c Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Mon, 5 Feb 2018 17:28:51 +0100 Subject: [PATCH 88/98] lib: add vrf-lite bind capability to vrf APIs Because socket creation is tightly linked with socket binding for vrf lite, the proposal is made to extend socket creation APIs and to create a new API called vrf_bind that applies to vrf lite. The passed interface name is the interface that will be bound to the socket passed. Signed-off-by: Philippe Guibert --- bgpd/bgp_network.c | 4 ++-- lib/vrf.c | 38 ++++++++++++++++++++++++++++++++++++-- lib/vrf.h | 8 ++++++-- zebra/if_netlink.c | 3 ++- 4 files changed, 46 insertions(+), 7 deletions(-) diff --git a/bgpd/bgp_network.c b/bgpd/bgp_network.c index 59c59f924e..240a23d198 100644 --- a/bgpd/bgp_network.c +++ b/bgpd/bgp_network.c @@ -571,7 +571,7 @@ int bgp_connect(struct peer *peer) if (bgpd_privs.change(ZPRIVS_RAISE)) zlog_err("Can't raise privileges"); /* Make socket for the peer. */ - peer->fd = vrf_sockunion_socket(&peer->su, peer->bgp->vrf_id); + peer->fd = vrf_sockunion_socket(&peer->su, peer->bgp->vrf_id, NULL); if (bgpd_privs.change(ZPRIVS_LOWER)) zlog_err("Can't lower privileges"); if (peer->fd < 0) @@ -751,7 +751,7 @@ int bgp_socket(struct bgp *bgp, unsigned short port, const char *address) if (bgpd_privs.change(ZPRIVS_RAISE)) zlog_err("Can't raise privileges"); sock = vrf_socket(ainfo->ai_family, ainfo->ai_socktype, - ainfo->ai_protocol, bgp->vrf_id); + ainfo->ai_protocol, bgp->vrf_id, NULL); if (bgpd_privs.change(ZPRIVS_LOWER)) zlog_err("Can't lower privileges"); if (sock < 0) { diff --git a/lib/vrf.c b/lib/vrf.c index 890a7adca2..ea106b90a2 100644 --- a/lib/vrf.c +++ b/lib/vrf.c @@ -493,7 +493,8 @@ void vrf_terminate(void) } /* Create a socket for the VRF. */ -int vrf_socket(int domain, int type, int protocol, vrf_id_t vrf_id) +int vrf_socket(int domain, int type, int protocol, vrf_id_t vrf_id, + char *interfacename) { int ret, save_errno, ret2; @@ -508,6 +509,13 @@ int vrf_socket(int domain, int type, int protocol, vrf_id_t vrf_id) zlog_err("%s: Can't switchback from VRF %u (%s)", __func__, vrf_id, safe_strerror(errno)); errno = save_errno; + if (ret <= 0) + return ret; + ret2 = vrf_bind(vrf_id, ret, interfacename); + if (ret2 < 0) { + close(ret); + ret = ret2; + } return ret; } @@ -795,6 +803,23 @@ vrf_id_t vrf_get_default_id(void) return VRF_DEFAULT_INTERNAL; } +int vrf_bind(vrf_id_t vrf_id, int fd, char *name) +{ + int ret = 0; + + if (fd < 0 || name == NULL) + return fd; + if (vrf_is_mapped_on_netns(vrf_id)) + return fd; +#ifdef SO_BINDTODEVICE + ret = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, name, + strlen(name)); + if (ret < 0) + zlog_debug("bind to interface %s failed, errno=%d", + name, errno); +#endif /* SO_BINDTODEVICE */ + return ret; +} int vrf_getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res, vrf_id_t vrf_id) @@ -835,7 +860,8 @@ int vrf_ioctl(vrf_id_t vrf_id, int d, unsigned long request, char *params) return rc; } -int vrf_sockunion_socket(const union sockunion *su, vrf_id_t vrf_id) +int vrf_sockunion_socket(const union sockunion *su, vrf_id_t vrf_id, + char *interfacename) { int ret, save_errno, ret2; @@ -850,5 +876,13 @@ int vrf_sockunion_socket(const union sockunion *su, vrf_id_t vrf_id) zlog_err("%s: Can't switchback from VRF %u (%s)", __func__, vrf_id, safe_strerror(errno)); errno = save_errno; + + if (ret <= 0) + return ret; + ret2 = vrf_bind(vrf_id, ret, interfacename); + if (ret2 < 0) { + close(ret); + ret = ret2; + } return ret; } diff --git a/lib/vrf.h b/lib/vrf.h index cb4159a0a3..062e6f3d8d 100644 --- a/lib/vrf.h +++ b/lib/vrf.h @@ -207,9 +207,13 @@ extern void vrf_terminate(void); /* Create a socket serving for the given VRF */ extern int vrf_socket(int domain, int type, - int protocol, vrf_id_t vrf_id); + int protocol, vrf_id_t vrf_id, + char *name); + extern int vrf_sockunion_socket(const union sockunion *su, - vrf_id_t vrf_id); + vrf_id_t vrf_id, char *name); + +extern int vrf_bind(vrf_id_t vrf_id, int fd, char *name); /* VRF ioctl operations */ extern int vrf_getaddrinfo(const char *node, const char *service, diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c index 65f1e56587..639f70a6b4 100644 --- a/zebra/if_netlink.c +++ b/zebra/if_netlink.c @@ -367,7 +367,8 @@ static int get_iflink_speed(struct interface *interface) /* use ioctl to get IP address of an interface */ if (zserv_privs.change(ZPRIVS_RAISE)) zlog_err("Can't raise privileges"); - sd = vrf_socket(PF_INET, SOCK_DGRAM, IPPROTO_IP, interface->vrf_id); + sd = vrf_socket(PF_INET, SOCK_DGRAM, IPPROTO_IP, + interface->vrf_id, NULL); if (sd < 0) { if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug("Failure to read interface %s speed: %d %s", From 97896a91c2a7c8d856a1326cd4017f458e4a5493 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Mon, 5 Feb 2018 17:39:37 +0100 Subject: [PATCH 89/98] bgpd: make bgpd rely on vrf_bind() API usage Instead of relying on local usage of vrf bind operation, the vrf API for that usage is done. Signed-off-by: Philippe Guibert fixup bgp --- bgpd/bgp_network.c | 53 ++++++++++++---------------------------------- 1 file changed, 13 insertions(+), 40 deletions(-) diff --git a/bgpd/bgp_network.c b/bgpd/bgp_network.c index 240a23d198..0ab583f444 100644 --- a/bgpd/bgp_network.c +++ b/bgpd/bgp_network.c @@ -45,7 +45,7 @@ extern struct zebra_privs_t bgpd_privs; -static int bgp_bind(struct peer *); +static char *bgp_get_bound_name(struct peer *peer); /* BGP listening socket. */ struct bgp_listener { @@ -415,7 +415,7 @@ static int bgp_accept(struct thread *thread) peer->doppelganger = peer1; peer1->doppelganger = peer; peer->fd = bgp_sock; - bgp_bind(peer); + vrf_bind(peer->bgp->vrf_id, bgp_sock, bgp_get_bound_name(peer)); bgp_fsm_change_status(peer, Active); BGP_TIMER_OFF(peer->t_start); /* created in peer_create() */ @@ -443,23 +443,20 @@ static int bgp_accept(struct thread *thread) } /* BGP socket bind. */ -static int bgp_bind(struct peer *peer) +static char *bgp_get_bound_name(struct peer *peer) { -#ifdef SO_BINDTODEVICE - int ret; - int myerrno; char *name = NULL; - /* If not bound to an interface or part of a VRF lite, we don't care. */ if ((peer->bgp->vrf_id == VRF_DEFAULT) && !peer->ifname && !peer->conf_if) - return 0; - if (vrf_is_mapped_on_netns(peer->bgp->vrf_id)) - return 0; + return NULL; + if (peer->su.sa.sa_family != AF_INET && peer->su.sa.sa_family != AF_INET6) - return 0; // unexpected + return NULL; // unexpected + if (!peer) + return name; /* For IPv6 peering, interface (unnumbered or link-local with interface) * takes precedence over VRF. For IPv4 peering, explicit interface or * VRF are the situations to bind. @@ -471,30 +468,7 @@ static int bgp_bind(struct peer *peer) else name = peer->ifname ? peer->ifname : peer->bgp->name; - if (!name) - return 0; - - if (bgp_debug_neighbor_events(peer)) - zlog_debug("%s Binding to interface %s", peer->host, name); - - if (bgpd_privs.change(ZPRIVS_RAISE)) - zlog_err("bgp_bind: could not raise privs"); - - ret = setsockopt(peer->fd, SOL_SOCKET, SO_BINDTODEVICE, name, - strlen(name)); - myerrno = errno; - - if (bgpd_privs.change(ZPRIVS_LOWER)) - zlog_err("bgp_bind: could not lower privs"); - - if (ret < 0) { - if (bgp_debug_neighbor_events(peer)) - zlog_debug("bind to interface %s failed, errno=%d", - name, myerrno); - return ret; - } -#endif /* SO_BINDTODEVICE */ - return 0; + return name; } static int bgp_update_address(struct interface *ifp, const union sockunion *dst, @@ -571,7 +545,8 @@ int bgp_connect(struct peer *peer) if (bgpd_privs.change(ZPRIVS_RAISE)) zlog_err("Can't raise privileges"); /* Make socket for the peer. */ - peer->fd = vrf_sockunion_socket(&peer->su, peer->bgp->vrf_id, NULL); + peer->fd = vrf_sockunion_socket(&peer->su, peer->bgp->vrf_id, + bgp_get_bound_name(peer)); if (bgpd_privs.change(ZPRIVS_LOWER)) zlog_err("Can't lower privileges"); if (peer->fd < 0) @@ -605,9 +580,6 @@ int bgp_connect(struct peer *peer) if (peer->password) bgp_md5_set_connect(peer->fd, &peer->su, peer->password); - /* Bind socket. */ - bgp_bind(peer); - /* Update source bind. */ if (bgp_update_source(peer) < 0) { return connect_error; @@ -751,7 +723,8 @@ int bgp_socket(struct bgp *bgp, unsigned short port, const char *address) if (bgpd_privs.change(ZPRIVS_RAISE)) zlog_err("Can't raise privileges"); sock = vrf_socket(ainfo->ai_family, ainfo->ai_socktype, - ainfo->ai_protocol, bgp->vrf_id, NULL); + ainfo->ai_protocol, bgp->vrf_id, + NULL); if (bgpd_privs.change(ZPRIVS_LOWER)) zlog_err("Can't lower privileges"); if (sock < 0) { From 3c0eb8faa2936ed43c557fe375383d6d03646291 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Mon, 29 Jan 2018 16:56:11 +0100 Subject: [PATCH 90/98] ospfd: basic support for VRF NETNS backend The change consists in taking into account of the VRF identifier upon which the ospf socket is created. Moreover, if the VRF is a netns backend, then it is not necessary to perform the bind operations to vrf device. Also, when a VRF instance is enabled, it informs ospf VRF, and automatically OSPF VRF benefits from it. Reversely, when VRF instance is disabled, then OSPF VRF will be disabled too. Signed-off-by: Philippe Guibert --- ospfd/ospf_network.c | 41 ++++++++++------------------------------- ospfd/ospf_network.h | 1 - ospfd/ospfd.c | 29 +++++++++++++++++++---------- 3 files changed, 29 insertions(+), 42 deletions(-) diff --git a/ospfd/ospf_network.c b/ospfd/ospf_network.c index 022a5a138a..045634d8ab 100644 --- a/ospfd/ospf_network.c +++ b/ospfd/ospf_network.c @@ -169,42 +169,27 @@ int ospf_if_ipmulticast(struct ospf *top, struct prefix *p, ifindex_t ifindex) return ret; } -int ospf_bind_vrfdevice(struct ospf *ospf, int ospf_sock) -{ - int ret = 0; - -#ifdef SO_BINDTODEVICE - - if (ospf && ospf->vrf_id != VRF_DEFAULT && - ospf->vrf_id != VRF_UNKNOWN) { - ret = setsockopt(ospf_sock, SOL_SOCKET, SO_BINDTODEVICE, - ospf->name, - strlen(ospf->name)); - if (ret < 0) { - int save_errno = errno; - - zlog_warn("%s: Could not setsockopt SO_BINDTODEVICE %s", - __PRETTY_FUNCTION__, - safe_strerror(save_errno)); - } - - } -#endif - return ret; -} - int ospf_sock_init(struct ospf *ospf) { int ospf_sock; int ret, hincl = 1; int bufsize = (8 * 1024 * 1024); + /* silently ignore. already done */ + if (ospf->fd > 0) + return -1; + + if (ospf->vrf_id == VRF_UNKNOWN) { + /* silently return since VRF is not ready */ + return -1; + } if (ospfd_privs.change(ZPRIVS_RAISE)) { zlog_err("ospf_sock_init: could not raise privs, %s", safe_strerror(errno)); } - ospf_sock = socket(AF_INET, SOCK_RAW, IPPROTO_OSPFIGP); + ospf_sock = vrf_socket(AF_INET, SOCK_RAW, IPPROTO_OSPFIGP, + ospf->vrf_id, ospf->name); if (ospf_sock < 0) { int save_errno = errno; @@ -216,12 +201,6 @@ int ospf_sock_init(struct ospf *ospf) exit(1); } - ret = ospf_bind_vrfdevice(ospf, ospf_sock); - if (ret < 0) { - close(ospf_sock); - goto out; - } - #ifdef IP_HDRINCL /* we will include IP header with packet */ ret = setsockopt(ospf_sock, IPPROTO_IP, IP_HDRINCL, &hincl, diff --git a/ospfd/ospf_network.h b/ospfd/ospf_network.h index 41a7abda70..cbaf132327 100644 --- a/ospfd/ospf_network.h +++ b/ospfd/ospf_network.h @@ -30,6 +30,5 @@ extern int ospf_if_add_alldrouters(struct ospf *, struct prefix *, ifindex_t); extern int ospf_if_drop_alldrouters(struct ospf *, struct prefix *, ifindex_t); extern int ospf_if_ipmulticast(struct ospf *, struct prefix *, ifindex_t); extern int ospf_sock_init(struct ospf *ospf); -extern int ospf_bind_vrfdevice(struct ospf *, int); #endif /* _ZEBRA_OSPF_NETWORK_H */ diff --git a/ospfd/ospfd.c b/ospfd/ospfd.c index 68c682c6c4..79af4a55fb 100644 --- a/ospfd/ospfd.c +++ b/ospfd/ospfd.c @@ -308,12 +308,6 @@ static struct ospf *ospf_new(u_short instance, const char *name) new->lsa_refresh_interval, &new->t_lsa_refresher); new->lsa_refresher_started = monotime(NULL); - if ((ospf_sock_init(new)) < 0) { - zlog_err( - "ospf_new: fatal error: ospf_sock_init was unable to open " - "a socket"); - exit(1); - } if ((new->ibuf = stream_new(OSPF_MAX_PACKET_SIZE + 1)) == NULL) { zlog_err( "ospf_new: fatal error: stream_new(%u) failed allocating ibuf", @@ -321,7 +315,6 @@ static struct ospf *ospf_new(u_short instance, const char *name) exit(1); } new->t_read = NULL; - thread_add_read(master, ospf_read, new, new->fd, &new->t_read); new->oi_write_q = list_new(); new->write_oi_count = OSPF_WRITE_INTERFACE_COUNT_DEFAULT; @@ -332,6 +325,16 @@ static struct ospf *ospf_new(u_short instance, const char *name) QOBJ_REG(new, ospf); + new->fd = -1; + if ((ospf_sock_init(new)) < 0) { + if (new->vrf_id != VRF_UNKNOWN) + zlog_warn( + "%s: ospf_sock_init is unable to open a socket", + __func__); + return new; + } + thread_add_read(master, ospf_read, new, new->fd, &new->t_read); + return new; } @@ -2050,6 +2053,7 @@ static int ospf_vrf_enable(struct vrf *vrf) { struct ospf *ospf = NULL; vrf_id_t old_vrf_id; + int ret = 0; if (IS_DEBUG_OSPF_EVENT) zlog_debug("%s: VRF %s id %u enabled", @@ -2070,13 +2074,15 @@ static int ospf_vrf_enable(struct vrf *vrf) zlog_err("ospf_sock_init: could not raise privs, %s", safe_strerror(errno)); } - if (ospf_bind_vrfdevice(ospf, ospf->fd) < 0) - return 0; + ret = ospf_sock_init(ospf); if (ospfd_privs.change(ZPRIVS_LOWER)) { zlog_err("ospf_sock_init: could not lower privs, %s", safe_strerror(errno)); } - + if (ret < 0 || ospf->fd <= 0) + return 0; + thread_add_read(master, ospf_read, ospf, + ospf->fd, &ospf->t_read); ospf->oi_running = 1; ospf_zebra_vrf_register(ospf); ospf_router_id_update(ospf); @@ -2111,6 +2117,9 @@ static int ospf_vrf_disable(struct vrf *vrf) if (IS_DEBUG_OSPF_EVENT) zlog_debug("%s: ospf old_vrf_id %d unlinked", __PRETTY_FUNCTION__, old_vrf_id); + thread_cancel(ospf->t_read); + close(ospf->fd); + ospf->fd = -1; } /* Note: This is a callback, the VRF will be deleted by the caller. */ From a2c999f21f037bcb5d86ff060fb2b08e9709a969 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Mon, 12 Feb 2018 23:00:04 +0100 Subject: [PATCH 91/98] lib: add debug guard for ns informational traces Informational traces are being added. Signed-off-by: Philippe Guibert --- lib/netns_linux.c | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/lib/netns_linux.c b/lib/netns_linux.c index 025862440a..c14d2ea4b4 100644 --- a/lib/netns_linux.c +++ b/lib/netns_linux.c @@ -54,6 +54,8 @@ static struct ns *default_ns; static int ns_current_ns_fd; static int ns_default_ns_fd; +static int ns_debug; + #ifndef CLONE_NEWNET #define CLONE_NEWNET 0x40000000 /* New network namespace (lo, device, names sockets, etc) */ @@ -164,10 +166,12 @@ static struct ns *ns_get_created_internal(struct ns *ns, char *name, } if (!created) return ns; - if (ns->ns_id != NS_UNKNOWN) - zlog_info("NS %u is created.", ns->ns_id); - else - zlog_info("NS %s is created.", ns->name); + if (ns_debug) { + if (ns->ns_id != NS_UNKNOWN) + zlog_info("NS %u is created.", ns->ns_id); + else + zlog_info("NS %s is created.", ns->name); + } if (ns_master.ns_new_hook) (*ns_master.ns_new_hook) (ns); return ns; @@ -205,11 +209,12 @@ static int ns_enable_internal(struct ns *ns, void (*func)(ns_id_t, void *)) } if (func) func(ns->ns_id, (void *)ns->vrf_ctxt); - if (have_netns()) - zlog_info("NS %u is associated with NETNS %s.", - ns->ns_id, ns->name); - - zlog_info("NS %u is enabled.", ns->ns_id); + if (ns_debug) { + if (have_netns()) + zlog_info("NS %u is associated with NETNS %s.", + ns->ns_id, ns->name); + zlog_info("NS %u is enabled.", ns->ns_id); + } /* zebra first receives NS enable event, * then VRF enable event */ @@ -241,7 +246,9 @@ static int ns_is_enabled(struct ns *ns) static void ns_disable_internal(struct ns *ns) { if (ns_is_enabled(ns)) { - zlog_info("NS %u is to be disabled.", ns->ns_id); + if (ns_debug) + zlog_info("NS %u is to be disabled.", + ns->ns_id); if (ns_master.ns_disable_hook) (*ns_master.ns_disable_hook)(ns); @@ -266,7 +273,8 @@ int ns_have_netns(void) /* Delete a NS. This is called in ns_terminate(). */ void ns_delete(struct ns *ns) { - zlog_info("NS %u is to be deleted.", ns->ns_id); + if (ns_debug) + zlog_info("NS %u is to be deleted.", ns->ns_id); ns_disable(ns); @@ -398,6 +406,7 @@ void ns_init(void) { static int ns_initialised; + ns_debug = 0; /* silently return as initialisation done */ if (ns_initialised == 1) return; @@ -436,7 +445,9 @@ void ns_init_management(ns_id_t default_ns_id) } /* Set the default NS name. */ default_ns->name = XSTRDUP(MTYPE_NS_NAME, NS_DEFAULT_NAME); - zlog_info("%s: default NSID is %u", __func__, default_ns->ns_id); + if (ns_debug) + zlog_info("%s: default NSID is %u", + __func__, default_ns->ns_id); /* Enable the default NS. */ if (!ns_enable(default_ns, NULL)) { From c485b14bdc0d8e1fb2d6ad19057bf328e4118cfd Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Tue, 13 Feb 2018 10:03:43 +0100 Subject: [PATCH 92/98] lib: netns checkstyle fix A space is appended between RB_FOREACH and ' ', to comply with style practiced in frr. Signed-off-by: Philippe Guibert --- lib/netns_linux.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/netns_linux.c b/lib/netns_linux.c index c14d2ea4b4..c2282f844c 100644 --- a/lib/netns_linux.c +++ b/lib/netns_linux.c @@ -130,7 +130,7 @@ static struct ns *ns_lookup_name_internal(const char *name) { struct ns *ns = NULL; - RB_FOREACH(ns, ns_head, &ns_tree) { + RB_FOREACH (ns, ns_head, &ns_tree) { if (ns->name != NULL) { if (strcmp(name, ns->name) == 0) return ns; @@ -328,7 +328,7 @@ void ns_walk_func(int (*func)(struct ns *)) { struct ns *ns = NULL; - RB_FOREACH(ns, ns_head, &ns_tree) + RB_FOREACH (ns, ns_head, &ns_tree) func(ns); } From 281da0a94dcd14d4f628026ffd628c08df5104f1 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Mon, 26 Feb 2018 09:14:50 +0100 Subject: [PATCH 93/98] lib: do not use ENOSYS errno when returning from ns ENOSYS should not be used for other goals. Signed-off-by: Philippe Guibert --- lib/netns_linux.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/netns_linux.c b/lib/netns_linux.c index c2282f844c..0e955bade9 100644 --- a/lib/netns_linux.c +++ b/lib/netns_linux.c @@ -67,7 +67,7 @@ static inline int setns(int fd, int nstype) #ifdef __NR_setns return syscall(__NR_setns, fd, nstype); #else - errno = ENOSYS; + errno = EINVAL; return -1; #endif } @@ -480,7 +480,7 @@ int ns_switch_to_netns(const char *name) return -1; fd = open(name, O_RDONLY); if (fd == -1) { - errno = ENOSYS; + errno = EINVAL; return -1; } ret = setns(fd, CLONE_NEWNET); @@ -512,7 +512,7 @@ int ns_socket(int domain, int type, int protocol, ns_id_t ns_id) int ret; if (!ns || !ns_is_enabled(ns)) { - errno = ENOSYS; + errno = EINVAL; return -1; } if (have_netns()) { From 009f8ad5f3a99ae813d8a6739a1137443c83e345 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Tue, 13 Feb 2018 10:48:48 +0100 Subject: [PATCH 94/98] zebra: retrieve zns context from zvrf when netlink discovery So as to get the correct NETNS where some discovery must be done and populated, the zns pointer is directly retrieved from zvrf, instead of checking that the VRF is a backend NETNS or not. In the case where the interfaces are discovered before the VRF is enabled ( VRF-lite populate), then the default NS is retrieved. Signed-off-by: Philippe Guibert --- zebra/interface.c | 12 +++++------- zebra/rt_netlink.c | 28 ++++++++-------------------- 2 files changed, 13 insertions(+), 27 deletions(-) diff --git a/zebra/interface.c b/zebra/interface.c index e919d9f08f..7229b8818d 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -513,13 +513,14 @@ void if_add_update(struct interface *ifp) { struct zebra_if *if_data; struct zebra_ns *zns; + struct zebra_vrf *zvrf = vrf_info_lookup(ifp->vrf_id); - if (vrf_is_backend_netns()) - zns = zebra_ns_lookup((ns_id_t)ifp->vrf_id); + /* case interface populate before vrf enabled */ + if (zvrf->zns) + zns = zvrf->zns; else zns = zebra_ns_lookup(NS_DEFAULT); if_link_per_ns(zns, ifp); - if_data = ifp->info; assert(if_data); @@ -810,11 +811,8 @@ void if_nbr_ipv6ll_to_ipv4ll_neigh_update(struct interface *ifp, inet_pton(AF_INET, buf, &ipv4_ll); ipv6_ll_address_to_mac(address, (u_char *)mac); + ns_id = zvrf->zns->ns_id; - if (!vrf_is_backend_netns()) - ns_id = NS_DEFAULT; - else - ns_id = (ns_id_t)(ifp->vrf_id); /* * Remove existed arp record for the interface as netlink * protocol does not have update message types diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index 0371c6b99b..e26109badf 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -1337,10 +1337,7 @@ static int netlink_route_multipath(int cmd, struct prefix *p, struct zebra_ns *zns; struct zebra_vrf *zvrf = vrf_info_lookup(re->vrf_id); - if (!vrf_is_backend_netns()) - zns = zebra_ns_lookup(NS_DEFAULT); - else - zns = (struct zebra_ns *)ns_info_lookup(re->vrf_id); + zns = zvrf->zns; memset(&req, 0, sizeof req - NL_PKT_BUF_SIZE); bytelen = (family == AF_INET ? 4 : 16); @@ -1640,10 +1637,7 @@ int kernel_get_ipmr_sg_stats(struct zebra_vrf *zvrf, void *in) mroute = mr; struct zebra_ns *zns; - if (!vrf_is_backend_netns()) - zns = zebra_ns_lookup(NS_DEFAULT); - else - zns = (struct zebra_ns *)ns_info_lookup(zvrf->vrf->vrf_id); + zns = zvrf->zns; memset(&req.n, 0, sizeof(req.n)); memset(&req.ndm, 0, sizeof(req.ndm)); @@ -1736,11 +1730,9 @@ static int netlink_vxlan_flood_list_update(struct interface *ifp, char buf[256]; } req; u_char dst_mac[6] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0}; + struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(ifp->vrf_id); - if (!vrf_is_backend_netns()) - zns = zebra_ns_lookup(NS_DEFAULT); - else - zns = (struct zebra_ns *)ns_info_lookup(ifp->vrf_id); + zns = zvrf->zns; memset(&req.n, 0, sizeof(req.n)); memset(&req.ndm, 0, sizeof(req.ndm)); @@ -2046,11 +2038,9 @@ static int netlink_macfdb_update(struct interface *ifp, vlanid_t vid, int vid_present = 0, dst_present = 0; char vid_buf[20]; char dst_buf[30]; + struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(ifp->vrf_id); - if (!vrf_is_backend_netns()) - zns = zebra_ns_lookup(NS_DEFAULT); - else - zns = (struct zebra_ns *)ns_info_lookup(ifp->vrf_id); + zns = zvrf->zns; zif = ifp->info; if ((br_if = zif->brslave_info.br_if) == NULL) { zlog_warn("MAC %s on IF %s(%u) - no mapping to bridge", @@ -2355,11 +2345,9 @@ static int netlink_neigh_update2(struct interface *ifp, struct ipaddr *ip, struct zebra_ns *zns; char buf[INET6_ADDRSTRLEN]; char buf2[ETHER_ADDR_STRLEN]; + struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(ifp->vrf_id); - if (!vrf_is_backend_netns()) - zns = zebra_ns_lookup(NS_DEFAULT); - else - zns = (struct zebra_ns *)ns_info_lookup(ifp->vrf_id); + zns = zvrf->zns; memset(&req.n, 0, sizeof(req.n)); memset(&req.ndm, 0, sizeof(req.ndm)); From b7b816df6bd8b110aedb0f047fa8e3105ce86d1d Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Fri, 16 Feb 2018 18:22:34 +0100 Subject: [PATCH 95/98] zebra: prevent from discovering a NS with same NSID as previous one This limitation ignores the creation of a new NS context, when an already present NS is available with the same NSID. This limitation removes confusion, so that only the first NS will be used for configuration. Signed-off-by: Philippe Guibert --- zebra/zebra_netns_notify.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/zebra/zebra_netns_notify.c b/zebra/zebra_netns_notify.c index b28998acf8..b98d6ed703 100644 --- a/zebra/zebra_netns_notify.c +++ b/zebra/zebra_netns_notify.c @@ -75,15 +75,22 @@ static void zebra_ns_notify_create_context_from_entry_name(const char *name) if (netnspath == NULL) return; - if (vrf_handler_create(NULL, name, &vrf) != CMD_SUCCESS) { - zlog_warn("NS notify : failed to create VRF %s", name); - return; - } if (zserv_privs.change(ZPRIVS_RAISE)) zlog_err("Can't raise privileges"); ns_id = zebra_ns_id_get(netnspath); if (zserv_privs.change(ZPRIVS_LOWER)) zlog_err("Can't lower privileges"); + /* if VRF with NS ID already present */ + vrf = vrf_lookup_by_id((vrf_id_t)ns_id); + if (vrf) { + zlog_warn("NS notify : same NSID used by VRF %s. Ignore NS %s creation", + vrf->name, netnspath); + return; + } + if (vrf_handler_create(NULL, name, &vrf) != CMD_SUCCESS) { + zlog_warn("NS notify : failed to create VRF %s", name); + return; + } ret = vrf_netns_handler_create(NULL, vrf, netnspath, ns_id); if (ret != CMD_SUCCESS) { zlog_warn("NS notify : failed to create NS %s", netnspath); From 11d3c0c3f60c7150f1736d5c2caf9b6b07ce4006 Mon Sep 17 00:00:00 2001 From: Mladen Sablic Date: Tue, 27 Feb 2018 14:03:39 +0100 Subject: [PATCH 96/98] pimd: added mtrace caveat Added mtrace caveat to CAVEATS. Signed-off-by: Mladen Sablic --- pimd/CAVEATS | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pimd/CAVEATS b/pimd/CAVEATS index 43dd823ae5..120708ba14 100644 --- a/pimd/CAVEATS +++ b/pimd/CAVEATS @@ -173,4 +173,10 @@ C19 Provision to prevent group mode clash flow. There could be some provision to prevent such a behavior, but currently there is none. +C20 Multicast traceroute module is based on: + draft-ietf-idmr-traceroute-ipm-07 + It only implements, so far, weak traceroutes. The multicast routing + state of the router is not quieried but RPF path is followed along + PIM and IGMP enabled interfaces. + -x- From 1753f727b7fc3b2a1638eae2a49e0c931964c851 Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Mon, 26 Feb 2018 18:22:36 +0100 Subject: [PATCH 97/98] vtysh: pass route-map & prefix-list commands to isisd and sharpd Use VTYSH_RMAP in extract.pl instead of having two lists, and add isisd and sharpd to VTYSH_RMAP. --- vtysh/extract.pl.in | 6 +++--- vtysh/vtysh.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/vtysh/extract.pl.in b/vtysh/extract.pl.in index 6cfb51b00f..bdee0d47e2 100755 --- a/vtysh/extract.pl.in +++ b/vtysh/extract.pl.in @@ -82,7 +82,7 @@ foreach (@ARGV) { $protocol = "VTYSH_RIPD"; } elsif ($file =~ /lib\/routemap\.c$/) { - $protocol = "VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_ZEBRA|VTYSH_PIMD|VTYSH_EIGRPD|VTYSH_SHARPD"; + $protocol = "VTYSH_RMAP"; } elsif ($file =~ /lib\/vrf\.c$/) { $protocol = "VTYSH_ALL"; @@ -101,9 +101,9 @@ foreach (@ARGV) { } elsif ($file =~ /lib\/plist\.c$/) { if ($defun_array[1] =~ m/ipv6/) { - $protocol = "VTYSH_RIPNGD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_ZEBRA|VTYSH_BABELD"; + $protocol = "VTYSH_RIPNGD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_ZEBRA|VTYSH_BABELD|VTYSH_ISISD"; } else { - $protocol = "VTYSH_RIPD|VTYSH_OSPFD|VTYSH_BGPD|VTYSH_ZEBRA|VTYSH_PIMD|VTYSH_EIGRPD|VTYSH_BABELD"; + $protocol = "VTYSH_RIPD|VTYSH_OSPFD|VTYSH_BGPD|VTYSH_ZEBRA|VTYSH_PIMD|VTYSH_EIGRPD|VTYSH_BABELD|VTYSH_ISISD"; } } elsif ($file =~ /lib\/distribute\.c$/) { diff --git a/vtysh/vtysh.h b/vtysh/vtysh.h index ab13182094..f9b07beb7e 100644 --- a/vtysh/vtysh.h +++ b/vtysh/vtysh.h @@ -46,7 +46,7 @@ DECLARE_MGROUP(MVTYSH) * run on it (logging & co. should stay in a fixed/frozen config, and * things like prefix lists are not even initialised) */ #define VTYSH_ALL VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_LDPD|VTYSH_BGPD|VTYSH_ISISD|VTYSH_PIMD|VTYSH_NHRPD|VTYSH_EIGRPD|VTYSH_BABELD|VTYSH_SHARPD -#define VTYSH_RMAP VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_PIMD|VTYSH_EIGRPD +#define VTYSH_RMAP VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_ISISD|VTYSH_PIMD|VTYSH_EIGRPD|VTYSH_SHARPD #define VTYSH_INTERFACE VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_ISISD|VTYSH_PIMD|VTYSH_NHRPD|VTYSH_EIGRPD|VTYSH_BABELD #define VTYSH_NS VTYSH_ZEBRA #define VTYSH_VRF VTYSH_ZEBRA|VTYSH_PIMD From 546067dfb15491faf70c3282831a650388fc403e Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Mon, 26 Feb 2018 18:53:28 +0100 Subject: [PATCH 98/98] vtysh: Fix missing \n at end of desc string --- vtysh/vtysh.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index e0a0dd585d..8719226281 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -1446,7 +1446,7 @@ DEFUNSH(VTYSH_LDPD, ldp_member_pseudowire_ifname, DEFUNSH(VTYSH_ISISD, router_isis, router_isis_cmd, "router isis WORD", ROUTER_STR "ISO IS-IS\n" - "ISO Routing area tag") + "ISO Routing area tag\n") { vty->node = ISIS_NODE; return CMD_SUCCESS;