diff options
111 files changed, 2826 insertions, 702 deletions
diff --git a/bgpd/bgp_aspath.c b/bgpd/bgp_aspath.c index e02617691f..05e67baa8a 100644 --- a/bgpd/bgp_aspath.c +++ b/bgpd/bgp_aspath.c @@ -1632,7 +1632,7 @@ struct aspath *aspath_reconcile_as4(struct aspath *aspath, struct aspath *newpath = NULL, *mergedpath; int hops, cpasns = 0; - if (!aspath) + if (!aspath || !as4path) return NULL; seg = aspath->segments; diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index 2c52b57b36..6596e7cfa2 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -1513,6 +1513,9 @@ bgp_attr_munge_as4_attrs(struct peer *const peer, struct attr *const attr, if (!ignore_as4_path && (attr->flag & (ATTR_FLAG_BIT(BGP_ATTR_AS4_PATH)))) { newpath = aspath_reconcile_as4(attr->aspath, as4_path); + if (!newpath) + return BGP_ATTR_PARSE_ERROR; + aspath_unintern(&attr->aspath); attr->aspath = aspath_intern(newpath); } diff --git a/bgpd/bgp_clist.c b/bgpd/bgp_clist.c index 7cf1477549..0ffbe174ed 100644 --- a/bgpd/bgp_clist.c +++ b/bgpd/bgp_clist.c @@ -1054,6 +1054,9 @@ int extcommunity_list_set(struct community_list_handler *ch, const char *name, struct ecommunity *ecom = NULL; regex_t *regex = NULL; + if (str == NULL) + return COMMUNITY_LIST_ERR_MALFORMED_VAL; + entry = NULL; /* Get community list. */ @@ -1089,7 +1092,7 @@ int extcommunity_list_set(struct community_list_handler *ch, const char *name, entry = community_entry_new(); entry->direct = direct; entry->style = style; - entry->any = (str ? 0 : 1); + entry->any = 0; if (ecom) entry->config = ecommunity_ecom2str( ecom, ECOMMUNITY_FORMAT_COMMUNITY_LIST, 0); diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index 0557bbcce9..959418658c 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -4242,8 +4242,9 @@ void bgp_evpn_unconfigure_import_rt_for_vrf(struct bgp *bgp_vrf, if (node_to_del) list_delete_node(bgp_vrf->vrf_import_rtl, node_to_del); + assert(bgp_vrf->vrf_import_rtl); /* fallback to auto import rt, if this was the last RT */ - if (list_isempty(bgp_vrf->vrf_import_rtl)) { + if (bgp_vrf->vrf_import_rtl && list_isempty(bgp_vrf->vrf_import_rtl)) { UNSET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD); evpn_auto_rt_import_add_for_vrf(bgp_vrf); } diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c index cb71e06149..5a4ebc9b17 100644 --- a/bgpd/bgp_evpn_vty.c +++ b/bgpd/bgp_evpn_vty.c @@ -52,7 +52,6 @@ struct vni_walk_ctx { json_object *json; }; -#if defined(HAVE_CUMULUS) static void display_vrf_import_rt(struct vty *vty, struct vrf_irt_node *irt, json_object *json) { @@ -245,7 +244,7 @@ static void display_import_rt(struct vty *vty, struct irt_node *irt, for (ALL_LIST_ELEMENTS(irt->vnis, node, nnode, tmp_vpn)) { if (json) json_object_array_add( - json_vnis, json_object_new_int64(tmp_vpn->vni)); + json_vnis, json_object_new_int(tmp_vpn->vni)); else vty_out(vty, " %u\n", tmp_vpn->vni); } @@ -980,7 +979,6 @@ static void show_vni_entry(struct hash_backet *backet, void *args[]) vty_out(vty, "\n"); } } -#endif /* HAVE_CUMULUS */ static int bgp_show_ethernet_vpn(struct vty *vty, struct prefix_rd *prd, enum bgp_show_type type, void *output_arg, @@ -1636,8 +1634,6 @@ DEFUN(no_evpnrt5_network, argv[idx_gwip]->arg, argv[idx_ethtag]->arg); } -#if defined(HAVE_CUMULUS) - static void evpn_import_rt_delete_auto(struct bgp *bgp, struct bgpevpn *vpn) { evpn_rt_delete_auto(bgp, vpn->vni, vpn->import_rtl); @@ -1721,6 +1717,7 @@ static void evpn_unconfigure_import_rt(struct bgp *bgp, struct bgpevpn *vpn, list_delete_node(vpn->import_rtl, node_to_del); } + assert(vpn->import_rtl); /* Reset to auto RT - this also rebuilds the RT to VNI mapping */ if (list_isempty(vpn->import_rtl)) { UNSET_FLAG(vpn->flags, VNI_FLAG_IMPRT_CFGD); @@ -1788,6 +1785,7 @@ static void evpn_unconfigure_export_rt(struct bgp *bgp, struct bgpevpn *vpn, list_delete_node(vpn->export_rtl, node_to_del); } + assert(vpn->export_rtl); if (list_isempty(vpn->export_rtl)) { UNSET_FLAG(vpn->flags, VNI_FLAG_EXPRT_CFGD); bgp_evpn_derive_auto_rt_export(bgp, vpn); @@ -2752,7 +2750,6 @@ static void evpn_unset_advertise_autort_rfc8365(struct bgp *bgp) bgp->advertise_autort_rfc8365 = 0; bgp_evpn_handle_autort_change(bgp); } -#endif /* HAVE_CUMULUS */ static void write_vni_config(struct vty *vty, struct bgpevpn *vpn) { @@ -2799,7 +2796,6 @@ static void write_vni_config(struct vty *vty, struct bgpevpn *vpn) } } -#if defined(HAVE_CUMULUS) DEFUN (bgp_evpn_advertise_default_gw_vni, bgp_evpn_advertise_default_gw_vni_cmd, "advertise-default-gw", @@ -3262,10 +3258,11 @@ DEFUN(show_bgp_l2vpn_evpn_es, { int idx = 0; uint8_t uj = 0; - esi_t esi = {0}; + esi_t esi; json_object *json = NULL; struct bgp *bgp = NULL; + memset(&esi, 0, sizeof(esi)); uj = use_json(argc, argv); bgp = bgp_get_default(); @@ -3547,10 +3544,11 @@ DEFUN(show_bgp_l2vpn_evpn_route_esi, JSON_STR) { int uj = 0; - esi_t esi = {0}; + esi_t esi; struct bgp *bgp = NULL; json_object *json = NULL; + memset(&esi, 0, sizeof(esi)); bgp = bgp_get_default(); if (!bgp) return CMD_WARNING; @@ -3904,7 +3902,6 @@ DEFUN(show_bgp_l2vpn_evpn_import_rt, return CMD_SUCCESS; } -#if defined(HAVE_CUMULUS) DEFUN(test_adv_evpn_type4_route, test_adv_evpn_type4_route_cmd, "advertise es ESI", @@ -4064,7 +4061,6 @@ ALIAS_HIDDEN(show_bgp_l2vpn_evpn_route_vni_all, show_bgp_evpn_route_vni_all_cmd, ALIAS_HIDDEN(show_bgp_l2vpn_evpn_import_rt, show_bgp_evpn_import_rt_cmd, "show bgp evpn import-rt", SHOW_STR BGP_STR EVPN_HELP_STR "Show import route target\n") -#endif DEFUN_NOSH (bgp_evpn_vni, bgp_evpn_vni_cmd, @@ -4841,7 +4837,6 @@ DEFUN (no_bgp_evpn_vni_rt_without_val, evpn_unconfigure_export_rt(bgp, vpn, NULL); return CMD_SUCCESS; } -#endif static int vni_cmp(const void **a, const void **b) { @@ -4961,7 +4956,6 @@ void bgp_ethernetvpn_init(void) install_element(VIEW_NODE, &show_ip_bgp_l2vpn_evpn_all_overlay_cmd); install_element(BGP_EVPN_NODE, &no_evpnrt5_network_cmd); install_element(BGP_EVPN_NODE, &evpnrt5_network_cmd); -#if defined(HAVE_CUMULUS) install_element(BGP_EVPN_NODE, &bgp_evpn_advertise_all_vni_cmd); install_element(BGP_EVPN_NODE, &no_bgp_evpn_advertise_all_vni_cmd); install_element(BGP_EVPN_NODE, &bgp_evpn_advertise_autort_rfc8365_cmd); @@ -5027,5 +5021,4 @@ void bgp_ethernetvpn_init(void) install_element(BGP_EVPN_VNI_NODE, &bgp_evpn_advertise_vni_subnet_cmd); install_element(BGP_EVPN_VNI_NODE, &no_bgp_evpn_advertise_vni_subnet_cmd); -#endif } diff --git a/bgpd/bgp_flowspec.c b/bgpd/bgp_flowspec.c index 884c5aa51a..9b998d4497 100644 --- a/bgpd/bgp_flowspec.c +++ b/bgpd/bgp_flowspec.c @@ -59,7 +59,8 @@ static int bgp_fs_nlri_validate(uint8_t *nlri_content, uint32_t len) len - offset, NULL, &error); break; case FLOWSPEC_TCP_FLAGS: - ret = bgp_flowspec_tcpflags_decode( + case FLOWSPEC_FRAGMENT: + ret = bgp_flowspec_bitmask_decode( BGP_FLOWSPEC_VALIDATE_ONLY, nlri_content + offset, len - offset, NULL, &error); @@ -71,12 +72,6 @@ static int bgp_fs_nlri_validate(uint8_t *nlri_content, uint32_t len) nlri_content + offset, len - offset, NULL, &error); break; - case FLOWSPEC_FRAGMENT: - ret = bgp_flowspec_fragment_type_decode( - BGP_FLOWSPEC_VALIDATE_ONLY, - nlri_content + offset, - len - offset, NULL, &error); - break; default: error = -1; break; diff --git a/bgpd/bgp_flowspec_util.c b/bgpd/bgp_flowspec_util.c index 956cf28c21..1b87427661 100644 --- a/bgpd/bgp_flowspec_util.c +++ b/bgpd/bgp_flowspec_util.c @@ -124,8 +124,9 @@ static bool bgp_flowspec_contains_prefix(struct prefix *pfs, len - offset, NULL, &error); break; + case FLOWSPEC_FRAGMENT: case FLOWSPEC_TCP_FLAGS: - ret = bgp_flowspec_tcpflags_decode( + ret = bgp_flowspec_bitmask_decode( BGP_FLOWSPEC_VALIDATE_ONLY, nlri_content+offset, len - offset, @@ -139,13 +140,6 @@ static bool bgp_flowspec_contains_prefix(struct prefix *pfs, len - offset, NULL, &error); break; - case FLOWSPEC_FRAGMENT: - ret = bgp_flowspec_fragment_type_decode( - BGP_FLOWSPEC_VALIDATE_ONLY, - nlri_content + offset, - len - offset, NULL, - &error); - break; default: error = -1; break; @@ -312,14 +306,14 @@ int bgp_flowspec_op_decode(enum bgp_flowspec_util_nlri_t type, /* - * handle the flowspec tcpflags field + * handle the flowspec tcpflags or fragment field * return number of bytes analysed * if there is an error, the passed error param is used to give error: * -1 if decoding error, * if result is a string, its assumed length * is BGP_FLOWSPEC_STRING_DISPLAY_MAX */ -int bgp_flowspec_tcpflags_decode(enum bgp_flowspec_util_nlri_t type, +int bgp_flowspec_bitmask_decode(enum bgp_flowspec_util_nlri_t type, uint8_t *nlri_ptr, uint32_t max_len, void *result, int *error) @@ -348,32 +342,33 @@ int bgp_flowspec_tcpflags_decode(enum bgp_flowspec_util_nlri_t type, case BGP_FLOWSPEC_RETURN_STRING: if (op[1] == 1 && loop != 0) { len_written = snprintf(ptr, len_string, - ", and "); + ",&"); len_string -= len_written; ptr += len_written; } else if (op[1] == 0 && loop != 0) { len_written = snprintf(ptr, len_string, - ", or "); + ",|"); len_string -= len_written; ptr += len_written; } - len_written = snprintf(ptr, len_string, - "tcp flags is "); - len_string -= len_written; - ptr += len_written; - if (op[6] == 1) { - ptr += snprintf(ptr, len_string, - "not "); + if (op[7] == 1) { + len_written = snprintf(ptr, len_string, + "= "); + len_string -= len_written; + ptr += len_written; + } else { + len_written = snprintf(ptr, len_string, + "∋ "); len_string -= len_written; ptr += len_written; } - if (op[7] == 1) { - ptr += snprintf(ptr, len_string, - "exactly match "); + if (op[6] == 1) { + len_written = snprintf(ptr, len_string, + "! "); len_string -= len_written; ptr += len_written; } - ptr += snprintf(ptr, len_string, + len_written = snprintf(ptr, len_string, "%d", value); len_string -= len_written; ptr += len_written; @@ -419,92 +414,6 @@ int bgp_flowspec_tcpflags_decode(enum bgp_flowspec_util_nlri_t type, return offset; } -/* - * handle the flowspec fragment type field - * return error (returned values are invalid) or number of bytes analysed - * -1 if error in decoding - * >= 0 : number of bytes analysed (ok). - */ -int bgp_flowspec_fragment_type_decode(enum bgp_flowspec_util_nlri_t type, - uint8_t *nlri_ptr, - uint32_t max_len, - void *result, int *error) -{ - int op[8]; - int len, value, value_size, loop = 0; - char *ptr = (char *)result; /* for return_string */ - struct bgp_pbr_fragment_val *mval = - (struct bgp_pbr_fragment_val *)result; - uint32_t offset = 0; - int len_string = BGP_FLOWSPEC_STRING_DISPLAY_MAX; - int len_written; - - *error = 0; - do { - hex2bin(&nlri_ptr[offset], op); - offset++; - len = 2 * op[2] + op[3]; - value_size = 1 << len; - value = hexstr2num(&nlri_ptr[offset], value_size); - if (value != 1 && value != 2 && value != 4 && value != 8) - *error = -1; - offset += value_size; - /* TODO : as per RFC5574 : first Fragment bits are Reserved - * does that mean that it is not possible - * to handle multiple occurences ? - * as of today, we only grab the first TCP fragment - */ - if (loop) { - *error = -2; - loop++; - continue; - } - switch (type) { - case BGP_FLOWSPEC_RETURN_STRING: - switch (value) { - case 1: - len_written = snprintf(ptr, len_string, - "dont-fragment"); - len_string -= len_written; - ptr += len_written; - break; - case 2: - len_written = snprintf(ptr, len_string, - "is-fragment"); - len_string -= len_written; - ptr += len_written; - break; - case 4: - len_written = snprintf(ptr, len_string, - "first-fragment"); - len_string -= len_written; - ptr += len_written; - break; - case 8: - len_written = snprintf(ptr, len_string, - "last-fragment"); - len_string -= len_written; - ptr += len_written; - break; - default: - {} - } - break; - case BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE: - mval->bitmask = (uint8_t)value; - break; - case BGP_FLOWSPEC_VALIDATE_ONLY: - default: - /* no action */ - break; - } - loop++; - } while (op[0] == 0 && offset < max_len - 1); - if (offset > max_len) - *error = -1; - return offset; -} - int bgp_flowspec_match_rules_fill(uint8_t *nlri_content, int len, struct bgp_pbr_entry_main *bpem) { @@ -623,7 +532,7 @@ int bgp_flowspec_match_rules_fill(uint8_t *nlri_content, int len, &error); break; case FLOWSPEC_TCP_FLAGS: - ret = bgp_flowspec_tcpflags_decode( + ret = bgp_flowspec_bitmask_decode( BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE, nlri_content + offset, len - offset, @@ -637,7 +546,7 @@ int bgp_flowspec_match_rules_fill(uint8_t *nlri_content, int len, offset += ret; break; case FLOWSPEC_FRAGMENT: - ret = bgp_flowspec_fragment_type_decode( + ret = bgp_flowspec_bitmask_decode( BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE, nlri_content + offset, len - offset, &bpem->fragment, @@ -646,7 +555,7 @@ int bgp_flowspec_match_rules_fill(uint8_t *nlri_content, int len, zlog_err("%s: flowspec_fragment_type_decode error %d", __func__, error); else - bpem->match_bitmask |= FRAGMENT_PRESENT; + bpem->match_fragment_num = error; offset += ret; break; default: diff --git a/bgpd/bgp_flowspec_util.h b/bgpd/bgp_flowspec_util.h index e4454ab4db..2d16e57a36 100644 --- a/bgpd/bgp_flowspec_util.h +++ b/bgpd/bgp_flowspec_util.h @@ -41,15 +41,11 @@ extern int bgp_flowspec_ip_address(enum bgp_flowspec_util_nlri_t type, uint32_t max_len, void *result, int *error); -extern int bgp_flowspec_tcpflags_decode(enum bgp_flowspec_util_nlri_t type, +extern int bgp_flowspec_bitmask_decode(enum bgp_flowspec_util_nlri_t type, uint8_t *nlri_ptr, uint32_t max_len, void *result, int *error); -extern int bgp_flowspec_fragment_type_decode(enum bgp_flowspec_util_nlri_t type, - uint8_t *nlri_ptr, - uint32_t max_len, - void *result, int *error); struct bgp_pbr_entry_main; extern int bgp_flowspec_match_rules_fill(uint8_t *nlri_content, int len, struct bgp_pbr_entry_main *bpem); diff --git a/bgpd/bgp_flowspec_vty.c b/bgpd/bgp_flowspec_vty.c index b21e5ae0dc..90acd8fcb1 100644 --- a/bgpd/bgp_flowspec_vty.c +++ b/bgpd/bgp_flowspec_vty.c @@ -62,7 +62,7 @@ static const struct message bgp_flowspec_display_min[] = { {FLOWSPEC_SRC_PORT, "srcp"}, {FLOWSPEC_ICMP_TYPE, "type"}, {FLOWSPEC_ICMP_CODE, "code"}, - {FLOWSPEC_TCP_FLAGS, "flags"}, + {FLOWSPEC_TCP_FLAGS, "tcp"}, {FLOWSPEC_PKT_LEN, "pktlen"}, {FLOWSPEC_DSCP, "dscp"}, {FLOWSPEC_FRAGMENT, "pktfrag"}, @@ -173,7 +173,7 @@ void bgp_fs_nlri_get_string(unsigned char *nlri_content, size_t len, ptr += len_written; break; case FLOWSPEC_TCP_FLAGS: - ret = bgp_flowspec_tcpflags_decode( + ret = bgp_flowspec_bitmask_decode( type_util, nlri_content+offset, len - offset, @@ -221,11 +221,11 @@ void bgp_fs_nlri_get_string(unsigned char *nlri_content, size_t len, ptr += len_written; break; case FLOWSPEC_FRAGMENT: - ret = bgp_flowspec_fragment_type_decode( - type_util, - nlri_content + offset, - len - offset, local_string, - &error); + ret = bgp_flowspec_bitmask_decode( + type_util, + nlri_content+offset, + len - offset, + local_string, &error); if (ret <= 0) break; if (json_path) { diff --git a/bgpd/bgp_io.c b/bgpd/bgp_io.c index 69c92e829c..c8d5b1daa1 100644 --- a/bgpd/bgp_io.c +++ b/bgpd/bgp_io.c @@ -174,7 +174,6 @@ static int bgp_process_reads(struct thread *thread) bool more = true; // whether we got more data bool fatal = false; // whether fatal error occurred bool added_pkt = false; // whether we pushed onto ->ibuf - bool header_valid = true; // whether header is valid /* clang-format on */ peer = THREAD_ARG(thread); @@ -214,10 +213,8 @@ static int bgp_process_reads(struct thread *thread) if (ringbuf_remain(ibw) < BGP_HEADER_SIZE) break; - /* validate header */ - header_valid = validate_header(peer); - - if (!header_valid) { + /* check that header is valid */ + if (!validate_header(peer)) { fatal = true; break; } diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c index 28e8ceb15d..3a854be534 100644 --- a/bgpd/bgp_mplsvpn.c +++ b/bgpd/bgp_mplsvpn.c @@ -466,6 +466,7 @@ leak_update( { struct prefix *p = &bn->p; struct bgp_info *bi; + struct bgp_info *bi_ultimate; struct bgp_info *new; char buf_prefix[PREFIX_STRLEN]; @@ -477,6 +478,26 @@ leak_update( } /* + * Routes that are redistributed into BGP from zebra do not get + * nexthop tracking. However, if those routes are subsequently + * imported to other RIBs within BGP, the leaked routes do not + * carry the original BGP_ROUTE_REDISTRIBUTE sub_type. Therefore, + * in order to determine if the route we are currently leaking + * should have nexthop tracking, we must find the ultimate + * parent so we can check its sub_type. + * + * As of now, source_bi may at most be a second-generation route + * (only one hop back to ultimate parent for vrf-vpn-vrf scheme). + * Using a loop here supports more complex intra-bgp import-export + * schemes that could be implemented in the future. + * + */ + for (bi_ultimate = source_bi; + bi_ultimate->extra && bi_ultimate->extra->parent; + bi_ultimate = bi_ultimate->extra->parent) + ; + + /* * match parent */ for (bi = bn->info; bi; bi = bi->next) { @@ -528,7 +549,7 @@ leak_update( bgp_nexthop = bi->extra->bgp_orig; /* No nexthop tracking for redistributed routes */ - if (source_bi->sub_type == BGP_ROUTE_REDISTRIBUTE) + if (bi_ultimate->sub_type == BGP_ROUTE_REDISTRIBUTE) nh_valid = 1; else /* @@ -591,7 +612,7 @@ leak_update( * their originating protocols will do the tracking and * withdraw those routes if the nexthops become unreachable */ - if (source_bi->sub_type == BGP_ROUTE_REDISTRIBUTE) + if (bi_ultimate->sub_type == BGP_ROUTE_REDISTRIBUTE) nh_valid = 1; else /* diff --git a/bgpd/bgp_network.c b/bgpd/bgp_network.c index 84a959d0e8..476b64e75a 100644 --- a/bgpd/bgp_network.c +++ b/bgpd/bgp_network.c @@ -745,7 +745,7 @@ int bgp_socket(struct bgp *bgp, unsigned short port, const char *address) close(sock); } freeaddrinfo(ainfo_save); - if (count == 0) { + if (count == 0 && bgp->inst_type != BGP_INSTANCE_TYPE_VRF) { zlog_err( "%s: no usable addresses please check other programs usage of specified port %d", __func__, port); diff --git a/bgpd/bgp_nexthop.c b/bgpd/bgp_nexthop.c index fd8d894878..32011d210b 100644 --- a/bgpd/bgp_nexthop.c +++ b/bgpd/bgp_nexthop.c @@ -438,7 +438,7 @@ int bgp_subgrp_multiaccess_check_v4(struct in_addr nexthop, struct bgp_node *rn1, *rn2; struct peer_af *paf; struct prefix p, np; - struct bgp *bgp = NULL; + struct bgp *bgp; np.family = AF_INET; np.prefixlen = IPV4_MAX_BITLEN; @@ -447,7 +447,7 @@ int bgp_subgrp_multiaccess_check_v4(struct in_addr nexthop, p.family = AF_INET; p.prefixlen = IPV4_MAX_BITLEN; - rn1 = rn2 = NULL; + rn2 = NULL; bgp = SUBGRP_INST(subgrp); rn1 = bgp_node_match(bgp->connected_table[AFI_IP], &np); diff --git a/bgpd/bgp_open.c b/bgpd/bgp_open.c index aa98f8a557..da90bbd67d 100644 --- a/bgpd/bgp_open.c +++ b/bgpd/bgp_open.c @@ -80,8 +80,9 @@ void bgp_capability_vty_out(struct vty *vty, struct peer *peer, afi_t afi; safi_t safi; - bgp_map_afi_safi_iana2int(ntohs(mpc.afi), mpc.safi, - &afi, &safi); + (void)bgp_map_afi_safi_iana2int(ntohs(mpc.afi), + mpc.safi, &afi, &safi); + if (use_json) { switch (afi) { case AFI_IP: diff --git a/bgpd/bgp_pbr.c b/bgpd/bgp_pbr.c index 5e36f91750..45ec21631c 100644 --- a/bgpd/bgp_pbr.c +++ b/bgpd/bgp_pbr.c @@ -21,6 +21,7 @@ #include "prefix.h" #include "zclient.h" #include "jhash.h" +#include "pbr.h" #include "bgpd/bgpd.h" #include "bgpd/bgp_pbr.h" @@ -31,11 +32,13 @@ #include "bgpd/bgp_attr.h" #include "bgpd/bgp_zebra.h" #include "bgpd/bgp_mplsvpn.h" +#include "bgpd/bgp_flowspec_private.h" DEFINE_MTYPE_STATIC(BGPD, PBR_MATCH_ENTRY, "PBR match entry") DEFINE_MTYPE_STATIC(BGPD, PBR_MATCH, "PBR match") DEFINE_MTYPE_STATIC(BGPD, PBR_ACTION, "PBR action") DEFINE_MTYPE_STATIC(BGPD, PBR, "BGP PBR Context") +DEFINE_MTYPE_STATIC(BGPD, PBR_VALMASK, "BGP PBR Val Mask Value") RB_GENERATE(bgp_pbr_interface_head, bgp_pbr_interface, id_entry, bgp_pbr_interface_compare); @@ -175,11 +178,220 @@ static int sprintf_bgp_pbr_match_val(char *str, struct bgp_pbr_match_val *mval, _cnt++; \ } while (0) +/* this structure can be used for port range, + * but also for other values range like packet length range + */ struct bgp_pbr_range_port { uint16_t min_port; uint16_t max_port; }; +/* this structure can be used to filter with a mask + * for instance it supports not instructions like for + * tcpflags + */ +struct bgp_pbr_val_mask { + uint16_t val; + uint16_t mask; +}; + +/* this structure is used to pass instructs + * so that BGP can create pbr instructions to ZEBRA + */ +struct bgp_pbr_filter { + vrf_id_t vrf_id; + struct prefix *src; + struct prefix *dst; + uint8_t protocol; + struct bgp_pbr_range_port *pkt_len; + struct bgp_pbr_range_port *src_port; + struct bgp_pbr_range_port *dst_port; + struct bgp_pbr_val_mask *tcp_flags; + struct bgp_pbr_val_mask *dscp; + struct bgp_pbr_val_mask *pkt_len_val; + struct bgp_pbr_val_mask *fragment; +}; + +/* this structure is used to contain OR instructions + * so that BGP can create multiple pbr instructions + * to ZEBRA + */ +struct bgp_pbr_or_filter { + struct list *tcpflags; + struct list *dscp; + struct list *pkt_len; + struct list *fragment; + struct list *icmp_type; + struct list *icmp_code; +}; + +static void bgp_pbr_policyroute_add_to_zebra_unit(struct bgp *bgp, + struct bgp_info *binfo, + struct bgp_pbr_filter *bpf, + struct nexthop *nh, + float *rate); + +static bool bgp_pbr_extract_enumerate_unary_opposite( + uint8_t unary_operator, + struct bgp_pbr_val_mask *and_valmask, + struct list *or_valmask, uint32_t value, + uint8_t type_entry) +{ + if (unary_operator == OPERATOR_UNARY_AND && and_valmask) { + if (type_entry == FLOWSPEC_TCP_FLAGS) { + and_valmask->mask |= + TCP_HEADER_ALL_FLAGS & + ~(value); + } else if (type_entry == FLOWSPEC_DSCP || + type_entry == FLOWSPEC_PKT_LEN || + type_entry == FLOWSPEC_FRAGMENT) { + and_valmask->val = value; + and_valmask->mask = 1; /* inverse */ + } + } else if (unary_operator == OPERATOR_UNARY_OR && or_valmask) { + and_valmask = XCALLOC(MTYPE_PBR_VALMASK, + sizeof(struct bgp_pbr_val_mask)); + if (type_entry == FLOWSPEC_TCP_FLAGS) { + and_valmask->val = TCP_HEADER_ALL_FLAGS; + and_valmask->mask |= + TCP_HEADER_ALL_FLAGS & + ~(value); + } else if (type_entry == FLOWSPEC_DSCP || + type_entry == FLOWSPEC_FRAGMENT || + type_entry == FLOWSPEC_PKT_LEN) { + and_valmask->val = value; + and_valmask->mask = 1; /* inverse */ + } + listnode_add(or_valmask, and_valmask); + } else if (type_entry == FLOWSPEC_ICMP_CODE || + type_entry == FLOWSPEC_ICMP_TYPE) + return false; + return true; +} + +/* TCP : FIN and SYN -> val = ALL; mask = 3 + * TCP : not (FIN and SYN) -> val = ALL; mask = ALL & ~(FIN|RST) + * other variables type: dscp, pkt len, fragment + * - value is copied in bgp_pbr_val_mask->val value + * - if negate form is identifierd, bgp_pbr_val_mask->mask set to 1 + */ +static bool bgp_pbr_extract_enumerate_unary(struct bgp_pbr_match_val list[], + int num, uint8_t unary_operator, + void *valmask, uint8_t type_entry) +{ + int i = 0; + struct bgp_pbr_val_mask *and_valmask = NULL; + struct list *or_valmask = NULL; + bool ret; + + if (valmask) { + if (unary_operator == OPERATOR_UNARY_AND) { + and_valmask = (struct bgp_pbr_val_mask *)valmask; + memset(and_valmask, 0, sizeof(struct bgp_pbr_val_mask)); + } else if (unary_operator == OPERATOR_UNARY_OR) { + or_valmask = (struct list *)valmask; + } + } + for (i = 0; i < num; i++) { + if (i != 0 && list[i].unary_operator != + unary_operator) + return false; + if (!(list[i].compare_operator & + OPERATOR_COMPARE_EQUAL_TO) && + !(list[i].compare_operator & + OPERATOR_COMPARE_EXACT_MATCH)) { + if ((list[i].compare_operator & + OPERATOR_COMPARE_LESS_THAN) && + (list[i].compare_operator & + OPERATOR_COMPARE_GREATER_THAN)) { + ret = bgp_pbr_extract_enumerate_unary_opposite( + unary_operator, and_valmask, + or_valmask, list[i].value, + type_entry); + if (ret == false) + return ret; + continue; + } + return false; + } + if (unary_operator == OPERATOR_UNARY_AND && and_valmask) { + if (type_entry == FLOWSPEC_TCP_FLAGS) + and_valmask->mask |= + TCP_HEADER_ALL_FLAGS & list[i].value; + } else if (unary_operator == OPERATOR_UNARY_OR && or_valmask) { + and_valmask = XCALLOC(MTYPE_PBR_VALMASK, + sizeof(struct bgp_pbr_val_mask)); + if (type_entry == FLOWSPEC_TCP_FLAGS) { + and_valmask->val = TCP_HEADER_ALL_FLAGS; + and_valmask->mask |= + TCP_HEADER_ALL_FLAGS & list[i].value; + } else if (type_entry == FLOWSPEC_DSCP || + type_entry == FLOWSPEC_ICMP_TYPE || + type_entry == FLOWSPEC_ICMP_CODE || + type_entry == FLOWSPEC_FRAGMENT || + type_entry == FLOWSPEC_PKT_LEN) + and_valmask->val = list[i].value; + listnode_add(or_valmask, and_valmask); + } + } + if (unary_operator == OPERATOR_UNARY_AND && and_valmask + && type_entry == FLOWSPEC_TCP_FLAGS) + and_valmask->val = TCP_HEADER_ALL_FLAGS; + return true; +} + +/* if unary operator can either be UNARY_OR/AND/OR-AND. + * in the latter case, combinationf of both is not handled + */ +static bool bgp_pbr_extract_enumerate(struct bgp_pbr_match_val list[], + int num, uint8_t unary_operator, + void *valmask, uint8_t type_entry) +{ + bool ret; + uint8_t unary_operator_val = unary_operator; + bool double_check = false; + + if ((unary_operator & OPERATOR_UNARY_OR) && + (unary_operator & OPERATOR_UNARY_AND)) { + unary_operator_val = OPERATOR_UNARY_AND; + double_check = true; + } else + unary_operator_val = unary_operator; + ret = bgp_pbr_extract_enumerate_unary(list, num, unary_operator_val, + valmask, type_entry); + if (!ret && double_check) + ret = bgp_pbr_extract_enumerate_unary(list, num, + OPERATOR_UNARY_OR, + valmask, + type_entry); + return ret; +} + +/* returns the unary operator that is in the list + * return 0 if both operators are used + */ +static uint8_t bgp_pbr_match_val_get_operator(struct bgp_pbr_match_val list[], + int num) + +{ + int i; + uint8_t unary_operator = OPERATOR_UNARY_AND; + + for (i = 0; i < num; i++) { + if (i == 0) + continue; + if (list[i].unary_operator & OPERATOR_UNARY_OR) + unary_operator = OPERATOR_UNARY_OR; + if ((list[i].unary_operator & OPERATOR_UNARY_AND + && unary_operator == OPERATOR_UNARY_OR) || + (list[i].unary_operator & OPERATOR_UNARY_OR + && unary_operator == OPERATOR_UNARY_AND)) + return 0; + } + return unary_operator; +} + + /* return true if extraction ok */ static bool bgp_pbr_extract(struct bgp_pbr_match_val list[], @@ -231,6 +443,8 @@ static bool bgp_pbr_extract(struct bgp_pbr_match_val list[], static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api) { + bool enumerate_icmp = false; + /* because bgp pbr entry may contain unsupported * combinations, a message will be displayed here if * not supported. @@ -240,16 +454,6 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api) * - combination src/dst => drop * - combination srcport + @IP */ - if (api->match_icmp_type_num || api->match_packet_length_num - || api->match_dscp_num || api->match_tcpflags_num) { - if (BGP_DEBUG(pbr, PBR)) { - bgp_pbr_print_policy_route(api); - zlog_debug("BGP: some SET actions not supported by Zebra. ignoring."); - zlog_debug("BGP: case icmp or length or dscp or tcp flags"); - } - return 0; - } - if (api->match_protocol_num > 1) { if (BGP_DEBUG(pbr, PBR)) zlog_debug("BGP: match protocol operations:" @@ -259,6 +463,7 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api) } if (api->match_protocol_num == 1 && api->protocol[0].value != PROTOCOL_UDP && + api->protocol[0].value != PROTOCOL_ICMP && api->protocol[0].value != PROTOCOL_TCP) { if (BGP_DEBUG(pbr, PBR)) zlog_debug("BGP: match protocol operations:" @@ -278,12 +483,114 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api) "too complex. ignoring."); return 0; } + if (!bgp_pbr_extract_enumerate(api->tcpflags, + api->match_tcpflags_num, + OPERATOR_UNARY_AND | + OPERATOR_UNARY_OR, NULL, + FLOWSPEC_TCP_FLAGS)) { + if (BGP_DEBUG(pbr, PBR)) + zlog_debug("BGP: match tcp flags:" + "too complex. ignoring."); + return 0; + } + if (!bgp_pbr_extract(api->icmp_type, api->match_icmp_type_num, NULL)) { + if (!bgp_pbr_extract_enumerate(api->icmp_type, + api->match_icmp_type_num, + OPERATOR_UNARY_OR, NULL, + FLOWSPEC_ICMP_TYPE)) { + if (BGP_DEBUG(pbr, PBR)) + zlog_debug("BGP: match icmp type operations:" + "too complex. ignoring."); + return 0; + } + enumerate_icmp = true; + } + if (!bgp_pbr_extract(api->icmp_code, api->match_icmp_code_num, NULL)) { + if (!bgp_pbr_extract_enumerate(api->icmp_code, + api->match_icmp_code_num, + OPERATOR_UNARY_OR, NULL, + FLOWSPEC_ICMP_CODE)) { + if (BGP_DEBUG(pbr, PBR)) + zlog_debug("BGP: match icmp code operations:" + "too complex. ignoring."); + return 0; + } else if (api->match_icmp_type_num > 1 && + enumerate_icmp == false) { + if (BGP_DEBUG(pbr, PBR)) + zlog_debug("BGP: match icmp code is enumerate" + ", and icmp type is not." + " too complex. ignoring."); + return 0; + } + } if (!bgp_pbr_extract(api->port, api->match_port_num, NULL)) { if (BGP_DEBUG(pbr, PBR)) zlog_debug("BGP: match port operations:" "too complex. ignoring."); return 0; } + if (api->match_packet_length_num) { + bool ret; + + ret = bgp_pbr_extract(api->packet_length, + api->match_packet_length_num, NULL); + if (!ret) + ret = bgp_pbr_extract_enumerate(api->packet_length, + api->match_packet_length_num, + OPERATOR_UNARY_OR + | OPERATOR_UNARY_AND, + NULL, FLOWSPEC_PKT_LEN); + if (!ret) { + if (BGP_DEBUG(pbr, PBR)) + zlog_debug("BGP: match packet length operations:" + "too complex. ignoring."); + return 0; + } + } + if (api->match_dscp_num) { + if (!bgp_pbr_extract_enumerate(api->dscp, api->match_dscp_num, + OPERATOR_UNARY_OR | OPERATOR_UNARY_AND, + NULL, FLOWSPEC_DSCP)) { + if (BGP_DEBUG(pbr, PBR)) + zlog_debug("BGP: match DSCP operations:" + "too complex. ignoring."); + return 0; + } + } + if (api->match_fragment_num) { + char fail_str[64]; + bool success; + + success = bgp_pbr_extract_enumerate(api->fragment, + api->match_fragment_num, + OPERATOR_UNARY_OR + | OPERATOR_UNARY_AND, + NULL, FLOWSPEC_FRAGMENT); + if (success) { + int i; + + for (i = 0; i < api->match_fragment_num; i++) { + if (api->fragment[i].value != 1 && + api->fragment[i].value != 2 && + api->fragment[i].value != 4 && + api->fragment[i].value != 8) { + success = false; + sprintf(fail_str, + "Value not valid (%d) for this implementation", + api->fragment[i].value); + } + } + } else + sprintf(fail_str, "too complex. ignoring"); + if (!success) { + if (BGP_DEBUG(pbr, PBR)) + zlog_debug("BGP: match fragment operation (%d) %s", + api->match_fragment_num, + fail_str); + return 0; + } + } + /* no combinations with both src_port and dst_port * or port with src_port and dst_port */ @@ -294,6 +601,14 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api) " too complex. ignoring."); return 0; } + if ((api->match_src_port_num || api->match_dst_port_num + || api->match_port_num) && (api->match_icmp_type_num + || api->match_icmp_code_num)) { + if (BGP_DEBUG(pbr, PBR)) + zlog_debug("BGP: match multiple port/imcp operations:" + " too complex. ignoring."); + return 0; + } if (!(api->match_bitmask & PREFIX_SRC_PRESENT) && !(api->match_bitmask & PREFIX_DST_PRESENT)) { if (BGP_DEBUG(pbr, PBR)) { @@ -518,6 +833,12 @@ uint32_t bgp_pbr_match_hash_key(void *arg) key = jhash_1word(pbm->vrf_id, 0x4312abde); key = jhash_1word(pbm->flags, key); + key = jhash_1word(pbm->pkt_len_min, key); + key = jhash_1word(pbm->pkt_len_max, key); + key = jhash_1word(pbm->tcp_flags, key); + key = jhash_1word(pbm->tcp_mask_flags, key); + key = jhash_1word(pbm->dscp_value, key); + key = jhash_1word(pbm->fragment, key); return jhash_1word(pbm->type, key); } @@ -540,6 +861,23 @@ int bgp_pbr_match_hash_equal(const void *arg1, const void *arg2) if (r1->action != r2->action) return 0; + if (r1->pkt_len_min != r2->pkt_len_min) + return 0; + + if (r1->pkt_len_max != r2->pkt_len_max) + return 0; + + if (r1->tcp_flags != r2->tcp_flags) + return 0; + + if (r1->tcp_mask_flags != r2->tcp_mask_flags) + return 0; + + if (r1->dscp_value != r2->dscp_value) + return 0; + + if (r1->fragment != r2->fragment) + return 0; return 1; } @@ -807,10 +1145,11 @@ void bgp_pbr_print_policy_route(struct bgp_pbr_entry_main *api) ptr += sprintf_bgp_pbr_match_val(ptr, &api->tcpflags[i], i > 0 ? NULL : "@tcpflags "); - if (api->match_bitmask & FRAGMENT_PRESENT) { + if (api->match_fragment_num) INCREMENT_DISPLAY(ptr, nb_items); - ptr += sprintf(ptr, "@fragment %u", api->fragment.bitmask); - } + for (i = 0; i < api->match_fragment_num; i++) + ptr += sprintf_bgp_pbr_match_val(ptr, &api->fragment[i], + i > 0 ? NULL : "@fragment "); if (!nb_items) ptr = return_string; else @@ -953,20 +1292,24 @@ static int bgp_pbr_get_remaining_entry(struct hash_backet *backet, void *arg) return HASHWALK_ABORT; } -static void bgp_pbr_policyroute_remove_from_zebra(struct bgp *bgp, - struct bgp_info *binfo, - vrf_id_t vrf_id, - struct prefix *src, - struct prefix *dst, - uint8_t protocol, - struct bgp_pbr_range_port *src_port, - struct bgp_pbr_range_port *dst_port) +static void bgp_pbr_policyroute_remove_from_zebra_unit(struct bgp *bgp, + struct bgp_info *binfo, + struct bgp_pbr_filter *bpf) { struct bgp_pbr_match temp; struct bgp_pbr_match_entry temp2; struct bgp_pbr_match *bpm; struct bgp_pbr_match_entry *bpme; struct bgp_pbr_match_entry_remain bpmer; + struct bgp_pbr_range_port *src_port; + struct bgp_pbr_range_port *dst_port; + struct bgp_pbr_range_port *pkt_len; + + if (!bpf) + return; + src_port = bpf->src_port; + dst_port = bpf->dst_port; + pkt_len = bpf->pkt_len; /* as we don't know information from EC * look for bpm that have the bpm @@ -974,17 +1317,19 @@ static void bgp_pbr_policyroute_remove_from_zebra(struct bgp *bgp, */ memset(&temp2, 0, sizeof(temp2)); memset(&temp, 0, sizeof(temp)); - if (src) { + if (bpf->src) { temp.flags |= MATCH_IP_SRC_SET; - prefix_copy(&temp2.src, src); + prefix_copy(&temp2.src, bpf->src); } else temp2.src.family = AF_INET; - if (dst) { + if (bpf->dst) { temp.flags |= MATCH_IP_DST_SET; - prefix_copy(&temp2.dst, dst); + prefix_copy(&temp2.dst, bpf->dst); } else temp2.dst.family = AF_INET; - if (src_port) { + if (src_port && (src_port->min_port || bpf->protocol == IPPROTO_ICMP)) { + if (bpf->protocol == IPPROTO_ICMP) + temp.flags |= MATCH_ICMP_SET; temp.flags |= MATCH_PORT_SRC_SET; temp2.src_port_min = src_port->min_port; if (src_port->max_port) { @@ -992,7 +1337,9 @@ static void bgp_pbr_policyroute_remove_from_zebra(struct bgp *bgp, temp2.src_port_max = src_port->max_port; } } - if (dst_port) { + if (dst_port && (dst_port->min_port || bpf->protocol == IPPROTO_ICMP)) { + if (bpf->protocol == IPPROTO_ICMP) + temp.flags |= MATCH_ICMP_SET; temp.flags |= MATCH_PORT_DST_SET; temp2.dst_port_min = dst_port->min_port; if (dst_port->max_port) { @@ -1000,9 +1347,35 @@ static void bgp_pbr_policyroute_remove_from_zebra(struct bgp *bgp, temp2.dst_port_max = dst_port->max_port; } } - temp2.proto = protocol; + temp2.proto = bpf->protocol; + + if (pkt_len) { + temp.pkt_len_min = pkt_len->min_port; + if (pkt_len->max_port) + temp.pkt_len_max = pkt_len->max_port; + } else if (bpf->pkt_len_val) { + if (bpf->pkt_len_val->mask) + temp.flags |= MATCH_PKT_LEN_INVERSE_SET; + temp.pkt_len_min = bpf->pkt_len_val->val; + } + if (bpf->tcp_flags) { + temp.tcp_flags = bpf->tcp_flags->val; + temp.tcp_mask_flags = bpf->tcp_flags->mask; + } + if (bpf->dscp) { + if (bpf->dscp->mask) + temp.flags |= MATCH_DSCP_INVERSE_SET; + else + temp.flags |= MATCH_DSCP_SET; + temp.dscp_value = bpf->dscp->val; + } + if (bpf->fragment) { + if (bpf->fragment->mask) + temp.flags |= MATCH_FRAGMENT_INVERSE_SET; + temp.fragment = bpf->fragment->val; + } - if (src == NULL || dst == NULL) { + if (bpf->src == NULL || bpf->dst == NULL) { if (temp.flags & (MATCH_PORT_DST_SET | MATCH_PORT_SRC_SET)) temp.type = IPSET_NET_PORT; else @@ -1013,10 +1386,10 @@ static void bgp_pbr_policyroute_remove_from_zebra(struct bgp *bgp, else temp.type = IPSET_NET_NET; } - if (vrf_id == VRF_UNKNOWN) /* XXX case BGP destroy */ + if (bpf->vrf_id == VRF_UNKNOWN) /* XXX case BGP destroy */ temp.vrf_id = 0; else - temp.vrf_id = vrf_id; + temp.vrf_id = bpf->vrf_id; bpme = &temp2; bpm = &temp; bpme->backpointer = bpm; @@ -1037,16 +1410,182 @@ static void bgp_pbr_policyroute_remove_from_zebra(struct bgp *bgp, } } -static void bgp_pbr_policyroute_add_to_zebra(struct bgp *bgp, +static uint8_t bgp_pbr_next_type_entry(uint8_t type_entry) +{ + if (type_entry == FLOWSPEC_TCP_FLAGS) + return FLOWSPEC_DSCP; + if (type_entry == FLOWSPEC_DSCP) + return FLOWSPEC_PKT_LEN; + if (type_entry == FLOWSPEC_PKT_LEN) + return FLOWSPEC_FRAGMENT; + if (type_entry == FLOWSPEC_FRAGMENT) + return FLOWSPEC_ICMP_TYPE; + return 0; +} + +static void bgp_pbr_icmp_action(struct bgp *bgp, + struct bgp_info *binfo, + struct bgp_pbr_filter *bpf, + struct bgp_pbr_or_filter *bpof, + bool add, + struct nexthop *nh, + float *rate) +{ + struct bgp_pbr_range_port srcp, dstp; + struct bgp_pbr_val_mask *icmp_type, *icmp_code; + struct listnode *tnode, *cnode; + + if (!bpf) + return; + if (bpf->protocol != IPPROTO_ICMP) + return; + bpf->src_port = &srcp; + bpf->dst_port = &dstp; + /* parse icmp type and lookup appropriate icmp code + * if no icmp code found, create as many entryes as + * there are listed icmp codes for that icmp type + */ + if (!bpof->icmp_type) { + srcp.min_port = 0; + srcp.max_port = 255; + for (ALL_LIST_ELEMENTS_RO(bpof->icmp_code, cnode, icmp_code)) { + dstp.min_port = icmp_code->val; + if (add) + bgp_pbr_policyroute_add_to_zebra_unit(bgp, binfo, + bpf, nh, rate); + else + bgp_pbr_policyroute_remove_from_zebra_unit( + bgp, binfo, bpf); + } + return; + } + for (ALL_LIST_ELEMENTS_RO(bpof->icmp_type, tnode, icmp_type)) { + srcp.min_port = icmp_type->val; + srcp.max_port = 0; + dstp.max_port = 0; + /* only icmp type. create an entry only with icmp type */ + if (!bpof->icmp_code) { + /* icmp type is not one of the above + * forge an entry only based on the icmp type + */ + dstp.min_port = 0; + dstp.max_port = 255; + if (add) + bgp_pbr_policyroute_add_to_zebra_unit( + bgp, binfo, + bpf, nh, rate); + else + bgp_pbr_policyroute_remove_from_zebra_unit(bgp, + binfo, bpf); + continue; + } + for (ALL_LIST_ELEMENTS_RO(bpof->icmp_code, cnode, icmp_code)) { + dstp.min_port = icmp_code->val; + if (add) + bgp_pbr_policyroute_add_to_zebra_unit( + bgp, binfo, + bpf, nh, rate); + else + bgp_pbr_policyroute_remove_from_zebra_unit( + bgp, binfo, bpf); + } + } +} + +static void bgp_pbr_policyroute_remove_from_zebra_recursive(struct bgp *bgp, + struct bgp_info *binfo, + struct bgp_pbr_filter *bpf, + struct bgp_pbr_or_filter *bpof, + uint8_t type_entry) +{ + struct listnode *node, *nnode; + struct bgp_pbr_val_mask *valmask; + uint8_t next_type_entry; + struct list *orig_list; + struct bgp_pbr_val_mask **target_val; + + if (type_entry == 0) + return bgp_pbr_policyroute_remove_from_zebra_unit(bgp, + binfo, bpf); + next_type_entry = bgp_pbr_next_type_entry(type_entry); + if (type_entry == FLOWSPEC_TCP_FLAGS && bpof->tcpflags) { + orig_list = bpof->tcpflags; + target_val = &bpf->tcp_flags; + } else if (type_entry == FLOWSPEC_DSCP && bpof->dscp) { + orig_list = bpof->dscp; + target_val = &bpf->dscp; + } else if (type_entry == FLOWSPEC_PKT_LEN && bpof->pkt_len) { + orig_list = bpof->pkt_len; + target_val = &bpf->pkt_len_val; + } else if (type_entry == FLOWSPEC_FRAGMENT && bpof->fragment) { + orig_list = bpof->fragment; + target_val = &bpf->fragment; + } else if (type_entry == FLOWSPEC_ICMP_TYPE && + (bpof->icmp_type || bpof->icmp_code)) { + /* enumerate list for icmp - must be last one */ + bgp_pbr_icmp_action(bgp, binfo, bpf, bpof, false, NULL, NULL); + return; + } else { + return bgp_pbr_policyroute_remove_from_zebra_recursive(bgp, + binfo, + bpf, bpof, + next_type_entry); + } + for (ALL_LIST_ELEMENTS(orig_list, node, nnode, valmask)) { + *target_val = valmask; + bgp_pbr_policyroute_remove_from_zebra_recursive(bgp, binfo, + bpf, bpof, + next_type_entry); + } +} + +static void bgp_pbr_policyroute_remove_from_zebra(struct bgp *bgp, + struct bgp_info *binfo, + struct bgp_pbr_filter *bpf, + struct bgp_pbr_or_filter *bpof) +{ + if (!bpof) + return bgp_pbr_policyroute_remove_from_zebra_unit(bgp, + binfo, + bpf); + if (bpof->tcpflags) + bgp_pbr_policyroute_remove_from_zebra_recursive(bgp, binfo, + bpf, bpof, + FLOWSPEC_TCP_FLAGS); + else if (bpof->dscp) + bgp_pbr_policyroute_remove_from_zebra_recursive(bgp, binfo, + bpf, bpof, + FLOWSPEC_DSCP); + else if (bpof->pkt_len) + bgp_pbr_policyroute_remove_from_zebra_recursive(bgp, binfo, + bpf, bpof, + FLOWSPEC_PKT_LEN); + else if (bpof->fragment) + bgp_pbr_policyroute_remove_from_zebra_recursive(bgp, binfo, + bpf, bpof, + FLOWSPEC_FRAGMENT); + else if (bpof->icmp_type || bpof->icmp_code) + bgp_pbr_policyroute_remove_from_zebra_recursive(bgp, binfo, + bpf, bpof, + FLOWSPEC_ICMP_TYPE); + else + bgp_pbr_policyroute_remove_from_zebra_unit(bgp, binfo, bpf); + /* flush bpof */ + if (bpof->tcpflags) + list_delete_all_node(bpof->tcpflags); + if (bpof->dscp) + list_delete_all_node(bpof->dscp); + if (bpof->pkt_len) + list_delete_all_node(bpof->pkt_len); + if (bpof->fragment) + list_delete_all_node(bpof->fragment); +} + +static void bgp_pbr_policyroute_add_to_zebra_unit(struct bgp *bgp, struct bgp_info *binfo, - vrf_id_t vrf_id, - struct prefix *src, - struct prefix *dst, + struct bgp_pbr_filter *bpf, struct nexthop *nh, - float *rate, - uint8_t protocol, - struct bgp_pbr_range_port *src_port, - struct bgp_pbr_range_port *dst_port) + float *rate) { struct bgp_pbr_match temp; struct bgp_pbr_match_entry temp2; @@ -1055,14 +1594,104 @@ static void bgp_pbr_policyroute_add_to_zebra(struct bgp *bgp, struct bgp_pbr_action temp3; struct bgp_pbr_action *bpa = NULL; struct bgp_pbr_match_entry_remain bpmer; + struct bgp_pbr_range_port *src_port; + struct bgp_pbr_range_port *dst_port; + struct bgp_pbr_range_port *pkt_len; + if (!bpf) + return; + src_port = bpf->src_port; + dst_port = bpf->dst_port; + pkt_len = bpf->pkt_len; + + if (BGP_DEBUG(zebra, ZEBRA)) { + char bufsrc[64], bufdst[64]; + char buffer[64]; + int remaining_len = 0; + char protocol_str[16]; + + protocol_str[0] = '\0'; + if (bpf->tcp_flags && bpf->tcp_flags->mask) + bpf->protocol = IPPROTO_TCP; + if (bpf->protocol) + snprintf(protocol_str, sizeof(protocol_str), + "proto %d", bpf->protocol); + buffer[0] = '\0'; + if (bpf->protocol == IPPROTO_ICMP && src_port && dst_port) + remaining_len += snprintf(buffer, sizeof(buffer), + "type %d, code %d", + src_port->min_port, dst_port->min_port); + else if (bpf->protocol == IPPROTO_UDP || + bpf->protocol == IPPROTO_TCP) { + + if (src_port && src_port->min_port) + remaining_len += snprintf(buffer, + sizeof(buffer), + "from [%u:%u]", + src_port->min_port, + src_port->max_port ? + src_port->max_port : + src_port->min_port); + if (dst_port && dst_port->min_port) + remaining_len += snprintf(buffer + + remaining_len, + sizeof(buffer) + - remaining_len, + "to [%u:%u]", + dst_port->min_port, + dst_port->max_port ? + dst_port->max_port : + dst_port->min_port); + } + if (pkt_len && (pkt_len->min_port || pkt_len->max_port)) { + remaining_len += snprintf(buffer + remaining_len, + sizeof(buffer) + - remaining_len, + " len [%u:%u]", + pkt_len->min_port, + pkt_len->max_port ? + pkt_len->max_port : + pkt_len->min_port); + } else if (bpf->pkt_len_val) { + remaining_len += snprintf(buffer + remaining_len, + sizeof(buffer) + - remaining_len, + " %s len %u", + bpf->pkt_len_val->mask + ? "!" : "", + bpf->pkt_len_val->val); + } + if (bpf->tcp_flags) { + remaining_len += snprintf(buffer + remaining_len, + sizeof(buffer) + - remaining_len, + "tcpflags %x/%x", + bpf->tcp_flags->val, + bpf->tcp_flags->mask); + } + if (bpf->dscp) { + snprintf(buffer + remaining_len, + sizeof(buffer) + - remaining_len, + "%s dscp %d", + bpf->dscp->mask + ? "!" : "", + bpf->dscp->val); + } + zlog_info("BGP: adding FS PBR from %s to %s, %s %s", + bpf->src == NULL ? "<all>" : + prefix2str(bpf->src, bufsrc, sizeof(bufsrc)), + bpf->dst == NULL ? "<all>" : + prefix2str(bpf->dst, bufdst, sizeof(bufdst)), + protocol_str, buffer); + } /* look for bpa first */ memset(&temp3, 0, sizeof(temp3)); if (rate) temp3.rate = *rate; if (nh) memcpy(&temp3.nh, nh, sizeof(struct nexthop)); - temp3.vrf_id = vrf_id; + temp3.vrf_id = bpf->vrf_id; bpa = hash_get(bgp->pbr_action_hash, &temp3, bgp_pbr_action_alloc_intern); @@ -1084,33 +1713,63 @@ static void bgp_pbr_policyroute_add_to_zebra(struct bgp *bgp, /* then look for bpm */ memset(&temp, 0, sizeof(temp)); - if (src == NULL || dst == NULL) { - if ((src_port && src_port->min_port) || - (dst_port && dst_port->min_port)) - temp.type = IPSET_NET_PORT; - else - temp.type = IPSET_NET; - } else { - if ((src_port && src_port->min_port) || - (dst_port && dst_port->min_port)) - temp.type = IPSET_NET_PORT_NET; - else - temp.type = IPSET_NET_NET; - } - temp.vrf_id = vrf_id; - if (src) + temp.vrf_id = bpf->vrf_id; + if (bpf->src) temp.flags |= MATCH_IP_SRC_SET; - if (dst) + if (bpf->dst) temp.flags |= MATCH_IP_DST_SET; - if (src_port && src_port->min_port) + if (src_port && (src_port->min_port || bpf->protocol == IPPROTO_ICMP)) { + if (bpf->protocol == IPPROTO_ICMP) + temp.flags |= MATCH_ICMP_SET; temp.flags |= MATCH_PORT_SRC_SET; - if (dst_port && dst_port->min_port) + } + if (dst_port && (dst_port->min_port || bpf->protocol == IPPROTO_ICMP)) { + if (bpf->protocol == IPPROTO_ICMP) + temp.flags |= MATCH_ICMP_SET; temp.flags |= MATCH_PORT_DST_SET; + } if (src_port && src_port->max_port) temp.flags |= MATCH_PORT_SRC_RANGE_SET; if (dst_port && dst_port->max_port) temp.flags |= MATCH_PORT_DST_RANGE_SET; + + if (bpf->src == NULL || bpf->dst == NULL) { + if (temp.flags & (MATCH_PORT_DST_SET | MATCH_PORT_SRC_SET)) + temp.type = IPSET_NET_PORT; + else + temp.type = IPSET_NET; + } else { + if (temp.flags & (MATCH_PORT_DST_SET | MATCH_PORT_SRC_SET)) + temp.type = IPSET_NET_PORT_NET; + else + temp.type = IPSET_NET_NET; + } + if (pkt_len) { + temp.pkt_len_min = pkt_len->min_port; + if (pkt_len->max_port) + temp.pkt_len_max = pkt_len->max_port; + } else if (bpf->pkt_len_val) { + if (bpf->pkt_len_val->mask) + temp.flags |= MATCH_PKT_LEN_INVERSE_SET; + temp.pkt_len_min = bpf->pkt_len_val->val; + } + if (bpf->tcp_flags) { + temp.tcp_flags = bpf->tcp_flags->val; + temp.tcp_mask_flags = bpf->tcp_flags->mask; + } + if (bpf->dscp) { + if (bpf->dscp->mask) + temp.flags |= MATCH_DSCP_INVERSE_SET; + else + temp.flags |= MATCH_DSCP_SET; + temp.dscp_value = bpf->dscp->val; + } + if (bpf->fragment) { + if (bpf->fragment->mask) + temp.flags |= MATCH_FRAGMENT_INVERSE_SET; + temp.fragment = bpf->fragment->val; + } temp.action = bpa; bpm = hash_get(bgp->pbr_match_hash, &temp, bgp_pbr_match_alloc_intern); @@ -1134,19 +1793,19 @@ static void bgp_pbr_policyroute_add_to_zebra(struct bgp *bgp, } memset(&temp2, 0, sizeof(temp2)); - if (src) - prefix_copy(&temp2.src, src); + if (bpf->src) + prefix_copy(&temp2.src, bpf->src); else temp2.src.family = AF_INET; - if (dst) - prefix_copy(&temp2.dst, dst); + if (bpf->dst) + prefix_copy(&temp2.dst, bpf->dst); else temp2.dst.family = AF_INET; temp2.src_port_min = src_port ? src_port->min_port : 0; temp2.dst_port_min = dst_port ? dst_port->min_port : 0; temp2.src_port_max = src_port ? src_port->max_port : 0; temp2.dst_port_max = dst_port ? dst_port->max_port : 0; - temp2.proto = protocol; + temp2.proto = bpf->protocol; if (bpm) bpme = hash_get(bpm->entry_hash, &temp2, bgp_pbr_match_entry_alloc_intern); @@ -1171,7 +1830,7 @@ static void bgp_pbr_policyroute_add_to_zebra(struct bgp *bgp, * it will be suppressed subsequently */ /* ip rule add */ - if (!bpa->installed) { + if (!bpa->installed && !bpa->install_in_progress) { bgp_send_pbr_rule_action(bpa, true); bgp_zebra_announce_default(bgp, nh, AFI_IP, bpa->table_id, true); @@ -1207,6 +1866,107 @@ static void bgp_pbr_policyroute_add_to_zebra(struct bgp *bgp, } +static void bgp_pbr_policyroute_add_to_zebra_recursive(struct bgp *bgp, + struct bgp_info *binfo, + struct bgp_pbr_filter *bpf, + struct bgp_pbr_or_filter *bpof, + struct nexthop *nh, + float *rate, + uint8_t type_entry) +{ + struct listnode *node, *nnode; + struct bgp_pbr_val_mask *valmask; + uint8_t next_type_entry; + struct list *orig_list; + struct bgp_pbr_val_mask **target_val; + + if (type_entry == 0) + return bgp_pbr_policyroute_add_to_zebra_unit(bgp, binfo, bpf, + nh, rate); + next_type_entry = bgp_pbr_next_type_entry(type_entry); + if (type_entry == FLOWSPEC_TCP_FLAGS && bpof->tcpflags) { + orig_list = bpof->tcpflags; + target_val = &bpf->tcp_flags; + } else if (type_entry == FLOWSPEC_DSCP && bpof->dscp) { + orig_list = bpof->dscp; + target_val = &bpf->dscp; + } else if (type_entry == FLOWSPEC_PKT_LEN && bpof->pkt_len) { + orig_list = bpof->pkt_len; + target_val = &bpf->pkt_len_val; + } else if (type_entry == FLOWSPEC_FRAGMENT && bpof->fragment) { + orig_list = bpof->fragment; + target_val = &bpf->fragment; + } else if (type_entry == FLOWSPEC_ICMP_TYPE && + (bpof->icmp_type || bpof->icmp_code)) { + /* enumerate list for icmp - must be last one */ + bgp_pbr_icmp_action(bgp, binfo, bpf, bpof, true, nh, rate); + return; + } else { + return bgp_pbr_policyroute_add_to_zebra_recursive(bgp, binfo, + bpf, bpof, nh, rate, + next_type_entry); + } + for (ALL_LIST_ELEMENTS(orig_list, node, nnode, valmask)) { + *target_val = valmask; + bgp_pbr_policyroute_add_to_zebra_recursive(bgp, binfo, + bpf, bpof, + nh, rate, + next_type_entry); + } +} + +static void bgp_pbr_policyroute_add_to_zebra(struct bgp *bgp, + struct bgp_info *binfo, + struct bgp_pbr_filter *bpf, + struct bgp_pbr_or_filter *bpof, + struct nexthop *nh, + float *rate) +{ + if (!bpof) + return bgp_pbr_policyroute_add_to_zebra_unit(bgp, binfo, + bpf, nh, rate); + if (bpof->tcpflags) + bgp_pbr_policyroute_add_to_zebra_recursive(bgp, binfo, + bpf, bpof, + nh, rate, + FLOWSPEC_TCP_FLAGS); + else if (bpof->dscp) + bgp_pbr_policyroute_add_to_zebra_recursive(bgp, binfo, + bpf, bpof, + nh, rate, + FLOWSPEC_DSCP); + else if (bpof->pkt_len) + bgp_pbr_policyroute_add_to_zebra_recursive(bgp, binfo, + bpf, bpof, + nh, rate, + FLOWSPEC_PKT_LEN); + else if (bpof->fragment) + bgp_pbr_policyroute_add_to_zebra_recursive(bgp, binfo, + bpf, bpof, + nh, rate, + FLOWSPEC_FRAGMENT); + else if (bpof->icmp_type || bpof->icmp_code) + bgp_pbr_policyroute_add_to_zebra_recursive(bgp, binfo, + bpf, bpof, nh, rate, + FLOWSPEC_ICMP_TYPE); + else + bgp_pbr_policyroute_add_to_zebra_unit(bgp, binfo, bpf, + nh, rate); + /* flush bpof */ + if (bpof->tcpflags) + list_delete_all_node(bpof->tcpflags); + if (bpof->dscp) + list_delete_all_node(bpof->dscp); + if (bpof->pkt_len) + list_delete_all_node(bpof->pkt_len); + if (bpof->fragment) + list_delete_all_node(bpof->fragment); + if (bpof->icmp_type) + list_delete_all_node(bpof->icmp_type); + if (bpof->icmp_code) + list_delete_all_node(bpof->icmp_code); +} + static void bgp_pbr_handle_entry(struct bgp *bgp, struct bgp_info *binfo, struct bgp_pbr_entry_main *api, @@ -1219,9 +1979,16 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, struct prefix *src = NULL, *dst = NULL; uint8_t proto = 0; struct bgp_pbr_range_port *srcp = NULL, *dstp = NULL; - struct bgp_pbr_range_port range; + struct bgp_pbr_range_port range, range_icmp_code; + struct bgp_pbr_range_port pkt_len; + struct bgp_pbr_filter bpf; + uint8_t kind_enum; + struct bgp_pbr_or_filter bpof; + struct bgp_pbr_val_mask bpvm; memset(&nh, 0, sizeof(struct nexthop)); + memset(&bpf, 0, sizeof(struct bgp_pbr_filter)); + memset(&bpof, 0, sizeof(struct bgp_pbr_or_filter)); if (api->match_bitmask & PREFIX_SRC_PRESENT) src = &api->src_prefix; if (api->match_bitmask & PREFIX_DST_PRESENT) @@ -1251,10 +2018,97 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, dstp = ⦥ srcp = NULL; } + if (api->match_icmp_type_num >= 1) { + proto = IPPROTO_ICMP; + if (bgp_pbr_extract(api->icmp_type, + api->match_icmp_type_num, + &range)) + srcp = ⦥ + else { + bpof.icmp_type = list_new(); + bgp_pbr_extract_enumerate(api->icmp_type, + api->match_icmp_type_num, + OPERATOR_UNARY_OR, + bpof.icmp_type, + FLOWSPEC_ICMP_TYPE); + } + } + if (api->match_icmp_code_num >= 1) { + proto = IPPROTO_ICMP; + if (bgp_pbr_extract(api->icmp_code, + api->match_icmp_code_num, + &range_icmp_code)) + dstp = &range_icmp_code; + else { + bpof.icmp_code = list_new(); + bgp_pbr_extract_enumerate(api->icmp_code, + api->match_icmp_code_num, + OPERATOR_UNARY_OR, + bpof.icmp_code, + FLOWSPEC_ICMP_CODE); + } + } + + if (api->match_tcpflags_num) { + kind_enum = bgp_pbr_match_val_get_operator(api->tcpflags, + api->match_tcpflags_num); + if (kind_enum == OPERATOR_UNARY_AND) { + bpf.tcp_flags = &bpvm; + bgp_pbr_extract_enumerate(api->tcpflags, + api->match_tcpflags_num, + OPERATOR_UNARY_AND, + bpf.tcp_flags, + FLOWSPEC_TCP_FLAGS); + } else if (kind_enum == OPERATOR_UNARY_OR) { + bpof.tcpflags = list_new(); + bgp_pbr_extract_enumerate(api->tcpflags, + api->match_tcpflags_num, + OPERATOR_UNARY_OR, + bpof.tcpflags, + FLOWSPEC_TCP_FLAGS); + } + } + if (api->match_packet_length_num) { + bool ret; + + ret = bgp_pbr_extract(api->packet_length, + api->match_packet_length_num, + &pkt_len); + if (ret) + bpf.pkt_len = &pkt_len; + else { + bpof.pkt_len = list_new(); + bgp_pbr_extract_enumerate(api->packet_length, + api->match_packet_length_num, + OPERATOR_UNARY_OR, + bpof.pkt_len, + FLOWSPEC_PKT_LEN); + } + } + if (api->match_dscp_num >= 1) { + bpof.dscp = list_new(); + bgp_pbr_extract_enumerate(api->dscp, api->match_dscp_num, + OPERATOR_UNARY_OR, + bpof.dscp, FLOWSPEC_DSCP); + } + if (api->match_fragment_num) { + bpof.fragment = list_new(); + bgp_pbr_extract_enumerate(api->fragment, + api->match_fragment_num, + OPERATOR_UNARY_OR, + bpof.fragment, + FLOWSPEC_FRAGMENT); + } + bpf.vrf_id = api->vrf_id; + bpf.src = src; + bpf.dst = dst; + bpf.protocol = proto; + bpf.src_port = srcp; + bpf.dst_port = dstp; if (!add) - return bgp_pbr_policyroute_remove_from_zebra(bgp, binfo, - api->vrf_id, src, dst, - proto, srcp, dstp); + return bgp_pbr_policyroute_remove_from_zebra(bgp, + binfo, + &bpf, &bpof); /* no action for add = true */ for (i = 0; i < api->action_num; i++) { switch (api->actions[i].action) { @@ -1264,9 +2118,8 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, nh.vrf_id = api->vrf_id; nh.type = NEXTHOP_TYPE_BLACKHOLE; bgp_pbr_policyroute_add_to_zebra(bgp, binfo, - api->vrf_id, src, dst, - &nh, &rate, proto, - srcp, dstp); + &bpf, &bpof, + &nh, &rate); } else { /* update rate. can be reentrant */ rate = api->actions[i].u.r.rate; @@ -1307,10 +2160,8 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, api->actions[i].u.zr.redirect_ip_v4.s_addr; nh.vrf_id = api->vrf_id; bgp_pbr_policyroute_add_to_zebra(bgp, binfo, - api->vrf_id, - src, dst, - &nh, &rate, proto, - srcp, dstp); + &bpf, &bpof, + &nh, &rate); /* XXX combination with REDIRECT_VRF * + REDIRECT_NH_IP not done */ @@ -1320,10 +2171,8 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, nh.vrf_id = api->actions[i].u.redirect_vrf; nh.type = NEXTHOP_TYPE_IPV4; bgp_pbr_policyroute_add_to_zebra(bgp, binfo, - api->vrf_id, - src, dst, - &nh, &rate, proto, - srcp, dstp); + &bpf, &bpof, + &nh, &rate); continue_loop = 0; break; case ACTION_MARKING: diff --git a/bgpd/bgp_pbr.h b/bgpd/bgp_pbr.h index 20edaf30b8..307a34e34f 100644 --- a/bgpd/bgp_pbr.h +++ b/bgpd/bgp_pbr.h @@ -107,7 +107,6 @@ struct bgp_pbr_entry_main { #define PREFIX_SRC_PRESENT (1 << 0) #define PREFIX_DST_PRESENT (1 << 1) -#define FRAGMENT_PRESENT (1 << 2) uint8_t match_bitmask; uint8_t match_src_port_num; @@ -119,12 +118,14 @@ struct bgp_pbr_entry_main { uint8_t match_packet_length_num; uint8_t match_dscp_num; uint8_t match_tcpflags_num; + uint8_t match_fragment_num; struct prefix src_prefix; struct prefix dst_prefix; #define PROTOCOL_UDP 17 #define PROTOCOL_TCP 6 +#define PROTOCOL_ICMP 1 struct bgp_pbr_match_val protocol[BGP_PBR_MATCH_VAL_MAX]; struct bgp_pbr_match_val src_port[BGP_PBR_MATCH_VAL_MAX]; struct bgp_pbr_match_val dst_port[BGP_PBR_MATCH_VAL_MAX]; @@ -133,8 +134,9 @@ struct bgp_pbr_entry_main { struct bgp_pbr_match_val icmp_code[BGP_PBR_MATCH_VAL_MAX]; struct bgp_pbr_match_val packet_length[BGP_PBR_MATCH_VAL_MAX]; struct bgp_pbr_match_val dscp[BGP_PBR_MATCH_VAL_MAX]; + struct bgp_pbr_match_val tcpflags[BGP_PBR_MATCH_VAL_MAX]; - struct bgp_pbr_fragment_val fragment; + struct bgp_pbr_match_val fragment[BGP_PBR_MATCH_VAL_MAX]; uint16_t action_num; struct bgp_pbr_entry_action actions[ACTIONS_MAX_NUM]; @@ -176,14 +178,15 @@ struct bgp_pbr_match { */ uint32_t type; -#define MATCH_IP_SRC_SET (1 << 0) -#define MATCH_IP_DST_SET (1 << 1) -#define MATCH_PORT_SRC_SET (1 << 2) -#define MATCH_PORT_DST_SET (1 << 3) -#define MATCH_PORT_SRC_RANGE_SET (1 << 4) -#define MATCH_PORT_DST_RANGE_SET (1 << 5) uint32_t flags; + uint16_t pkt_len_min; + uint16_t pkt_len_max; + uint16_t tcp_flags; + uint16_t tcp_mask_flags; + uint8_t dscp_value; + uint8_t fragment; + vrf_id_t vrf_id; /* unique identifier for ipset create transaction diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 95e7def8fb..7057b62f2b 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -1318,6 +1318,8 @@ void bgp_attr_add_gshut_community(struct attr *attr) old = attr->community; gshut = community_str2com("graceful-shutdown"); + assert(gshut); + if (old) { merge = community_merge(community_dup(old), gshut); @@ -6280,7 +6282,6 @@ static void route_vty_out_route(struct prefix *p, struct vty *vty, prefix2str(p, buf, PREFIX_STRLEN); len = vty_out(vty, "%s", buf); } else if (p->family == AF_EVPN) { -#if defined(HAVE_CUMULUS) if (!json) len = vty_out( vty, "%s", @@ -6288,10 +6289,6 @@ static void route_vty_out_route(struct prefix *p, struct vty *vty, BUFSIZ)); else bgp_evpn_route2json((struct prefix_evpn *)p, json); -#else - prefix2str(p, buf, PREFIX_STRLEN); - len = vty_out(vty, "%s", buf); -#endif } else if (p->family == AF_FLOWSPEC) { route_vty_out_flowspec(vty, p, NULL, json ? @@ -6564,14 +6561,8 @@ void route_vty_out(struct vty *vty, struct prefix *p, struct bgp_info *binfo, } else { char buf[BUFSIZ]; - if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_EVPN)) - snprintf(buf, sizeof(buf), "%s%s", - inet_ntoa(attr->mp_nexthop_global_in), - vrf_id_str); - else - snprintf(buf, sizeof(buf), "%s%s", - inet_ntoa(attr->nexthop), - vrf_id_str); + snprintf(buf, sizeof(buf), "%s%s", + inet_ntoa(attr->nexthop), vrf_id_str); vty_out(vty, "%-16s", buf); } } @@ -7305,9 +7296,7 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct prefix *p, { char buf[INET6_ADDRSTRLEN]; char buf1[BUFSIZ]; -#if defined(HAVE_CUMULUS) char buf2[EVPN_ROUTE_STRLEN]; -#endif struct attr *attr; int sockunion_vty_out(struct vty *, union sockunion *); time_t tbuf; @@ -7340,7 +7329,6 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct prefix *p, json_nexthop_global = json_object_new_object(); } -#if defined(HAVE_CUMULUS) if (!json_paths && safi == SAFI_EVPN) { char tag_buf[30]; @@ -7370,7 +7358,6 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct prefix *p, } } } -#endif attr = binfo->attr; @@ -8023,14 +8010,9 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct prefix *p, if (binfo->extra && binfo->extra->damp_info) bgp_damp_info_vty(vty, binfo, json_path); -/* Remote Label */ -#if defined(HAVE_CUMULUS) + /* Remote Label */ if (binfo->extra && bgp_is_valid_label(&binfo->extra->label[0]) - && safi != SAFI_EVPN) -#else - if (binfo->extra && bgp_is_valid_label(&binfo->extra->label[0])) -#endif - { + && safi != SAFI_EVPN) { mpls_label_t label = label_pton(&binfo->extra->label[0]); if (json_paths) @@ -8601,9 +8583,7 @@ void route_vty_out_detail_header(struct vty *vty, struct bgp *bgp, struct listnode *node, *nnode; char buf1[RD_ADDRSTRLEN]; char buf2[INET6_ADDRSTRLEN]; -#if defined(HAVE_CUMULUS) char buf3[EVPN_ROUTE_STRLEN]; -#endif char prefix_str[BUFSIZ]; int count = 0; int best = 0; @@ -8630,7 +8610,6 @@ void route_vty_out_detail_header(struct vty *vty, struct bgp *bgp, json, "prefix", prefix2str(p, prefix_str, sizeof(prefix_str))); } else { -#if defined(HAVE_CUMULUS) if (safi == SAFI_EVPN) vty_out(vty, "BGP routing table entry for %s%s%s\n", prd ? prefix_rd2str(prd, buf1, sizeof(buf1)) @@ -8648,29 +8627,10 @@ void route_vty_out_detail_header(struct vty *vty, struct bgp *bgp, inet_ntop(p->family, &p->u.prefix, buf2, INET6_ADDRSTRLEN), p->prefixlen); -#else - if (p->family == AF_ETHERNET) - prefix2str(p, buf2, INET6_ADDRSTRLEN); - else - inet_ntop(p->family, &p->u.prefix, buf2, - INET6_ADDRSTRLEN); - vty_out(vty, "BGP routing table entry for %s%s%s/%d\n", - ((safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP - || safi == SAFI_EVPN) - ? prefix_rd2str(prd, buf1, sizeof(buf1)) - : ""), - ((safi == SAFI_MPLS_VPN) || (safi == SAFI_EVPN)) ? ":" - : "", - buf2, p->prefixlen); -#endif if (has_valid_label) vty_out(vty, "Local label: %d\n", label); -#if defined(HAVE_CUMULUS) if (bgp_labeled_safi(safi) && safi != SAFI_EVPN) -#else - if (bgp_labeled_safi(safi)) -#endif vty_out(vty, "not allocated\n"); } diff --git a/bgpd/bgp_rpki.c b/bgpd/bgp_rpki.c index ac4dabc164..82e857dbf4 100644 --- a/bgpd/bgp_rpki.c +++ b/bgpd/bgp_rpki.c @@ -158,6 +158,30 @@ static void free_wrapper(void *ptr) XFREE(MTYPE_BGP_RPKI_CACHE, ptr); } +static void init_tr_socket(struct cache *cache) +{ + if (cache->type == TCP) + tr_tcp_init(cache->tr_config.tcp_config, + cache->tr_socket); +#if defined(FOUND_SSH) + else + tr_ssh_init(cache->tr_config.ssh_config, + cache->tr_socket); +#endif +} + +static void free_tr_socket(struct cache *cache) +{ + if (cache->type == TCP) + tr_tcp_init(cache->tr_config.tcp_config, + cache->tr_socket); +#if defined(FOUND_SSH) + else + tr_ssh_init(cache->tr_config.ssh_config, + cache->tr_socket); +#endif +} + static int rpki_validate_prefix(struct peer *peer, struct attr *attr, struct prefix *prefix); @@ -253,14 +277,7 @@ static struct rtr_mgr_group *get_groups(void) rtr_mgr_groups[i].sockets_len = 1; rtr_mgr_groups[i].preference = cache->preference; - if (cache->type == TCP) - tr_tcp_init(cache->tr_config.tcp_config, - cache->tr_socket); -#if defined(FOUND_SSH) - else - tr_ssh_init(cache->tr_config.ssh_config, - cache->tr_socket); -#endif + init_tr_socket(cache); i++; } @@ -517,9 +534,13 @@ static int add_cache(struct cache *cache) listnode_add(cache_list, cache); - if (rtr_is_running - && rtr_mgr_add_group(rtr_config, &group) != RTR_SUCCESS) { - return ERROR; + if (rtr_is_running) { + init_tr_socket(cache); + + if (rtr_mgr_add_group(rtr_config, &group) != RTR_SUCCESS) { + free_tr_socket(cache); + return ERROR; + } } return SUCCESS; diff --git a/bgpd/bgp_updgrp_packet.c b/bgpd/bgp_updgrp_packet.c index cabd5b5cbd..34ddbfcd14 100644 --- a/bgpd/bgp_updgrp_packet.c +++ b/bgpd/bgp_updgrp_packet.c @@ -397,7 +397,7 @@ struct stream *bpacket_reformat_for_peer(struct bpacket *pkt, vec = &pkt->arr.entries[BGP_ATTR_VEC_NH]; if (CHECK_FLAG(vec->flags, BPKT_ATTRVEC_FLAGS_UPDATED)) { uint8_t nhlen; - afi_t nhafi = AFI_MAX; /* NH AFI is based on nhlen! */ + afi_t nhafi; int route_map_sets_nh; nhlen = stream_getc_from(s, vec->offset); if (peer_cap_enhe(peer, paf->afi, paf->safi)) diff --git a/bgpd/bgp_vpn.c b/bgpd/bgp_vpn.c index 351f91dd1a..a771eedf0f 100644 --- a/bgpd/bgp_vpn.c +++ b/bgpd/bgp_vpn.c @@ -125,7 +125,7 @@ int show_adj_route_vpn(struct vty *vty, struct peer *peer, if (rd_header) { uint16_t type; - struct rd_as rd_as; + struct rd_as rd_as = {0}; struct rd_ip rd_ip = {0}; #if ENABLE_BGP_VNC struct rd_vnc_eth rd_vnc_eth = { @@ -223,21 +223,27 @@ int show_adj_route_vpn(struct vty *vty, struct peer *peer, } rd_header = 0; } - route_vty_out_tmp(vty, &rm->p, attr, - SAFI_MPLS_VPN, - use_json, json_array); + if (use_json) { + char buf_a[BUFSIZ]; + char buf_b[BUFSIZ]; + + sprintf(buf_a, "%s/%d", + inet_ntop(rm->p.family, + rm->p.u.val, + buf_b, + BUFSIZ), + rm->p.prefixlen); + json_object_object_add( + json_routes, buf_a, + json_array); + } else { + route_vty_out_tmp( + vty, &rm->p, attr, + SAFI_MPLS_VPN, use_json, + json_array); + } } } - if (use_json && rm) { - char buf_a[BUFSIZ]; - char buf_b[BUFSIZ]; - sprintf(buf_a, "%s/%d", - inet_ntop(rm->p.family, rm->p.u.val, - buf_b, BUFSIZ), - rm->p.prefixlen); - json_object_object_add(json_routes, buf_a, - json_array); - } } } if (use_json) { diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 4e0f7155ba..86f3f97c49 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -2009,27 +2009,19 @@ DEFUN (no_bgp_fast_external_failover, CPP_NOTICE("bgpd: remove deprecated '[no] bgp enforce-first-as' commands") #endif -DEFUN_DEPRECATED (bgp_enforce_first_as, - bgp_enforce_first_as_cmd, - "bgp enforce-first-as", - BGP_STR - "Enforce the first AS for EBGP routes\n") +DEFUN_HIDDEN (bgp_enforce_first_as, + bgp_enforce_first_as_cmd, + "[no] bgp enforce-first-as", + NO_STR + BGP_STR + "Enforce the first AS for EBGP routes\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); - bgp_flag_set(bgp, BGP_FLAG_ENFORCE_FIRST_AS); - - return CMD_SUCCESS; -} -DEFUN_DEPRECATED (no_bgp_enforce_first_as, - no_bgp_enforce_first_as_cmd, - "no bgp enforce-first-as", - NO_STR - BGP_STR - "Enforce the first AS for EBGP routes\n") -{ - VTY_DECLVAR_CONTEXT(bgp, bgp); - bgp_flag_unset(bgp, BGP_FLAG_ENFORCE_FIRST_AS); + if (strmatch(argv[0]->text, "no")) + bgp_flag_unset(bgp, BGP_FLAG_ENFORCE_FIRST_AS); + else + bgp_flag_set(bgp, BGP_FLAG_ENFORCE_FIRST_AS); return CMD_SUCCESS; } @@ -6745,6 +6737,11 @@ DEFPY (bgp_imexport_vrf, safi_t safi; afi_t afi; + if (import_name == NULL) { + vty_out(vty, "%% Missing import name\n"); + return CMD_WARNING; + } + if (argv_find(argv, argc, "no", &idx)) remove = true; @@ -7075,7 +7072,7 @@ static int bgp_clear_prefix(struct vty *vty, const char *view_name, != NULL) { if (rm->p.prefixlen == match.prefixlen) { - SET_FLAG(rn->flags, + SET_FLAG(rm->flags, BGP_NODE_USER_CLEAR); bgp_process(bgp, rm, afi, safi); } @@ -7938,6 +7935,11 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi, if (CHECK_FLAG(peer->flags, PEER_FLAG_SHUTDOWN)) json_object_string_add(json_peer, "state", "Idle (Admin)"); + else if (peer->afc_recv[afi][safi]) + json_object_string_add( + json_peer, "state", + lookup_msg(bgp_status_msg, peer->status, + NULL)); else if (CHECK_FLAG(peer->sflags, PEER_STATUS_PREFIX_OVERFLOW)) json_object_string_add(json_peer, "state", @@ -11409,7 +11411,6 @@ DEFUN (show_ip_bgp_peer_groups, "Peer group name\n") { char *vrf, *pg; - vrf = pg = NULL; int idx = 0; vrf = argv_find(argv, argc, "VIEWVRFNAME", &idx) ? argv[idx]->arg @@ -12440,7 +12441,6 @@ void bgp_vty_init(void) /* "bgp enforce-first-as" commands */ install_element(BGP_NODE, &bgp_enforce_first_as_cmd); - install_element(BGP_NODE, &no_bgp_enforce_first_as_cmd); /* "bgp bestpath compare-routerid" commands */ install_element(BGP_NODE, &bgp_bestpath_compare_router_id_cmd); diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index 2d808a6ffb..df3f9ddd6f 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -2219,6 +2219,12 @@ static void bgp_encode_pbr_iptable_match(struct stream *s, stream_putl(s, bpa->fwmark); stream_put(s, pbm->ipset_name, ZEBRA_IPSET_NAME_SIZE); + stream_putw(s, pbm->pkt_len_min); + stream_putw(s, pbm->pkt_len_max); + stream_putw(s, pbm->tcp_flags); + stream_putw(s, pbm->tcp_mask_flags); + stream_putc(s, pbm->dscp_value); + stream_putc(s, pbm->fragment); } /* BGP has established connection with Zebra. */ diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 64f36ec6e3..283949ab2a 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -1032,6 +1032,8 @@ static inline bgp_peer_sort_t peer_calc_sort(struct peer *peer) else { struct peer *peer1; + + assert(peer->group); peer1 = listnode_head(peer->group->peer); if (peer1) @@ -2733,9 +2735,8 @@ int peer_group_bind(struct bgp *bgp, union sockunion *su, struct peer *peer, if (peer->group) { assert(group && peer->group == group); } else { - struct listnode *pn; - pn = listnode_lookup(bgp->peer, peer); - list_delete_node(bgp->peer, pn); + listnode_delete(bgp->peer, peer); + peer->group = group; listnode_add_sort(bgp->peer, peer); diff --git a/bgpd/rfapi/bgp_rfapi_cfg.c b/bgpd/rfapi/bgp_rfapi_cfg.c index 72255e54fb..8553846c90 100644 --- a/bgpd/rfapi/bgp_rfapi_cfg.c +++ b/bgpd/rfapi/bgp_rfapi_cfg.c @@ -1426,7 +1426,8 @@ DEFUN (vnc_export_nvegroup, if (rfg_new == NULL) { rfg_new = bgp_rfapi_cfg_match_byname(bgp, argv[5]->arg, RFAPI_GROUP_CFG_VRF); - vnc_add_vrf_opener(bgp, rfg_new); + if (rfg_new) + vnc_add_vrf_opener(bgp, rfg_new); } if (rfg_new == NULL) { @@ -4518,7 +4519,7 @@ void bgp_rfapi_show_summary(struct bgp *bgp, struct vty *vty) if (VNC_EXPORT_ZEBRA_GRP_ENABLED(hc)) { redist++; vty_out(vty, "%sToZebra Groups={", (redist == 1 ? "" : " ")); - if (hc->rfg_export_direct_bgp_l) { + if (hc->rfg_export_zebra_l) { int cnt = 0; struct listnode *node, *nnode; struct rfapi_rfg_name *rfgn; diff --git a/bgpd/rfapi/rfapi_vty.c b/bgpd/rfapi/rfapi_vty.c index 18a979e531..cd12edbccb 100644 --- a/bgpd/rfapi/rfapi_vty.c +++ b/bgpd/rfapi/rfapi_vty.c @@ -388,15 +388,11 @@ int rfapiStream2Vty(void *stream, /* input */ return 1; } - if (stream) { - *vty = stream; /* VTYNL requires vty to be legit */ - *fp = (int (*)(void *, const char *, ...))vty_out; - *outstream = stream; - *vty_newline = str_vty_newline(*vty); - return 1; - } - - return 0; + *vty = stream; /* VTYNL requires vty to be legit */ + *fp = (int (*)(void *, const char *, ...))vty_out; + *outstream = stream; + *vty_newline = str_vty_newline(*vty); + return 1; } /* called from bgpd/bgp_vty.c'route_vty_out() */ @@ -3021,9 +3017,9 @@ static int rfapiDeleteLocalPrefixesByRFD(struct rfapi_local_reg_delete_arg *cda, * match un, vn addresses of NVEs */ if (pUn && (rfapi_ip_addr_cmp(pUn, &rfd->un_addr))) - continue; + break; if (pVn && (rfapi_ip_addr_cmp(pVn, &rfd->vn_addr))) - continue; + break; #if DEBUG_L2_EXTRA vnc_zlog_debug_verbose("%s: un, vn match", __func__); diff --git a/doc/user/basic.rst b/doc/user/basic.rst index a75017c442..cb46080055 100644 --- a/doc/user/basic.rst +++ b/doc/user/basic.rst @@ -405,6 +405,18 @@ These options apply to all |PACKAGE_NAME| daemons. Print program version. +.. option:: --log <stdout|syslog|file:/path/to/log/file> + + When initializing the daemon, setup the log to go to either stdout, + syslog or to a file. These values will be displayed as part of + a show run. Additionally they can be overridden at runtime if + desired via the normal log commands. + +.. option:: --log-level <emergencies|alerts|critical|errors|warnings|notifications|informational|debugging> + + When initializing the daemon, allow the specification of a default + log level at startup from one of the specified levels. + .. _loadable-module-support: Loadable Module Support diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst index 13e0252210..725c55b15b 100644 --- a/doc/user/bgp.rst +++ b/doc/user/bgp.rst @@ -60,11 +60,11 @@ Address Families ---------------- Multiprotocol BGP enables BGP to carry routing information for multiple Network -Layer protocols. BGP supports multiple Address Family Identifier (AFI), namely -IPv4 and IPv6. Support is also provided for multiple sets of per-AFI -information via Subsequent Address Family Identifiers (SAFI). In addition to -unicast information, VPN information :rfc:`4364` and :rfc:`4659`, and -Encapsulation attribute :rfc:`5512` is supported. +Layer protocols. BGP supports an Address Family Identifier (AFI) for IPv4 and +IPv6. Support is also provided for multiple sets of per-AFI information via the +BGP Subsequent Address Family Identifier (SAFI). FRR supports SAFIs for unicast +information, labeled information :rfc:`3107` and :rfc:`8277`, and Layer 3 VPN +information :rfc:`4364` and :rfc:`4659`. .. _bgp-route-selection: @@ -174,6 +174,19 @@ will establish the connection with unicast only capability. When there are no common capabilities, FRR sends Unsupported Capability error and then resets the connection. +.. _bgp-concepts-vrfs: + +VRFs: Virtual Routing and Forwarding +------------------------------------ + +*bgpd* supports :abbr:`L3VPN (Layer 3 Virtual Private Networks)` :abbr:`VRFs +(Virtual Routing and Forwarding tables)` for IPv4 :rfc:`4364` and IPv6 +:rfc:`4659`. L3VPN routes, and their associated VRF MPLS labels, can be +distributed to VPN SAFI neighbors in the *default*, i.e., non VRF, BGP +instance. VRF MPLS labels are reached using *core* MPLS labels which are +distributed using LDP or BGP labeled unicast. *bgpd* also supports inter-VRF +route leaking. General information on FRR's VRF support can be found in +:ref:`zebra-vrf`. .. _bgp-router-configuration: @@ -1550,10 +1563,11 @@ VRF Route Leaking ^^^^^^^^^^^^^^^^^ BGP routes may be leaked (i.e. copied) between a unicast VRF RIB and the VPN -SAFI RIB of the default VRF (leaking is also permitted between the unicast RIB -of the default VRF and VPN). A shortcut syntax is also available for specifying -leaking from one vrf to another vrf using the VPN RIB as the intemediary. A -common application of the VPN-VRF feature is to connect a customer's private +SAFI RIB of the default VRF for use in MPLS-based L3VPNs. Unicast routes may +also be leaked between any VRFs (including the unicast RIB of the default BGP +instanced). A shortcut syntax is also available for specifying leaking from one +VRF to another VRF using the default instance's VPN RIB as the intemediary. A +common application of the VRF-VRF feature is to connect a customer's private routing domain to a provider's VPN service. Leaking is configured from the point of view of an individual VRF: ``import`` refers to routes leaked from VPN to a unicast VRF, whereas ``export`` refers to routes leaked from a unicast VRF @@ -1596,7 +1610,7 @@ auto-derived. General configuration """"""""""""""""""""" -Configuration of route leaking between a unicast VRF RIB and the VPN safi RIB +Configuration of route leaking between a unicast VRF RIB and the VPN SAFI RIB of the default VRF is accomplished via commands in the context of a VRF address-family: @@ -1850,22 +1864,22 @@ Debugging Show all enabled debugs. -.. index:: [no] debug neighbor-events -.. clicmd:: [no] debug neighbor-events +.. index:: [no] debug bgp neighbor-events +.. clicmd:: [no] debug bgp neighbor-events Enable or disable debugging for neighbor events. This provides general information on BGP events such as peer connection / disconnection, session establishment / teardown, and capability negotiation. -.. index:: [no] debug updates -.. clicmd:: [no] debug updates +.. index:: [no] debug bgp updates +.. clicmd:: [no] debug bgp updates Enable or disable debugging for BGP updates. This provides information on BGP UPDATE messages transmitted and received between local and remote instances. -.. index:: [no] debug keepalives -.. clicmd:: [no] debug keepalives +.. index:: [no] debug bgp keepalives +.. clicmd:: [no] debug bgp keepalives Enable or disable debugging for BGP keepalives. This provides information on BGP KEEPALIVE messages transmitted and received between local and remote @@ -2434,6 +2448,8 @@ Example of how to set up a 6-Bone connection. .. include:: rpki.rst +.. include:: flowspec.rst + .. [#med-transitivity-rant] For some set of objects to have an order, there *must* be some binary ordering relation that is defined for *every* combination of those objects, and that relation *must* be transitive. I.e.:, if the relation operator is <, and if a < b and b < c then that relation must carry over and it *must* be that a < c for the objects to have an order. The ordering relation may allow for equality, i.e. a < b and b < a may both be true and imply that a and b are equal in the order and not distinguished by it, in which case the set has a partial order. Otherwise, if there is an order, all the objects have a distinct place in the order and the set has a total order) .. [bgp-route-osci-cond] McPherson, D. and Gill, V. and Walton, D., "Border Gateway Protocol (BGP) Persistent Route Oscillation Condition", IETF RFC3345 .. [stable-flexible-ibgp] Flavel, A. and M. Roughan, "Stable and flexible iBGP", ACM SIGCOMM 2009 diff --git a/doc/user/conf.py b/doc/user/conf.py index 3fced11024..28081bca7d 100644 --- a/doc/user/conf.py +++ b/doc/user/conf.py @@ -131,7 +131,8 @@ language = None # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. -exclude_patterns = ['_build', 'rpki.rst', 'routeserver.rst', 'ospf_fundamentals.rst'] +exclude_patterns = ['_build', 'rpki.rst', 'routeserver.rst', + 'ospf_fundamentals.rst', 'flowspec.rst'] # The reST default role (used for this markup: `text`) to use for all # documents. diff --git a/doc/user/flowspec.rst b/doc/user/flowspec.rst new file mode 100644 index 0000000000..4672b143ec --- /dev/null +++ b/doc/user/flowspec.rst @@ -0,0 +1,348 @@ +.. _flowspec: + +Flowspec +======== + +.. _features-of-the-current-implementation-flowspec: + +Overview +--------- + +Flowspec introduces a new :abbr:`NLRI (Network Layer Reachability Information)` +encoding format that is used to distribute traffic rule flow specifications. +Basically, instead of simply relying on destination IP address for IP prefixes, +the IP prefix is replaced by a n-tuple consisting of a rule. That rule can be a +more or less complex combination of the following: + + +- Network source/destination (can be one or the other, or both). +- Layer 4 information for UDP/TCP : source port, destination port, or any port. +- Layer 4 information for ICMP type and ICMP code. +- Layer 4 information for TCP Flags. +- Layer 3 information : DSCP value, Protocol type, packet length, fragmentation. +- Misc layer 4 TCP flags. + +A combination of the above rules is applied for traffic filtering. This is +encoded as part of specific BGP extended communities and the action can range +from the obvious rerouting (to nexthop or to separate VRF) to shaping, or +discard. + +The following IETF drafts and RFCs have been used to implement FRR Flowspec: + +- :rfc:`5575` +- [Draft IETF IDR Flowspec redirect IP]_ + +.. _design-principles-flowspec: + +Design Principles +----------------- + +FRR implements the Flowspec client side, that is to say that BGP is able to +receive Flowspec entries, but is not able to act as manager and send Flowspec +entries. + +Linux provides the following mechanisms to implement policy based routing: + +- Filtering the traffic with ``Netfilter``. + ``Netfilter`` provides a set of tools like ``ipset`` and ``iptables`` that are + powerful enough to be able to filter such Flowspec filter rule. + +- using non standard routing tables via ``iproute2`` (via the ``ip rule`` + command provided by ``iproute2``). + ``iproute2`` is already used by FRR's :ref:`pbr` daemon which provides basic + policy based routing based on IP source and destination criterion. + +Below example is an illustration of what Flowspec will inject in the underlying +system: + +.. code-block:: shell + + # linux shell + ipset create match0x102 hash:net,net counters + ipset add match0x102 32.0.0.0/16,40.0.0.0/16 + iptables -N match0x102 -t mangle + iptables -A match0x102 -t mangle -j MARK --set-mark 102 + iptables -A match0x102 -t mangle -j ACCEPT + iptables -i ntfp3 -t mangle -I PREROUTING -m set --match-set match0x102 + src,dst -g match0x102 + ip rule add fwmark 102 lookup 102 + ip route add 40.0.0.0/16 via 44.0.0.2 table 102 + +For handling an incoming Flowspec entry, the following workflow is applied: + +- incoming Flowspec entries are handled by *bgpd*, stored in the BGP RIB. +- Flowspec entry is installed according to its complexity. + +It will be installed if one of the following filtering action is seen on the BGP +extended community: either redirect IP, or redirect VRF, in conjunction with +rate option, for redirecting traffic. Or rate option set to 0, for discarding +traffic. + +According to the degree of complexity of the Flowspec entry, it will be +installed in *zebra* RIB. For more information about what is supported in the +FRR implementation as rule, see :ref:`flowspec-known-issues` chapter. Flowspec +entry is split in several parts before being sent to *zebra*. + +- *zebra* daemon receives the policy routing configuration + +Policy Based Routing entities necessary to policy route the traffic in the +underlying system, are received by *zebra*. Two filtering contexts will be +created or appended in ``Netfilter``: ``ipset`` and ``iptable`` context. The +former is used to define an IP filter based on multiple criterium. For instance, +an ipset ``net:net`` is based on two ip addresses, while ``net,port,net`` is +based on two ip addresses and one port ( for ICMP, UDP, or TCP). The way the +filtering is used ( for example, is src port or dst port used ?) is defined by +the latter filtering context. ``iptable`` command will reference the ``ipset`` +context and will tell how to filter and what to do. In our case, a marker will +be set to indicate ``iproute2`` where to forward the traffic to. Sometimes, for +dropping action, there is no need to add a marker; the ``iptable`` will tell to +drop all packets matching the ``ipset`` entry. + +Configuration guide +------------------- + +In order to configure an IPv4 Flowspec engine, use the following configuration. +As of today, it is only possible to configure Flowspec on the default VRF. + +.. code-block:: frr + + router bgp <AS> + neighbor <A.B.C.D> remote-as <remoteAS> + address-family ipv4 flowspec + neighbor <A.B.C.D> activate + exit + exit + +You can see Flowspec entries, by using one of the following show commands: + +.. index:: show bgp ipv4 flowspec [detail | A.B.C.D] +.. clicmd:: show bgp ipv4 flowspec [detail | A.B.C.D] + + +Per-Interface Configuration +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +One nice feature to use is the ability to apply Flowspec to a specific +interface, instead of applying it to the whole machine. Despite the following +IETF draft [Draft IETF IDR Flowspec Interface Set]_ is not implemented, it is +possible to manually limit Flowspec application to some incoming interfaces. +Actually, not using it can result to some unexpected behaviour like accounting +twice the traffic, or slow down the traffic (filtering costs). To limit Flowspec +to one specific interface, use the following command, under +`flowspec address-family` node. + +.. index:: [no] local-install <IFNAME | any> +.. clicmd:: [no] local-install <IFNAME | any> + +By default, Flowspec is activated on all interfaces. Installing it to a named +interface will result in allowing only this interface. Conversely, enabling any +interface will flush all previously configured interfaces. + +VRF redirection +^^^^^^^^^^^^^^^ + +Another nice feature to configure is the ability to redirect traffic to a +separate VRF. This feature does not go against the ability to configure Flowspec +only on default VRF. Actually, when you receive incoming BGP flowspec entries on +that default VRF, you can redirect traffic to an other VRF. + +As a reminder, BGP flowspec entries have a BGP extended community that contains +a Route Target. Finding out a local VRF based on Route Target consists in the +following: + +- A configuration of each VRF must be done, with its Route Target set + Each VRF is being configured within a BGP VRF instance with its own Route + Target list. Route Target accepted format matches the following: + ``A.B.C.D:U16``, or ``U16:U32``, ``U32:U16``. + +- The first VRF with the matching Route Target will be selected to route traffic + to. Use the following command under ipv4 unicast address-family node + +.. index:: [no] rt redirect import RTLIST... +.. clicmd:: [no] rt redirect import RTLIST... + +In order to illustrate, if the Route Target configured in the Flowspec entry is +E.F.G.H:II, then a BGP VRF instance with the same Route Target will be set set. +That VRF will then be selected. The below full configuration example depicts how +Route Targets are configured and how VRFs and cross VRF configuration is done. +Note that the VRF are mapped on Linux Network Namespaces. For data traffic to +cross VRF boundaries, virtual ethernet interfaces are created with private IP +adressing scheme. + +.. code-block:: frr + + router bgp <ASx> + neighbor <A.B.C.D> remote-as <ASz> + address-family ipv4 flowspec + neighbor A.B.C.D activate + exit + exit + router bgp <ASy> vrf vrf2 + address-family ipv4 unicast + rt redirect import <E.F.G.H:II> + exit + exit + +Flowspec Monitor and troubleshooting +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +You can monitor policy-routing objects by using one of the following commands. +Those command rely on the filtering contexts configured from BGP, and get the +statistics information retrieved from the underlying system. In other words, +those statistics are retrieved from ``Netfilter``. + +.. index:: show pbr ipset IPSETNAME | iptable +.. clicmd:: show pbr ipset IPSETNAME | iptable + +``IPSETNAME`` is the policy routing object name created by ``ipset``. +About rule contexts, it is possible to know which rule has been configured to +policy-route some specific traffic. The :clicmd:`show pbr iptable` command +displays for forwarded traffic, which table is used. Then it is easy to use that +table identifier to dump the routing table that the forwarded traffic will +match. + +.. code-block:: frr + + show ip route table TABLEID + +``TABLEID`` is the table number identifier referencing the non standard routing +table used in this example. +You can troubleshoot Flowspec, or BGP policy based routing. For instance, if you +encounter some issues when decoding a Flowspec entry, you should enable +:clicmd:`debug bgp flowspec`. + +.. index:: [no] debug bgp flowspec +.. clicmd:: [no] debug bgp flowspec + +If you fail to apply the flowspec entry into *zebra*, there should be some +relationship with policy routing mechanism. Here, :clicmd:`debug bgp pbr error` +could help. + +.. index:: [no] debug bgp pbr [error] +.. clicmd:: [no] debug bgp pbr [error] + +To get information about policy routing contexts created/removed, only use +:clicmd:`debug bgp pbr` command. + +Ensuring that a Flowspec entry has been correctly installed and that incoming +traffic is policy-routed correctly can be checked like illustrated below. First +of all, you must check whether the Flowspec entry has been installed or not. + +.. code-block:: frr + + CLI# show bgp ipv4 flowspec 5.5.5.2/32 + BGP flowspec entry: (flags 0x418) + Destination Address 5.5.5.2/32 + IP Protocol = 17 + Destination Port >= 50 , <= 90 + FS:redirect VRF RT:255.255.255.255:255 + received for 18:41:37 + installed in PBR (match0x271ce00) + +This means that the Flowspec entry has been installed in a `iptable` +named `match0x271ce00`. Once you have confirmation it is installed, you can +check whether you find the associate entry by executing following command. You +can also check whether incoming traffic has been matched by looking at counter +line. + +.. code-block:: frr + + CLI# show pbr ipset match0x271ce00 + IPset match0x271ce00 type net,port + to 5.5.5.0/24:proto 6:80-120 (8) + pkts 1000, bytes 1000000 + to 5.5.5.2:proto 17:50-90 (5) + pkts 1692918, bytes 157441374 + +As you can see, the entry is present. note that an `iptable` entry can be used +to host several Flowspec entries. In order to know where the matching traffic is +redirected to, you have to look at the policy routing rules. The policy-routing +is done by forwarding traffic to a routing table number. That routing table +number is reached by using a `iptable`. The relationship between the routing +table number and the incoming traffic is a MARKER that is set by the IPtable +referencing the IPSet. In Flowspec case, `iptable` referencing the `ipset` +context have the same name. So it is easy to know which routing table is used by +issuing following command: + +.. code-block:: frr + + CLI# show pbr iptable + IPtable match0x271ce00 action redirect (5) + pkts 1700000, bytes 158000000 + table 257, fwmark 257 + ... + +As you can see, by using following Linux commands, the MARKER `0x101` is present +in both ``iptable`` and ``ip rule`` contexts. + +.. code-block:: shell + + # iptables -t mangle --list match0x271ce00 -v + Chain match0x271ce00 (1 references) + pkts bytes target prot opt in out source destination + 1700K 158M MARK all -- any any anywhere anywhere + MARK set 0x101 + 1700K 158M ACCEPT all -- any any anywhere anywhere + + # ip rule list + 0:from all lookup local + 0:from all fwmark 0x101 lookup 257 + 32766:from all lookup main + 32767:from all lookup default + +This allows us to see where the traffic is forwarded to. + +.. _flowspec-known-issues: + +Limitations / Known issues +-------------------------- + +As you can see, Flowspec is rich and can be very complex. +As of today, not all Flowspec rules will be able to be converted into Policy +Based Routing actions. + +- The ``Netfilter`` driver is not integrated into FRR yet. Not having this piece + of code prevents from injecting flowspec entries into the underlying system. + +- There are some limitations around filtering contexts + + If I take example of UDP ports, or TCP ports in Flowspec, the information + can be a range of ports, or a unique value. This case is handled. + However, complexity can be increased, if the flow is a combination of a list + of range of ports and an enumerate of unique values. Here this case is not + handled. Similarly, it is not possible to create a filter for both src port + and dst port. For instance, filter on src port from [1-1000] and dst port = + 80. The same kind of complexity is not possible for packet length, ICMP type, + ICMP code. + +There are some other known issues: + +- The validation procedure depicted in :rfc:`5575` is not available. + + This validation procedure has not been implemented, as this feature was not + used in the existing setups you shared wih us. + +- The filtering action shaper value, if positive, is not used to apply shaping. + + If value is positive, the traffic is redirected to the wished destination, + without any other action configured by Flowspec. + It is recommended to configure Quality of Service if needed, more globally on + a per interface basis. + +- upon crash or unknown event, *zebra* may not have time to flush pbr contexts. + + That is to say ``ipset``, ``iptable`` and ``ip rule`` contexts. This is also a + consequence due to the fact that ip rule / ipset / iptables are not discovered + at startup (not able to read appropriate contexts coming from Flowspec). + +Appendix +-------- + +More information with a public presentation that explains the design of Flowspec +inside FRRouting. + +[Presentation]_ + +.. [Draft IETF IDR Flowspec redirect IP] <https://tools.ietf.org/id/draft-ietf-idr-flowspec-redirect-ip-02.txt> +.. [Draft IETF IDR Flowspec Interface Set] <https://tools.ietf.org/id/draft-ietf-idr-flowspec-interfaceset-03.txt> +.. [Presentation] <https://docs.google.com/presentation/d/1ekQygUAG5yvQ3wWUyrw4Wcag0LgmbW1kV02IWcU4iUg/edit#slide=id.g378f0e1b5e_1_44> diff --git a/doc/user/installation.rst b/doc/user/installation.rst index 7a430fdf98..26d30f1e10 100644 --- a/doc/user/installation.rst +++ b/doc/user/installation.rst @@ -289,6 +289,91 @@ FRR will run with any kernel configuration but some recommendations do exist. (:ref:`rip`) or *ospfd* (:ref:`ospfv2`) because these protocols use multicast. +Linux sysctl settings and kernel modules +```````````````````````````````````````` + +There are several kernel parameters that impact overall operation of FRR when +using Linux as a router. Generally these parameters should be set in a +sysctl related configuration file, e.g., :file:`/etc/sysctl.conf` on +Ubuntu based systems and a new file +:file:`/etc/sysctl.d/90-routing-sysctl.conf` on Centos based systems. +Additional kernel modules are also needed to support MPLS forwarding. + +:makevar:`IPv4 and IPv6 forwarding` + The following are set to enable IP forwarding in the kernel: + + .. code-block:: shell + + net.ipv4.conf.all.forwarding=1 + net.ipv6.conf.all.forwarding=1 + +:makevar:`MPLS forwarding` + Basic MPLS kernel support was introduced 4.1, additional capability + was introduced in 4.3 and 4.5. For some general information on Linux + MPLS support see + https://www.netdevconf.org/1.1/proceedings/slides/prabhu-mpls-tutorial.pdf. + The following modules should be loaded to support MPLS forwarding, + and are generally added to a configuration file such as + :file:`/etc/modules-load.d/modules.conf`: + + .. code-block:: shell + + # Load MPLS Kernel Modules + mpls_router + mpls_iptunnel + + The following is an example to enable MPLS forwarding in the kernel: + + .. code-block:: shell + + # Enable MPLS Label processing on all interfaces + net.mpls.conf.eth0.input=1 + net.mpls.conf.eth1.input=1 + net.mpls.conf.eth2.input=1 + net.mpls.platform_labels=100000 + + Make sure to add a line equal to :file:`net.mpls.conf.<if>.input` for + each interface *'<if>'* used with MPLS and to set labels to an + appropriate value. + +:makevar:`VRF forwarding` + General information on Linux VRF support can be found in + https://www.kernel.org/doc/Documentation/networking/vrf.txt. Kernel + support for VRFs was introduced in 4.3 and improved upon through + 4.13, which is the version most used in FRR testing (as of June + 2018). Additional background on using Linux VRFs and kernel specific + features can be found in + http://schd.ws/hosted_files/ossna2017/fe/vrf-tutorial-oss.pdf. + + The following impacts how BGP TCP sockets are managed across VRFs: + + .. code-block:: shell + + net.ipv4.tcp_l3mdev_accept=0 + + With this setting a BGP TCP socket is opened per VRF. This setting + ensures that other TCP services, such as SSH, provided for non-VRF + purposes are blocked from VRF associated Linux interfaces. + + .. code-block:: shell + + net.ipv4.tcp_l3mdev_accept=1 + + With this setting a single BGP TCP socket is shared across the + system. This setting exposes any TCP service running on the system, + e.g., SSH, to all VRFs. Generally this setting is not used in + environments where VRFs are used to support multiple administrative + groups. + + **Important note** as of June 2018, Kernel versions 4.14-4.18 have a + known bug where VRF-specific TCP sockets are not properly handled. When + running these kernel versions, if unable to establish any VRF BGP + adjacencies, either downgrade to 4.13 or set + 'net.ipv4.tcp_l3mdev_accept=1'. The fix for this issue is planned to be + included in future kernel versions so upgrading your kernel may also + address this issue. + + Building ^^^^^^^^ diff --git a/doc/user/pbr.rst b/doc/user/pbr.rst index aa48a3cd4f..638767c557 100644 --- a/doc/user/pbr.rst +++ b/doc/user/pbr.rst @@ -33,7 +33,6 @@ Nexthop Groups Nexthop groups are a way to encapsulate ECMP information together. It's a listing of ECMP nexthops used to forward packets for when a pbr-map is matched. -.. index:: nexthop-group .. clicmd:: nexthop-group NAME Create a nexthop-group with an associated NAME. This will put you into a @@ -46,24 +45,38 @@ listing of ECMP nexthops used to forward packets for when a pbr-map is matched. are used to are allowed here. The syntax was intentionally kept the same as creating nexthops as you would for static routes. +.. clicmd:: [no] pbr table range (10000-4294966272) (10000-4294966272) + + Set or unset the range used to assign numeric table ID's to new + nexthop-group tables. Existing tables will not be modified to fit in this + range, so it is recommended to configure this before adding nexthop groups. + + .. seealso:: :ref:`pbr-details` + +Showing Nexthop Group Information +--------------------------------- + +.. clicmd:: show pbr nexthop-groups [NAME] + + Display information on a PBR nexthop-group. If ``NAME`` is omitted, all + nexthop groups are shown. + .. _pbr-maps: PBR Maps ======== -PBR maps are a way to group policies that we would like to apply -to individual interfaces. These policies when applied are matched -against incoming packets. If matched the nexthop-group or nexthop -is used to forward the packets to the end destination +PBR maps are a way to group policies that we would like to apply to individual +interfaces. These policies when applied are matched against incoming packets. +If matched the nexthop-group or nexthop is used to forward the packets to the +end destination. -.. index:: pbr-map .. clicmd:: pbr-map NAME seq (1-700) Create a pbr-map with NAME and sequence number specified. This command puts you into a new submode for pbr-map specification. To exit this mode type exit or end as per normal conventions for leaving a sub-mode. -.. index:: match .. clicmd:: match src-ip PREFIX When a incoming packet matches the source prefix specified, take the packet diff --git a/doc/user/zebra.rst b/doc/user/zebra.rst index b6060f0737..180d2d7efd 100644 --- a/doc/user/zebra.rst +++ b/doc/user/zebra.rst @@ -46,7 +46,7 @@ Besides the common invocation options (:ref:`common-invocation-options`), the ZEBRA will create an associated VRF. The other daemons will operate on the VRF VRF defined by *Zebra*, as usual. - .. seealso:: :ref:`vrf` + .. seealso:: :ref:`zebra-vrf` .. option:: --v6-rr-semantics @@ -396,7 +396,7 @@ default) should the specified gateways not be reachable. E.g.: After setting TABLENO with this command, static routes defined after this are added to the specified table. -.. _vrf: +.. _zebra-vrf: Virtual Routing and Forwarding ============================== diff --git a/eigrpd/eigrp_fsm.c b/eigrpd/eigrp_fsm.c index 4107d44090..eeefc51968 100644 --- a/eigrpd/eigrp_fsm.c +++ b/eigrpd/eigrp_fsm.c @@ -486,6 +486,7 @@ int eigrp_fsm_event_q_fcn(struct eigrp_fsm_action_message *msg) int eigrp_fsm_event_keep_state(struct eigrp_fsm_action_message *msg) { + struct eigrp *eigrp; struct eigrp_prefix_entry *prefix = msg->prefix; struct eigrp_nexthop_entry *ne = listnode_head(prefix->entries); @@ -498,9 +499,10 @@ int eigrp_fsm_event_keep_state(struct eigrp_fsm_action_message *msg) if (msg->packet_type == EIGRP_OPC_QUERY) eigrp_send_reply(msg->adv_router, prefix); prefix->req_action |= EIGRP_FSM_NEED_UPDATE; - listnode_add( - (eigrp_lookup())->topology_changes_internalIPV4, - prefix); + eigrp = eigrp_lookup(); + assert(eigrp); + listnode_add(eigrp->topology_changes_internalIPV4, + prefix); } eigrp_topology_update_node_flags(prefix); eigrp_update_routing_table(prefix); diff --git a/eigrpd/eigrp_hello.c b/eigrpd/eigrp_hello.c index d9e89357ca..2e55d57c31 100644 --- a/eigrpd/eigrp_hello.c +++ b/eigrpd/eigrp_hello.c @@ -417,7 +417,8 @@ void eigrp_sw_version_initialize(void) if (dash) dash[0] = '\0'; - ret = sscanf(ver_string, "%d.%d", &FRR_MAJOR, &FRR_MINOR); + ret = sscanf(ver_string, "%" SCNu32 ".%" SCNu32, &FRR_MAJOR, + &FRR_MINOR); if (ret != 2) zlog_err("Did not Properly parse %s, please fix VERSION string", VERSION); diff --git a/eigrpd/eigrp_interface.c b/eigrpd/eigrp_interface.c index cd459fdc42..cd62811fdf 100644 --- a/eigrpd/eigrp_interface.c +++ b/eigrpd/eigrp_interface.c @@ -336,6 +336,9 @@ void eigrp_if_free(struct eigrp_interface *ei, int source) struct eigrp_prefix_entry *pe; struct eigrp *eigrp = eigrp_lookup(); + if (!eigrp) + return; + if (source == INTERFACE_DOWN_BY_VTY) { THREAD_OFF(ei->t_hello); eigrp_hello_send(ei, EIGRP_HELLO_GRACEFUL_SHUTDOWN, NULL); diff --git a/eigrpd/eigrp_packet.c b/eigrpd/eigrp_packet.c index fab21e5201..027f30563f 100644 --- a/eigrpd/eigrp_packet.c +++ b/eigrpd/eigrp_packet.c @@ -566,7 +566,7 @@ int eigrp_read(struct thread *thread) // return -1; /* If incoming interface is passive one, ignore it. */ - if (ei && eigrp_if_is_passive(ei)) { + if (eigrp_if_is_passive(ei)) { char buf[3][INET_ADDRSTRLEN]; if (IS_DEBUG_EIGRP_TRANSMIT(0, RECV)) @@ -725,12 +725,12 @@ static struct stream *eigrp_recv_packet(int fd, struct interface **ifp, zlog_warn("stream_recvmsg failed: %s", safe_strerror(errno)); return NULL; } - if ((unsigned int)ret < sizeof(iph)) /* ret must be > 0 now */ + if ((unsigned int)ret < sizeof(*iph)) /* ret must be > 0 now */ { zlog_warn( "eigrp_recv_packet: discarding runt packet of length %d " "(ip header size is %u)", - ret, (unsigned int)sizeof(iph)); + ret, (unsigned int)sizeof(*iph)); return NULL; } diff --git a/eigrpd/eigrp_topology.c b/eigrpd/eigrp_topology.c index becb29a95f..8ca0e282a8 100644 --- a/eigrpd/eigrp_topology.c +++ b/eigrpd/eigrp_topology.c @@ -182,6 +182,9 @@ void eigrp_prefix_entry_delete(struct route_table *table, struct eigrp *eigrp = eigrp_lookup(); struct route_node *rn; + if (!eigrp) + return; + rn = route_node_lookup(table, pe->destination); if (!rn) return; @@ -426,6 +429,9 @@ void eigrp_topology_update_all_node_flags(struct eigrp *eigrp) struct eigrp_prefix_entry *pe; struct route_node *rn; + if (!eigrp) + return; + for (rn = route_top(eigrp->topology_table); rn; rn = route_next(rn)) { pe = rn->info; @@ -442,6 +448,8 @@ void eigrp_topology_update_node_flags(struct eigrp_prefix_entry *dest) struct eigrp_nexthop_entry *entry; struct eigrp *eigrp = eigrp_lookup(); + assert(eigrp); + for (ALL_LIST_ELEMENTS_RO(dest->entries, node, entry)) { if (entry->reported_distance < dest->fdistance) { // is feasible successor, can be successor @@ -471,11 +479,15 @@ void eigrp_topology_update_node_flags(struct eigrp_prefix_entry *dest) void eigrp_update_routing_table(struct eigrp_prefix_entry *prefix) { struct eigrp *eigrp = eigrp_lookup(); - struct list *successors = - eigrp_topology_get_successor_max(prefix, eigrp->max_paths); + struct list *successors; struct listnode *node; struct eigrp_nexthop_entry *entry; + if (!eigrp) + return; + + successors = eigrp_topology_get_successor_max(prefix, eigrp->max_paths); + if (successors) { eigrp_zebra_route_add(prefix->destination, successors); for (ALL_LIST_ELEMENTS_RO(successors, node, entry)) diff --git a/include/linux/netlink.h b/include/linux/netlink.h new file mode 100644 index 0000000000..0b2c29bd08 --- /dev/null +++ b/include/linux/netlink.h @@ -0,0 +1,247 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_NETLINK_H +#define __LINUX_NETLINK_H + +#include <linux/kernel.h> +#include <linux/socket.h> /* for __kernel_sa_family_t */ +#include <linux/types.h> + +#define NETLINK_ROUTE 0 /* Routing/device hook */ +#define NETLINK_UNUSED 1 /* Unused number */ +#define NETLINK_USERSOCK 2 /* Reserved for user mode socket protocols */ +#define NETLINK_FIREWALL 3 /* Unused number, formerly ip_queue */ +#define NETLINK_SOCK_DIAG 4 /* socket monitoring */ +#define NETLINK_NFLOG 5 /* netfilter/iptables ULOG */ +#define NETLINK_XFRM 6 /* ipsec */ +#define NETLINK_SELINUX 7 /* SELinux event notifications */ +#define NETLINK_ISCSI 8 /* Open-iSCSI */ +#define NETLINK_AUDIT 9 /* auditing */ +#define NETLINK_FIB_LOOKUP 10 +#define NETLINK_CONNECTOR 11 +#define NETLINK_NETFILTER 12 /* netfilter subsystem */ +#define NETLINK_IP6_FW 13 +#define NETLINK_DNRTMSG 14 /* DECnet routing messages */ +#define NETLINK_KOBJECT_UEVENT 15 /* Kernel messages to userspace */ +#define NETLINK_GENERIC 16 +/* leave room for NETLINK_DM (DM Events) */ +#define NETLINK_SCSITRANSPORT 18 /* SCSI Transports */ +#define NETLINK_ECRYPTFS 19 +#define NETLINK_RDMA 20 +#define NETLINK_CRYPTO 21 /* Crypto layer */ +#define NETLINK_SMC 22 /* SMC monitoring */ + +#define NETLINK_INET_DIAG NETLINK_SOCK_DIAG + +#define MAX_LINKS 32 + +struct sockaddr_nl { + __kernel_sa_family_t nl_family; /* AF_NETLINK */ + unsigned short nl_pad; /* zero */ + __u32 nl_pid; /* port ID */ + __u32 nl_groups; /* multicast groups mask */ +}; + +struct nlmsghdr { + __u32 nlmsg_len; /* Length of message including header */ + __u16 nlmsg_type; /* Message content */ + __u16 nlmsg_flags; /* Additional flags */ + __u32 nlmsg_seq; /* Sequence number */ + __u32 nlmsg_pid; /* Sending process port ID */ +}; + +/* Flags values */ + +#define NLM_F_REQUEST 0x01 /* It is request message. */ +#define NLM_F_MULTI 0x02 /* Multipart message, terminated by NLMSG_DONE */ +#define NLM_F_ACK 0x04 /* Reply with ack, with zero or error code */ +#define NLM_F_ECHO 0x08 /* Echo this request */ +#define NLM_F_DUMP_INTR 0x10 /* Dump was inconsistent due to sequence change */ +#define NLM_F_DUMP_FILTERED 0x20 /* Dump was filtered as requested */ + +/* Modifiers to GET request */ +#define NLM_F_ROOT 0x100 /* specify tree root */ +#define NLM_F_MATCH 0x200 /* return all matching */ +#define NLM_F_ATOMIC 0x400 /* atomic GET */ +#define NLM_F_DUMP (NLM_F_ROOT|NLM_F_MATCH) + +/* Modifiers to NEW request */ +#define NLM_F_REPLACE 0x100 /* Override existing */ +#define NLM_F_EXCL 0x200 /* Do not touch, if it exists */ +#define NLM_F_CREATE 0x400 /* Create, if it does not exist */ +#define NLM_F_APPEND 0x800 /* Add to end of list */ + +/* Modifiers to DELETE request */ +#define NLM_F_NONREC 0x100 /* Do not delete recursively */ + +/* Flags for ACK message */ +#define NLM_F_CAPPED 0x100 /* request was capped */ +#define NLM_F_ACK_TLVS 0x200 /* extended ACK TVLs were included */ + +/* + 4.4BSD ADD NLM_F_CREATE|NLM_F_EXCL + 4.4BSD CHANGE NLM_F_REPLACE + + True CHANGE NLM_F_CREATE|NLM_F_REPLACE + Append NLM_F_CREATE + Check NLM_F_EXCL + */ + +#define NLMSG_ALIGNTO 4U +#define NLMSG_ALIGN(len) ( ((len)+NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1) ) +#define NLMSG_HDRLEN ((int) NLMSG_ALIGN(sizeof(struct nlmsghdr))) +#define NLMSG_LENGTH(len) ((len) + NLMSG_HDRLEN) +#define NLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len)) +#define NLMSG_DATA(nlh) ((void*)(((char*)nlh) + NLMSG_LENGTH(0))) +#define NLMSG_NEXT(nlh,len) ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), \ + (struct nlmsghdr*)(((char*)(nlh)) + NLMSG_ALIGN((nlh)->nlmsg_len))) +#define NLMSG_OK(nlh,len) ((len) >= (int)sizeof(struct nlmsghdr) && \ + (nlh)->nlmsg_len >= sizeof(struct nlmsghdr) && \ + (nlh)->nlmsg_len <= (len)) +#define NLMSG_PAYLOAD(nlh,len) ((nlh)->nlmsg_len - NLMSG_SPACE((len))) + +#define NLMSG_NOOP 0x1 /* Nothing. */ +#define NLMSG_ERROR 0x2 /* Error */ +#define NLMSG_DONE 0x3 /* End of a dump */ +#define NLMSG_OVERRUN 0x4 /* Data lost */ + +#define NLMSG_MIN_TYPE 0x10 /* < 0x10: reserved control messages */ + +struct nlmsgerr { + int error; + struct nlmsghdr msg; + /* + * followed by the message contents unless NETLINK_CAP_ACK was set + * or the ACK indicates success (error == 0) + * message length is aligned with NLMSG_ALIGN() + */ + /* + * followed by TLVs defined in enum nlmsgerr_attrs + * if NETLINK_EXT_ACK was set + */ +}; + +/** + * enum nlmsgerr_attrs - nlmsgerr attributes + * @NLMSGERR_ATTR_UNUSED: unused + * @NLMSGERR_ATTR_MSG: error message string (string) + * @NLMSGERR_ATTR_OFFS: offset of the invalid attribute in the original + * message, counting from the beginning of the header (u32) + * @NLMSGERR_ATTR_COOKIE: arbitrary subsystem specific cookie to + * be used - in the success case - to identify a created + * object or operation or similar (binary) + * @__NLMSGERR_ATTR_MAX: number of attributes + * @NLMSGERR_ATTR_MAX: highest attribute number + */ +enum nlmsgerr_attrs { + NLMSGERR_ATTR_UNUSED, + NLMSGERR_ATTR_MSG, + NLMSGERR_ATTR_OFFS, + NLMSGERR_ATTR_COOKIE, + + __NLMSGERR_ATTR_MAX, + NLMSGERR_ATTR_MAX = __NLMSGERR_ATTR_MAX - 1 +}; + +#define NETLINK_ADD_MEMBERSHIP 1 +#define NETLINK_DROP_MEMBERSHIP 2 +#define NETLINK_PKTINFO 3 +#define NETLINK_BROADCAST_ERROR 4 +#define NETLINK_NO_ENOBUFS 5 +#define NETLINK_RX_RING 6 +#define NETLINK_TX_RING 7 +#define NETLINK_LISTEN_ALL_NSID 8 +#define NETLINK_LIST_MEMBERSHIPS 9 +#define NETLINK_CAP_ACK 10 +#define NETLINK_EXT_ACK 11 + +struct nl_pktinfo { + __u32 group; +}; + +struct nl_mmap_req { + unsigned int nm_block_size; + unsigned int nm_block_nr; + unsigned int nm_frame_size; + unsigned int nm_frame_nr; +}; + +struct nl_mmap_hdr { + unsigned int nm_status; + unsigned int nm_len; + __u32 nm_group; + /* credentials */ + __u32 nm_pid; + __u32 nm_uid; + __u32 nm_gid; +}; + +enum nl_mmap_status { + NL_MMAP_STATUS_UNUSED, + NL_MMAP_STATUS_RESERVED, + NL_MMAP_STATUS_VALID, + NL_MMAP_STATUS_COPY, + NL_MMAP_STATUS_SKIP, +}; + +#define NL_MMAP_MSG_ALIGNMENT NLMSG_ALIGNTO +#define NL_MMAP_MSG_ALIGN(sz) __ALIGN_KERNEL(sz, NL_MMAP_MSG_ALIGNMENT) +#define NL_MMAP_HDRLEN NL_MMAP_MSG_ALIGN(sizeof(struct nl_mmap_hdr)) + +#define NET_MAJOR 36 /* Major 36 is reserved for networking */ + +enum { + NETLINK_UNCONNECTED = 0, + NETLINK_CONNECTED, +}; + +/* + * <------- NLA_HDRLEN ------> <-- NLA_ALIGN(payload)--> + * +---------------------+- - -+- - - - - - - - - -+- - -+ + * | Header | Pad | Payload | Pad | + * | (struct nlattr) | ing | | ing | + * +---------------------+- - -+- - - - - - - - - -+- - -+ + * <-------------- nlattr->nla_len --------------> + */ + +struct nlattr { + __u16 nla_len; + __u16 nla_type; +}; + +/* + * nla_type (16 bits) + * +---+---+-------------------------------+ + * | N | O | Attribute Type | + * +---+---+-------------------------------+ + * N := Carries nested attributes + * O := Payload stored in network byte order + * + * Note: The N and O flag are mutually exclusive. + */ +#define NLA_F_NESTED (1 << 15) +#define NLA_F_NET_BYTEORDER (1 << 14) +#define NLA_TYPE_MASK ~(NLA_F_NESTED | NLA_F_NET_BYTEORDER) + +#define NLA_ALIGNTO 4 +#define NLA_ALIGN(len) (((len) + NLA_ALIGNTO - 1) & ~(NLA_ALIGNTO - 1)) +#define NLA_HDRLEN ((int) NLA_ALIGN(sizeof(struct nlattr))) + +/* Generic 32 bitflags attribute content sent to the kernel. + * + * The value is a bitmap that defines the values being set + * The selector is a bitmask that defines which value is legit + * + * Examples: + * value = 0x0, and selector = 0x1 + * implies we are selecting bit 1 and we want to set its value to 0. + * + * value = 0x2, and selector = 0x2 + * implies we are selecting bit 2 and we want to set its value to 1. + * + */ +struct nla_bitfield32 { + __u32 value; + __u32 selector; +}; + +#endif /* __LINUX_NETLINK_H */ diff --git a/include/subdir.am b/include/subdir.am index db5ed06c61..731785d4b4 100644 --- a/include/subdir.am +++ b/include/subdir.am @@ -4,6 +4,7 @@ noinst_HEADERS += \ include/linux/lwtunnel.h \ include/linux/mpls_iptunnel.h \ include/linux/neighbour.h \ + include/linux/netlink.h \ include/linux/rtnetlink.h \ include/linux/socket.h \ include/linux/net_namespace.h \ diff --git a/isisd/isis_lsp.c b/isisd/isis_lsp.c index bba86d4c1f..c8ef829064 100644 --- a/isisd/isis_lsp.c +++ b/isisd/isis_lsp.c @@ -622,7 +622,7 @@ static const char *lsp_bits2string(uint8_t lsp_bits, char *buf, size_t buf_size) pos += sprintf(pos, "%d/", ISIS_MASK_LSP_PARTITION_BIT(lsp_bits) ? 1 : 0); - pos += sprintf(pos, "%d", ISIS_MASK_LSP_OL_BIT(lsp_bits) ? 1 : 0); + sprintf(pos, "%d", ISIS_MASK_LSP_OL_BIT(lsp_bits) ? 1 : 0); return buf; } @@ -1052,6 +1052,7 @@ static void lsp_build(struct isis_lsp *lsp, struct isis_area *area) uint8_t subtlv_len; if (IS_MPLS_TE(isisMplsTE) + && circuit->interface != NULL && HAS_LINK_PARAMS( circuit->interface)) /* Update Local and Remote IP diff --git a/isisd/isis_pfpacket.c b/isisd/isis_pfpacket.c index 6e56870ebd..fd82b85f51 100644 --- a/isisd/isis_pfpacket.c +++ b/isisd/isis_pfpacket.c @@ -309,9 +309,9 @@ int isis_recv_pdu_p2p(struct isis_circuit *circuit, uint8_t *ssnpa) addr_len = sizeof(s_addr); /* we can read directly to the stream */ - stream_recvfrom(circuit->rcv_stream, circuit->fd, - circuit->interface->mtu, 0, (struct sockaddr *)&s_addr, - (socklen_t *)&addr_len); + (void)stream_recvfrom( + circuit->rcv_stream, circuit->fd, circuit->interface->mtu, 0, + (struct sockaddr *)&s_addr, (socklen_t *)&addr_len); if (s_addr.sll_pkttype == PACKET_OUTGOING) { /* Read the packet into discard buff */ diff --git a/isisd/isis_spf.c b/isisd/isis_spf.c index a55a0e1902..556f2890cf 100644 --- a/isisd/isis_spf.c +++ b/isisd/isis_spf.c @@ -77,14 +77,13 @@ enum vertextype { /* * Triple <N, d(N), {Adj(N)}> */ +union isis_N { + uint8_t id[ISIS_SYS_ID_LEN + 1]; + struct prefix prefix; +}; struct isis_vertex { enum vertextype type; - - union { - uint8_t id[ISIS_SYS_ID_LEN + 1]; - struct prefix prefix; - } N; - + union isis_N N; uint32_t d_N; /* d(N) Distance from this IS */ uint16_t depth; /* The depth in the imaginary tree */ struct list *Adj_N; /* {Adj(N)} next hop or neighbor list */ @@ -407,28 +406,28 @@ static const char *vid2string(struct isis_vertex *vertex, char *buff, int size) return "UNKNOWN"; } -static void isis_vertex_id_init(struct isis_vertex *vertex, void *id, +static void isis_vertex_id_init(struct isis_vertex *vertex, union isis_N *n, enum vertextype vtype) { vertex->type = vtype; if (VTYPE_IS(vtype) || VTYPE_ES(vtype)) { - memcpy(vertex->N.id, (uint8_t *)id, ISIS_SYS_ID_LEN + 1); + memcpy(vertex->N.id, n->id, ISIS_SYS_ID_LEN + 1); } else if (VTYPE_IP(vtype)) { - memcpy(&vertex->N.prefix, (struct prefix *)id, - sizeof(struct prefix)); + memcpy(&vertex->N.prefix, &n->prefix, sizeof(struct prefix)); } else { zlog_err("WTF!"); } } -static struct isis_vertex *isis_vertex_new(void *id, enum vertextype vtype) +static struct isis_vertex *isis_vertex_new(union isis_N *n, + enum vertextype vtype) { struct isis_vertex *vertex; vertex = XCALLOC(MTYPE_ISIS_VERTEX, sizeof(struct isis_vertex)); - isis_vertex_id_init(vertex, id, vtype); + isis_vertex_id_init(vertex, n, vtype); vertex->Adj_N = list_new(); vertex->parents = list_new(); @@ -598,17 +597,17 @@ static struct isis_vertex *isis_spf_add_root(struct isis_spftree *spftree, #ifdef EXTREME_DEBUG char buff[PREFIX2STR_BUFFER]; #endif /* EXTREME_DEBUG */ - uint8_t id[ISIS_SYS_ID_LEN + 1]; + union isis_N n; - memcpy(id, sysid, ISIS_SYS_ID_LEN); - LSP_PSEUDO_ID(id) = 0; + memcpy(n.id, sysid, ISIS_SYS_ID_LEN); + LSP_PSEUDO_ID(n.id) = 0; lsp = isis_root_system_lsp(spftree->area, spftree->level, sysid); if (lsp == NULL) zlog_warn("ISIS-Spf: could not find own l%d LSP!", spftree->level); - vertex = isis_vertex_new(id, + vertex = isis_vertex_new(&n, spftree->area->oldmetric ? VTYPE_NONPSEUDO_IS : VTYPE_NONPSEUDO_TE_IS); @@ -625,11 +624,12 @@ static struct isis_vertex *isis_spf_add_root(struct isis_spftree *spftree, } static struct isis_vertex *isis_find_vertex(struct isis_vertex_queue *queue, - void *id, enum vertextype vtype) + union isis_N *n, + enum vertextype vtype) { struct isis_vertex querier; - isis_vertex_id_init(&querier, id, vtype); + isis_vertex_id_init(&querier, n, vtype); return hash_lookup(queue->hash, &querier); } @@ -1212,7 +1212,7 @@ static void add_to_paths(struct isis_spftree *spftree, { char buff[PREFIX2STR_BUFFER]; - if (isis_find_vertex(&spftree->paths, vertex->N.id, vertex->type)) + if (isis_find_vertex(&spftree->paths, &vertex->N, vertex->type)) return; isis_vertex_queue_append(&spftree->paths, vertex); @@ -1473,8 +1473,9 @@ static void isis_print_paths(struct vty *vty, struct isis_vertex_queue *queue, vty_out(vty, "%-20s %-12s %-6u ", vid2string(vertex, buff, sizeof(buff)), vtype2string(vertex->type), vertex->d_N); - for (unsigned int i = 0; i < MAX(listcount(vertex->Adj_N), - listcount(vertex->parents)); + for (unsigned int i = 0; + i < MAX(vertex->Adj_N ? listcount(vertex->Adj_N) : 0, + vertex->parents ? listcount(vertex->parents) : 0); i++) { if (anode) { adj = listgetdata(anode); diff --git a/isisd/isis_te.c b/isisd/isis_te.c index 6834f52a82..8e53df3b61 100644 --- a/isisd/isis_te.c +++ b/isisd/isis_te.c @@ -884,7 +884,7 @@ static uint8_t print_subtlv_use_bw(struct sbuf *buf, int indent, static uint8_t print_unknown_tlv(struct sbuf *buf, int indent, struct subtlv_header *tlvh) { - int i, rtn = 1; + int i, rtn; uint8_t *v = (uint8_t *)tlvh; if (tlvh->length != 0) { diff --git a/isisd/isisd.c b/isisd/isisd.c index 6f04d72082..cecaa0693d 100644 --- a/isisd/isisd.c +++ b/isisd/isisd.c @@ -1373,7 +1373,7 @@ static int show_isis_database(struct vty *vty, const char *argv, int ui_level) struct isis_area *area; struct isis_lsp *lsp; struct isis_dynhn *dynhn; - const char *pos = argv; + const char *pos; uint8_t lspid[ISIS_SYS_ID_LEN + 2]; char sysid[255]; uint8_t number[3]; diff --git a/ldpd/ldp_debug.c b/ldpd/ldp_debug.c index 39e20ef7c8..ec70ef510a 100644 --- a/ldpd/ldp_debug.c +++ b/ldpd/ldp_debug.c @@ -41,6 +41,9 @@ int ldp_vty_debug(struct vty *vty, const char *negate, const char *type_str, const char *dir_str, const char *all) { + if (type_str == NULL) + return (CMD_WARNING_CONFIG_FAILED); + if (strcmp(type_str, "discovery") == 0) { if (dir_str == NULL) return (CMD_WARNING_CONFIG_FAILED); diff --git a/ldpd/ldp_vty_conf.c b/ldpd/ldp_vty_conf.c index 382b006884..4ef57f574a 100644 --- a/ldpd/ldp_vty_conf.c +++ b/ldpd/ldp_vty_conf.c @@ -89,6 +89,9 @@ struct cmd_node ldp_pseudowire_node = int ldp_get_address(const char *str, int *af, union ldpd_addr *addr) { + if (!str || !af || !addr) + return (-1); + memset(addr, 0, sizeof(*addr)); if (inet_pton(AF_INET, str, &addr->v4) == 1) { @@ -428,6 +431,9 @@ ldp_vty_address_family(struct vty *vty, const char *negate, const char *af_str) struct ldpd_af_conf *af_conf; int af; + if (af_str == NULL) + return (CMD_WARNING_CONFIG_FAILED); + if (strcmp(af_str, "ipv4") == 0) { af = AF_INET; af_conf = &vty_conf->ipv4; @@ -709,6 +715,11 @@ ldp_vty_interface(struct vty *vty, const char *negate, const char *ifname) struct iface *iface; struct iface_af *ia; + if (ifname == NULL) { + vty_out (vty, "%% Missing IF name\n"); + return (CMD_WARNING_CONFIG_FAILED); + } + af = ldp_vty_get_af(vty); iface = if_lookup_name(vty_conf, ifname); @@ -776,8 +787,9 @@ ldp_vty_trans_addr(struct vty *vty, const char *negate, const char *addr_str) if (negate) memset(&af_conf->trans_addr, 0, sizeof(af_conf->trans_addr)); else { - if (inet_pton(af, addr_str, &af_conf->trans_addr) != 1 || - bad_addr(af, &af_conf->trans_addr)) { + if (addr_str == NULL + || inet_pton(af, addr_str, &af_conf->trans_addr) != 1 + || bad_addr(af, &af_conf->trans_addr)) { vty_out (vty, "%% Malformed address\n"); return (CMD_SUCCESS); } @@ -797,7 +809,7 @@ ldp_vty_neighbor_targeted(struct vty *vty, const char *negate, const char *addr_ af = ldp_vty_get_af(vty); - if (inet_pton(af, addr_str, &addr) != 1 || + if (addr_str == NULL || inet_pton(af, addr_str, &addr) != 1 || bad_addr(af, &addr)) { vty_out (vty, "%% Malformed address\n"); return (CMD_WARNING_CONFIG_FAILED); @@ -1018,6 +1030,11 @@ ldp_vty_neighbor_password(struct vty *vty, const char *negate, struct in_addr ls size_t password_len; struct nbr_params *nbrp; + if (password_str == NULL) { + vty_out (vty, "%% Missing password\n"); + return (CMD_WARNING_CONFIG_FAILED); + } + if (bad_addr_v4(lsr_id)) { vty_out (vty, "%% Malformed address\n"); return (CMD_WARNING_CONFIG_FAILED); @@ -1113,6 +1130,11 @@ ldp_vty_l2vpn(struct vty *vty, const char *negate, const char *name_str) struct l2vpn_if *lif; struct l2vpn_pw *pw; + if (name_str == NULL) { + vty_out (vty, "%% Missing name\n"); + return (CMD_WARNING_CONFIG_FAILED); + } + l2vpn = l2vpn_find(vty_conf, name_str); if (negate) { @@ -1158,8 +1180,13 @@ ldp_vty_l2vpn_bridge(struct vty *vty, const char *negate, const char *ifname) if (negate) memset(l2vpn->br_ifname, 0, sizeof(l2vpn->br_ifname)); - else + else { + if (ifname == NULL) { + vty_out (vty, "%% Missing IF name\n"); + return (CMD_WARNING_CONFIG_FAILED); + } strlcpy(l2vpn->br_ifname, ifname, sizeof(l2vpn->br_ifname)); + } ldp_config_apply(vty, vty_conf); @@ -1187,6 +1214,11 @@ ldp_vty_l2vpn_pwtype(struct vty *vty, const char *negate, const char *type_str) VTY_DECLVAR_CONTEXT(l2vpn, l2vpn); int pw_type; + if (type_str == NULL) { + vty_out (vty, "%% Missing type\n"); + return (CMD_WARNING_CONFIG_FAILED); + } + if (strcmp(type_str, "ethernet") == 0) pw_type = PW_TYPE_ETHERNET; else @@ -1208,6 +1240,11 @@ ldp_vty_l2vpn_interface(struct vty *vty, const char *negate, const char *ifname) VTY_DECLVAR_CONTEXT(l2vpn, l2vpn); struct l2vpn_if *lif; + if (ifname == NULL) { + vty_out (vty, "%% Missing IF name\n"); + return (CMD_WARNING_CONFIG_FAILED); + } + lif = l2vpn_if_find(l2vpn, ifname); if (negate) { @@ -1246,6 +1283,11 @@ ldp_vty_l2vpn_pseudowire(struct vty *vty, const char *negate, const char *ifname VTY_DECLVAR_CONTEXT(l2vpn, l2vpn); struct l2vpn_pw *pw; + if (ifname == NULL) { + vty_out (vty, "%% Missing IF name\n"); + return (CMD_WARNING_CONFIG_FAILED); + } + pw = l2vpn_pw_find(l2vpn, ifname); if (negate) { @@ -1294,6 +1336,10 @@ ldp_vty_l2vpn_pw_cword(struct vty *vty, const char *negate, const char *preferen if (negate) pw->flags |= F_PW_CWORD_CONF; else { + if (!preference_str) { + vty_out (vty, "%% Missing preference\n"); + return (CMD_WARNING_CONFIG_FAILED); + } if (preference_str[0] == 'e') pw->flags &= ~F_PW_CWORD_CONF; else diff --git a/ldpd/ldpd.c b/ldpd/ldpd.c index b265c98dae..b51ff82cea 100644 --- a/ldpd/ldpd.c +++ b/ldpd/ldpd.c @@ -187,6 +187,22 @@ FRR_DAEMON_INFO(ldpd, LDP, .privs = &ldpd_privs, ) +static int ldp_config_fork_apply(struct thread *t) +{ + /* + * So the frr_config_fork() function schedules + * the read of the vty config( if there is a + * non-integrated config ) to be after the + * end of startup and we are starting the + * main process loop. We need to schedule + * the application of this if necessary + * after the read in of the config. + */ + ldp_config_apply(NULL, vty_conf); + + return 0; +} + int main(int argc, char *argv[]) { @@ -195,6 +211,7 @@ main(int argc, char *argv[]) int pipe_parent2ldpe[2], pipe_parent2ldpe_sync[2]; int pipe_parent2lde[2], pipe_parent2lde_sync[2]; char *ctl_sock_name; + struct thread *thread = NULL; ldpd_process = PROC_MAIN; log_procname = log_procnames[ldpd_process]; @@ -331,7 +348,7 @@ main(int argc, char *argv[]) frr_config_fork(); /* apply configuration */ - ldp_config_apply(NULL, vty_conf); + thread_add_event(master, ldp_config_fork_apply, NULL, 0, &thread); /* setup pipes to children */ if ((iev_ldpe = calloc(1, sizeof(struct imsgev))) == NULL || diff --git a/lib/clippy.c b/lib/clippy.c index bcec6c2cca..44dcc02eb8 100644 --- a/lib/clippy.c +++ b/lib/clippy.c @@ -31,9 +31,11 @@ #define pychar wchar_t static wchar_t *wconv(const char *s) { - size_t outlen = mbstowcs(NULL, s, 0); + size_t outlen = s ? mbstowcs(NULL, s, 0) : 0; wchar_t *out = malloc((outlen + 1) * sizeof(wchar_t)); - mbstowcs(out, s, outlen + 1); + + if (outlen > 0) + mbstowcs(out, s, outlen); out[outlen] = 0; return out; } diff --git a/lib/command.c b/lib/command.c index a8e61c6bb4..0bf856f248 100644 --- a/lib/command.c +++ b/lib/command.c @@ -261,8 +261,11 @@ void print_version(const char *progname) char *argv_concat(struct cmd_token **argv, int argc, int shift) { - int cnt = argc - shift; - const char *argstr[cnt]; + int cnt = MAX(argc - shift, 0); + const char *argstr[cnt + 1]; + + if (!cnt) + return NULL; for (int i = 0; i < cnt; i++) argstr[i] = argv[i + shift]->arg; @@ -515,13 +518,6 @@ static int config_write_host(struct vty *vty) host.enable); } - if (zlog_default->default_lvl != LOG_DEBUG) { - vty_out(vty, - "! N.B. The 'log trap' command is deprecated.\n"); - vty_out(vty, "log trap %s\n", - zlog_priority[zlog_default->default_lvl]); - } - if (host.logfile && (zlog_default->maxlvl[ZLOG_DEST_FILE] != ZLOG_DISABLED)) { @@ -2429,7 +2425,8 @@ static int set_log_file(struct vty *vty, const char *fname, int loglevel) XFREE(MTYPE_TMP, p); if (!ret) { - vty_out(vty, "can't open logfile %s\n", fname); + if (vty) + vty_out(vty, "can't open logfile %s\n", fname); return CMD_WARNING_CONFIG_FAILED; } @@ -2445,6 +2442,39 @@ static int set_log_file(struct vty *vty, const char *fname, int loglevel) return CMD_SUCCESS; } +void command_setup_early_logging(const char *dest, const char *level) +{ + char *token; + + if (level) { + int nlevel = level_match(level); + + if (nlevel != ZLOG_DISABLED) + zlog_default->default_lvl = nlevel; + } + + if (!dest) + return; + + if (strcmp(dest, "stdout") == 0) { + zlog_set_level(ZLOG_DEST_STDOUT, zlog_default->default_lvl); + return; + } + + if (strcmp(dest, "syslog") == 0) { + zlog_set_level(ZLOG_DEST_SYSLOG, zlog_default->default_lvl); + return; + } + + token = strstr(dest, ":"); + if (token == NULL) + return; + + token++; + + set_log_file(NULL, token, zlog_default->default_lvl); +} + DEFUN (config_log_file, config_log_file_cmd, "log file FILENAME [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]", @@ -2552,36 +2582,6 @@ DEFUN (no_config_log_facility, return CMD_SUCCESS; } -DEFUN_DEPRECATED( - config_log_trap, config_log_trap_cmd, - "log trap <emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>", - "Logging control\n" - "(Deprecated) Set logging level and default for all destinations\n" LOG_LEVEL_DESC) -{ - int new_level; - int i; - - if ((new_level = level_match(argv[2]->arg)) == ZLOG_DISABLED) - return CMD_ERR_NO_MATCH; - - zlog_default->default_lvl = new_level; - for (i = 0; i < ZLOG_NUM_DESTS; i++) - if (zlog_default->maxlvl[i] != ZLOG_DISABLED) - zlog_default->maxlvl[i] = new_level; - return CMD_SUCCESS; -} - -DEFUN_DEPRECATED( - no_config_log_trap, no_config_log_trap_cmd, - "no log trap [emergencies|alerts|critical|errors|warnings|notifications|informational|debugging]", - NO_STR - "Logging control\n" - "Permit all logging information\n" LOG_LEVEL_DESC) -{ - zlog_default->default_lvl = LOG_DEBUG; - return CMD_SUCCESS; -} - DEFUN (config_log_record_priority, config_log_record_priority_cmd, "log record-priority", @@ -2871,8 +2871,6 @@ void cmd_init(int terminal) install_element(CONFIG_NODE, &no_config_log_syslog_cmd); install_element(CONFIG_NODE, &config_log_facility_cmd); install_element(CONFIG_NODE, &no_config_log_facility_cmd); - install_element(CONFIG_NODE, &config_log_trap_cmd); - install_element(CONFIG_NODE, &no_config_log_trap_cmd); install_element(CONFIG_NODE, &config_log_record_priority_cmd); install_element(CONFIG_NODE, &no_config_log_record_priority_cmd); diff --git a/lib/command.h b/lib/command.h index 9bf482f41b..2d333b098a 100644 --- a/lib/command.h +++ b/lib/command.h @@ -240,9 +240,6 @@ struct cmd_node { #define DEFUN_HIDDEN(funcname, cmdname, cmdstr, helpstr) \ DEFUN_ATTR(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN) -#define DEFUN_DEPRECATED(funcname, cmdname, cmdstr, helpstr) \ - DEFUN_ATTR(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_DEPRECATED) - /* DEFUN_NOSH for commands that vtysh should ignore */ #define DEFUN_NOSH(funcname, cmdname, cmdstr, helpstr) \ DEFUN(funcname, cmdname, cmdstr, helpstr) @@ -479,4 +476,5 @@ extern void cmd_variable_handler_register(const struct cmd_variable_handler *cvh); extern char *cmd_variable_comp2str(vector comps, unsigned short cols); +extern void command_setup_early_logging(const char *dest, const char *level); #endif /* _ZEBRA_COMMAND_H */ diff --git a/lib/command_match.c b/lib/command_match.c index 99ec03e0c2..c165305d78 100644 --- a/lib/command_match.c +++ b/lib/command_match.c @@ -608,11 +608,14 @@ static struct cmd_token *disambiguate_tokens(struct cmd_token *first, static struct list *disambiguate(struct list *first, struct list *second, vector vline, unsigned int n) { + assert(first != NULL); + assert(second != NULL); // doesn't make sense for these to be inequal length assert(first->count == second->count); assert(first->count == vector_active(vline) - n + 1); - struct listnode *fnode = listhead(first), *snode = listhead(second); + struct listnode *fnode = listhead_unchecked(first), + *snode = listhead_unchecked(second); struct cmd_token *ftok = listgetdata(fnode), *stok = listgetdata(snode), *best = NULL; diff --git a/lib/imsg.c b/lib/imsg.c index 6419f805ab..5424140720 100644 --- a/lib/imsg.c +++ b/lib/imsg.c @@ -77,7 +77,7 @@ ssize_t imsg_read(struct imsgbuf *ibuf) char buf[CMSG_SPACE(sizeof(int) * 1)]; } cmsgbuf; struct iovec iov; - ssize_t n = -1; + ssize_t n; int fd; struct imsg_fd *ifd; @@ -110,7 +110,8 @@ again: return (-1); } - if ((n = recvmsg(ibuf->fd, &msg, 0)) == -1) { + n = recvmsg(ibuf->fd, &msg, 0); + if (n == -1) { if (errno == EINTR) goto again; goto fail; diff --git a/lib/libfrr.c b/lib/libfrr.c index 88203fbeb6..9ea5e985cd 100644 --- a/lib/libfrr.c +++ b/lib/libfrr.c @@ -78,6 +78,8 @@ static void opt_extend(const struct optspec *os) #define OPTION_VTYSOCK 1000 #define OPTION_MODULEDIR 1002 +#define OPTION_LOG 1003 +#define OPTION_LOGLEVEL 1004 static const struct option lo_always[] = { {"help", no_argument, NULL, 'h'}, @@ -86,6 +88,8 @@ static const struct option lo_always[] = { {"module", no_argument, NULL, 'M'}, {"vty_socket", required_argument, NULL, OPTION_VTYSOCK}, {"moduledir", required_argument, NULL, OPTION_MODULEDIR}, + {"log", required_argument, NULL, OPTION_LOG}, + {"log-level", required_argument, NULL, OPTION_LOGLEVEL}, {NULL}}; static const struct optspec os_always = { "hvdM:", @@ -94,7 +98,9 @@ static const struct optspec os_always = { " -d, --daemon Runs in daemon mode\n" " -M, --module Load specified module\n" " --vty_socket Override vty socket path\n" - " --moduledir Override modules directory\n", + " --moduledir Override modules directory\n" + " --log Set Logging to stdout, syslog, or file:<name>\n" + " --log-level Set Logging Level to use, debug, info, warn, etc\n", lo_always}; @@ -444,6 +450,12 @@ static int frr_opt(int opt) return 1; di->privs->group = optarg; break; + case OPTION_LOG: + di->early_logging = optarg; + break; + case OPTION_LOGLEVEL: + di->early_loglevel = optarg; + break; default: return 1; } @@ -543,9 +555,8 @@ struct thread_master *frr_init(void) openzlog(di->progname, di->logname, di->instance, LOG_CONS | LOG_NDELAY | LOG_PID, LOG_DAEMON); -#if defined(HAVE_CUMULUS) - zlog_set_level(ZLOG_DEST_SYSLOG, zlog_default->default_lvl); -#endif + + command_setup_early_logging(di->early_logging, di->early_loglevel); if (!frr_zclient_addr(&zclient_addr, &zclient_addr_len, frr_zclientpath)) { @@ -721,15 +732,37 @@ static void frr_daemonize(void) frr_daemon_wait(fds[0]); } +/* + * Why is this a thread? + * + * The read in of config for integrated config happens *after* + * thread execution starts( because it is passed in via a vtysh -b -n ) + * While if you are not using integrated config we want the ability + * to read the config in after thread execution starts, so that + * we can match this behavior. + */ +static int frr_config_read_in(struct thread *t) +{ + if (!vty_read_config(di->config_file, config_default) && + di->backup_config_file) { + zlog_info("Attempting to read backup config file: %s specified", + di->backup_config_file); + vty_read_config(di->backup_config_file, config_default); + } + return 0; +} + void frr_config_fork(void) { hook_call(frr_late_init, master); - vty_read_config(di->config_file, config_default); - /* Don't start execution if we are in dry-run mode */ - if (di->dryrun) + if (di->dryrun) { + frr_config_read_in(NULL); exit(0); + } + + thread_add_event(master, frr_config_read_in, NULL, 0, &di->read_in); if (di->daemon_mode || di->terminal) frr_daemonize(); @@ -813,7 +846,9 @@ static int frr_daemon_ctl(struct thread *t) switch (buf[0]) { case 'S': /* SIGTSTP */ vty_stdio_suspend(); - send(daemon_ctl_sock, "s", 1, 0); + if (send(daemon_ctl_sock, "s", 1, 0) < 0) + zlog_err("%s send(\"s\") error (SIGTSTP propagation)", + (di && di->name ? di->name : "")); break; case 'R': /* SIGTCNT [implicit] */ vty_stdio_resume(); diff --git a/lib/libfrr.h b/lib/libfrr.h index 7ffa780bfb..d255279906 100644 --- a/lib/libfrr.h +++ b/lib/libfrr.h @@ -50,11 +50,16 @@ struct frr_daemon_info { bool dryrun; bool daemon_mode; bool terminal; + + struct thread *read_in; const char *config_file; + const char *backup_config_file; const char *pid_file; const char *vty_path; const char *module_path; const char *pathspace; + const char *early_logging; + const char *early_loglevel; const char *proghelp; void (*printhelp)(FILE *target); diff --git a/lib/linklist.c b/lib/linklist.c index 2cfa3e7482..86649dd495 100644 --- a/lib/linklist.c +++ b/lib/linklist.c @@ -186,26 +186,10 @@ void listnode_move_to_tail(struct list *l, struct listnode *n) void listnode_delete(struct list *list, void *val) { - struct listnode *node; + struct listnode *node = listnode_lookup(list, val); - assert(list); - for (node = list->head; node; node = node->next) { - if (node->data == val) { - if (node->prev) - node->prev->next = node->next; - else - list->head = node->next; - - if (node->next) - node->next->prev = node->prev; - else - list->tail = node->prev; - - list->count--; - listnode_free(node); - return; - } - } + if (node) + list_delete_node(list, node); } void *listnode_head(struct list *list) diff --git a/lib/linklist.h b/lib/linklist.h index 39e70293d2..1e2631ea46 100644 --- a/lib/linklist.h +++ b/lib/linklist.h @@ -52,7 +52,9 @@ struct list { }; #define listnextnode(X) ((X) ? ((X)->next) : NULL) +#define listnextnode_unchecked(X) ((X)->next) #define listhead(X) ((X) ? ((X)->head) : NULL) +#define listhead_unchecked(X) ((X)->head) #define listtail(X) ((X) ? ((X)->tail) : NULL) #define listcount(X) ((X)->count) #define list_isempty(X) ((X)->head == NULL && (X)->tail == NULL) @@ -20,6 +20,12 @@ #ifndef _PBR_H #define _PBR_H +#include <zebra.h> +#include "stream.h" +#include "prefix.h" + +#define PBR_STR "Policy Based Routing\n" + /* * A PBR filter * @@ -83,6 +89,35 @@ struct pbr_rule { uint32_t ifindex; }; +/* TCP flags value shared + * those are values of byte 13 of TCP header + * as mentioned in rfc793 + */ +#define TCP_HEADER_FIN (0x01) +#define TCP_HEADER_SYN (0x02) +#define TCP_HEADER_RST (0x04) +#define TCP_HEADER_PSH (0x08) +#define TCP_HEADER_ACK (0x10) +#define TCP_HEADER_URG (0x20) +#define TCP_HEADER_ALL_FLAGS (TCP_HEADER_FIN | TCP_HEADER_SYN \ + | TCP_HEADER_RST | TCP_HEADER_PSH \ + | TCP_HEADER_ACK | TCP_HEADER_URG) + +/* Pbr IPTable defines + * those are common flags shared between BGP and Zebra + */ +#define MATCH_IP_SRC_SET (1 << 0) +#define MATCH_IP_DST_SET (1 << 1) +#define MATCH_PORT_SRC_SET (1 << 2) +#define MATCH_PORT_DST_SET (1 << 3) +#define MATCH_PORT_SRC_RANGE_SET (1 << 4) +#define MATCH_PORT_DST_RANGE_SET (1 << 5) +#define MATCH_DSCP_SET (1 << 6) +#define MATCH_DSCP_INVERSE_SET (1 << 7) +#define MATCH_PKT_LEN_INVERSE_SET (1 << 8) +#define MATCH_FRAGMENT_INVERSE_SET (1 << 9) +#define MATCH_ICMP_SET (1 << 10) + extern int zapi_pbr_rule_encode(uint8_t cmd, struct stream *s, struct pbr_rule *zrule); diff --git a/lib/plist.c b/lib/plist.c index 5ed1589f45..056b737f54 100644 --- a/lib/plist.c +++ b/lib/plist.c @@ -850,6 +850,11 @@ static int vty_prefix_list_install(struct vty *vty, afi_t afi, const char *name, int lenum = 0; int genum = 0; + if (name == NULL || prefix == NULL || typestr == NULL) { + vty_out(vty, "%% Missing prefix or type\n"); + return CMD_WARNING_CONFIG_FAILED; + } + /* Sequential number. */ if (seq) seqnum = (int64_t)atol(seq); diff --git a/lib/privs.c b/lib/privs.c index cfe7d6d6f8..7c99742d34 100644 --- a/lib/privs.c +++ b/lib/privs.c @@ -824,6 +824,19 @@ void zprivs_init(struct zebra_privs_t *zprivs) #ifdef HAVE_CAPABILITIES zprivs_caps_init(zprivs); + + /* + * If we have initialized the system with no requested + * capabilities, change will not have been set + * to anything by zprivs_caps_init, As such + * we should make sure that when we attempt + * to raize privileges that we actually have + * a do nothing function to call instead of a + * crash :). + */ + if (!zprivs->change) + zprivs->change = zprivs_change_null; + #else /* !HAVE_CAPABILITIES */ /* we dont have caps. we'll need to maintain rid and saved uid * and change euid back to saved uid (who we presume has all neccessary diff --git a/lib/sbuf.c b/lib/sbuf.c index 37c1e5283d..03a2be3e09 100644 --- a/lib/sbuf.c +++ b/lib/sbuf.c @@ -63,13 +63,12 @@ void sbuf_push(struct sbuf *buf, int indent, const char *format, ...) int written; if (!buf->fixed) { - char dummy; int written1, written2; size_t new_size; - written1 = snprintf(&dummy, 0, "%*s", indent, ""); + written1 = indent; va_start(args, format); - written2 = vsnprintf(&dummy, 0, format, args); + written2 = vsnprintf(NULL, 0, format, args); va_end(args); new_size = buf->size; diff --git a/lib/sockopt.c b/lib/sockopt.c index 1d8d9990df..815be86c2e 100644 --- a/lib/sockopt.c +++ b/lib/sockopt.c @@ -457,8 +457,7 @@ int setsockopt_ifindex(int af, int sock, ifindex_t val) */ static ifindex_t getsockopt_ipv4_ifindex(struct msghdr *msgh) { - /* XXX: initialize to zero? (Always overwritten, so just cosmetic.) */ - ifindex_t ifindex = -1; + ifindex_t ifindex; #if defined(IP_PKTINFO) /* Linux pktinfo based ifindex retrieval */ @@ -466,7 +465,11 @@ static ifindex_t getsockopt_ipv4_ifindex(struct msghdr *msgh) pktinfo = (struct in_pktinfo *)getsockopt_cmsg_data(msgh, IPPROTO_IP, IP_PKTINFO); - /* XXX Can pktinfo be NULL? Clean up post 0.98. */ + + /* getsockopt_ifindex() will forward this, being 0 "not found" */ + if (pktinfo == NULL) + return 0; + ifindex = pktinfo->ipi_ifindex; #elif defined(IP_RECVIF) diff --git a/lib/sockunion.c b/lib/sockunion.c index 28a7f647cb..44378b5363 100644 --- a/lib/sockunion.c +++ b/lib/sockunion.c @@ -46,6 +46,9 @@ int str2sockunion(const char *str, union sockunion *su) { int ret; + if (str == NULL) + return -1; + memset(su, 0, sizeof(union sockunion)); ret = inet_pton(AF_INET, str, &su->sin.sin_addr); diff --git a/lib/stream.c b/lib/stream.c index aba4c20166..a172eedc99 100644 --- a/lib/stream.c +++ b/lib/stream.c @@ -1109,6 +1109,10 @@ struct stream_fifo *stream_fifo_new(void) /* Add new stream to fifo. */ void stream_fifo_push(struct stream_fifo *fifo, struct stream *s) { +#if defined DEV_BUILD + size_t max, curmax; +#endif + if (fifo->tail) fifo->tail->next = s; else @@ -1116,8 +1120,15 @@ void stream_fifo_push(struct stream_fifo *fifo, struct stream *s) fifo->tail = s; fifo->tail->next = NULL; - +#if !defined DEV_BUILD atomic_fetch_add_explicit(&fifo->count, 1, memory_order_release); +#else + max = atomic_fetch_add_explicit(&fifo->count, 1, memory_order_release); + curmax = atomic_load_explicit(&fifo->max_count, memory_order_relaxed); + if (max > curmax) + atomic_store_explicit(&fifo->max_count, max, + memory_order_relaxed); +#endif } void stream_fifo_push_safe(struct stream_fifo *fifo, struct stream *s) diff --git a/lib/stream.h b/lib/stream.h index e5d325e43e..11af85c663 100644 --- a/lib/stream.h +++ b/lib/stream.h @@ -115,6 +115,9 @@ struct stream_fifo { /* number of streams in this fifo */ _Atomic size_t count; +#if defined DEV_BUILD + _Atomic size_t max_count; +#endif struct stream *head; struct stream *tail; @@ -121,10 +121,10 @@ int vrf_switch_to_netns(vrf_id_t vrf_id) /* VRF is default VRF. silently ignore */ if (!vrf || vrf->vrf_id == VRF_DEFAULT) - return 0; + return 1; /* 1 = default */ /* VRF has no NETNS backend. silently ignore */ if (vrf->data.l.netns_name[0] == '\0') - return 0; + return 2; /* 2 = no netns */ name = ns_netns_pathname(NULL, vrf->data.l.netns_name); if (debug_vrf) zlog_debug("VRF_SWITCH: %s(%u)", name, vrf->vrf_id); @@ -505,6 +505,35 @@ void vrf_terminate(void) } } +static int vrf_default_accepts_vrf(int type) +{ + const char *fname = NULL; + char buf[32] = {0x0}; + int ret = 0; + FILE *fd = NULL; + + /* + * TCP & UDP services running in the default VRF context (ie., not bound + * to any VRF device) can work across all VRF domains by enabling the + * tcp_l3mdev_accept and udp_l3mdev_accept sysctl options: + * sysctl -w net.ipv4.tcp_l3mdev_accept=1 + * sysctl -w net.ipv4.udp_l3mdev_accept=1 + */ + if (type == SOCK_STREAM) + fname = "/proc/sys/net/ipv4/tcp_l3mdev_accept"; + else if (type == SOCK_DGRAM) + fname = "/proc/sys/net/ipv4/udp_l3mdev_accept"; + else + return ret; + fd = fopen(fname, "r"); + if (fd == NULL) + return ret; + fgets(buf, 32, fd); + ret = atoi(buf); + fclose(fd); + return ret; +} + /* Create a socket for the VRF. */ int vrf_socket(int domain, int type, int protocol, vrf_id_t vrf_id, char *interfacename) @@ -515,6 +544,12 @@ int vrf_socket(int domain, int type, int protocol, vrf_id_t vrf_id, if (ret < 0) zlog_err("%s: Can't switch to VRF %u (%s)", __func__, vrf_id, safe_strerror(errno)); + if (ret > 0 && interfacename && vrf_default_accepts_vrf(type)) { + zlog_err("VRF socket not used since net.ipv4.%s_l3mdev_accept != 0", + (type == SOCK_STREAM ? "tcp" : "udp")); + errno = EEXIST; /* not sure if this is the best error... */ + return -2; + } ret = socket(domain, type, protocol); save_errno = errno; ret2 = vrf_switchback_to_initial(); @@ -2462,12 +2462,13 @@ static FILE *vty_use_backup_config(const char *fullpath) } /* Read up configuration file from file_name. */ -void vty_read_config(const char *config_file, char *config_default_dir) +bool vty_read_config(const char *config_file, char *config_default_dir) { char cwd[MAXPATHLEN]; FILE *confp = NULL; const char *fullpath; char *tmp = NULL; + bool read_success = false; /* If -f flag specified. */ if (config_file != NULL) { @@ -2525,8 +2526,10 @@ void vty_read_config(const char *config_file, char *config_default_dir) if (strstr(config_default_dir, "vtysh") == NULL) { ret = stat(integrate_default, &conf_stat); - if (ret >= 0) + if (ret >= 0) { + read_success = true; goto tmp_free_and_out; + } } #endif /* VTYSH */ confp = fopen(config_default_dir, "r"); @@ -2550,6 +2553,7 @@ void vty_read_config(const char *config_file, char *config_default_dir) } vty_read_file(confp); + read_success = true; fclose(confp); @@ -2558,6 +2562,8 @@ void vty_read_config(const char *config_file, char *config_default_dir) tmp_free_and_out: if (tmp) XFREE(MTYPE_TMP, tmp); + + return read_success; } /* Small utility function which output log to the VTY. */ @@ -242,7 +242,7 @@ extern void vty_frame(struct vty *, const char *, ...) PRINTF_ATTRIBUTE(2, 3); extern void vty_endframe(struct vty *, const char *); bool vty_set_include(struct vty *vty, const char *regexp); -extern void vty_read_config(const char *, char *); +extern bool vty_read_config(const char *, char *); extern void vty_time_print(struct vty *, int); extern void vty_serv_sock(const char *, unsigned short, const char *); extern void vty_close(struct vty *); diff --git a/nhrpd/nhrp_event.c b/nhrpd/nhrp_event.c index e7adc971e5..7ca9731765 100644 --- a/nhrpd/nhrp_event.c +++ b/nhrpd/nhrp_event.c @@ -59,7 +59,7 @@ static void evmgr_recv_message(struct event_manager *evmgr, struct zbuf *zb) buf[len] = 0; debugf(NHRP_DEBUG_EVENT, "evmgr: msg: %s", buf); - if (sscanf(buf, "eventid=%d", &eventid) != 1) + if (sscanf(buf, "eventid=%" SCNu32, &eventid) != 1) continue; if (sscanf(buf, "result=%63s", result) != 1) continue; diff --git a/nhrpd/nhrp_packet.c b/nhrpd/nhrp_packet.c index c27ebe1d90..e62ee1ef72 100644 --- a/nhrpd/nhrp_packet.c +++ b/nhrpd/nhrp_packet.c @@ -164,7 +164,7 @@ struct nhrp_cie_header *nhrp_cie_pull(struct zbuf *zb, if (!cie) return NULL; - if (cie->nbma_address_len + cie->nbma_subaddress_len) { + if (cie->nbma_address_len + cie->nbma_subaddress_len > 0) { sockunion_set(nbma, afi2family(htons(hdr->afnum)), zbuf_pulln(zb, cie->nbma_address_len diff --git a/nhrpd/vici.c b/nhrpd/vici.c index e6111f9d71..eb3827a12f 100644 --- a/nhrpd/vici.c +++ b/nhrpd/vici.c @@ -287,6 +287,7 @@ static void vici_recv_sa(struct vici_conn *vici, struct zbuf *msg, int event) char buf[32]; struct handle_sa_ctx ctx = { .event = event, + .msgctx.nsections = 0 }; vici_parse_message(vici, msg, parse_sa_message, &ctx.msgctx); @@ -305,7 +306,7 @@ static void vici_recv_message(struct vici_conn *vici, struct zbuf *msg) uint32_t msglen; uint8_t msgtype; struct blob name; - struct vici_message_ctx ctx; + struct vici_message_ctx ctx = { .nsections = 0 }; msglen = zbuf_get_be32(msg); msgtype = zbuf_get8(msg); diff --git a/ospf6d/ospf6_abr.c b/ospf6d/ospf6_abr.c index b3aa3b21d2..7bccc78e00 100644 --- a/ospf6d/ospf6_abr.c +++ b/ospf6d/ospf6_abr.c @@ -161,9 +161,10 @@ int ospf6_abr_originate_summary_to_area(struct ospf6_route *route, && route->type != OSPF6_DEST_TYPE_RANGE && ((route->type != OSPF6_DEST_TYPE_ROUTER) || !CHECK_FLAG(route->path.router_bits, OSPF6_ROUTER_BIT_E))) { - if (is_debug) - zlog_debug( - "Route type is none of network, range nor ASBR, ignore"); +#if 0 + zlog_debug( + "Route type is none of network, range nor ASBR, ignore"); +#endif return 0; } @@ -177,16 +178,17 @@ int ospf6_abr_originate_summary_to_area(struct ospf6_route *route, /* do not generate if the path's area is the same as target area */ if (route->path.area_id == area->area_id) { - if (is_debug) - zlog_debug("The route is in the area itself, ignore"); +#if 0 + zlog_debug("The route is in the area itself, ignore"); +#endif return 0; } /* do not generate if the nexthops belongs to the target area */ if (ospf6_abr_nexthops_belong_to_area(route, area)) { - if (is_debug) - zlog_debug( - "The route's nexthop is in the same area, ignore"); +#if 0 + zlog_debug("The route's nexthop is in the same area, ignore"); +#endif return 0; } @@ -641,6 +643,11 @@ void ospf6_abr_originate_summary(struct ospf6_route *route) if (route->type == OSPF6_DEST_TYPE_NETWORK) { oa = ospf6_area_lookup(route->path.area_id, ospf6); + if (!oa) { + zlog_err("OSPFv6 area lookup failed"); + return; + } + range = ospf6_route_lookup_bestmatch(&route->prefix, oa->range_table); if (range) { @@ -864,7 +871,8 @@ void ospf6_abr_examin_summary(struct ospf6_lsa *lsa, struct ospf6_area *oa) lsa->header); prefix.family = AF_INET6; prefix.prefixlen = prefix_lsa->prefix.prefix_length; - ospf6_prefix_in6_addr(&prefix.u.prefix6, &prefix_lsa->prefix); + ospf6_prefix_in6_addr(&prefix.u.prefix6, prefix_lsa, + &prefix_lsa->prefix); if (is_debug) prefix2str(&prefix, buf, sizeof(buf)); table = oa->ospf6->route_table; @@ -1284,7 +1292,7 @@ static char *ospf6_inter_area_prefix_lsa_get_prefix_str(struct ospf6_lsa *lsa, (struct ospf6_inter_prefix_lsa *)OSPF6_LSA_HEADER_END( lsa->header); - ospf6_prefix_in6_addr(&in6, &prefix_lsa->prefix); + ospf6_prefix_in6_addr(&in6, prefix_lsa, &prefix_lsa->prefix); if (buf) { inet_ntop(AF_INET6, &in6, buf, buflen); sprintf(&buf[strlen(buf)], "/%d", diff --git a/ospf6d/ospf6_asbr.c b/ospf6d/ospf6_asbr.c index 7f575ee506..a723396507 100644 --- a/ospf6d/ospf6_asbr.c +++ b/ospf6d/ospf6_asbr.c @@ -497,7 +497,8 @@ void ospf6_asbr_lsa_add(struct ospf6_lsa *lsa) route->type = OSPF6_DEST_TYPE_NETWORK; route->prefix.family = AF_INET6; route->prefix.prefixlen = external->prefix.prefix_length; - ospf6_prefix_in6_addr(&route->prefix.u.prefix6, &external->prefix); + ospf6_prefix_in6_addr(&route->prefix.u.prefix6, external, + &external->prefix); route->path.area_id = asbr_entry->path.area_id; route->path.origin.type = lsa->header->type; @@ -576,7 +577,7 @@ void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa, route_to_del->type = OSPF6_DEST_TYPE_NETWORK; route_to_del->prefix.family = AF_INET6; route_to_del->prefix.prefixlen = external->prefix.prefix_length; - ospf6_prefix_in6_addr(&route_to_del->prefix.u.prefix6, + ospf6_prefix_in6_addr(&route_to_del->prefix.u.prefix6, external, &external->prefix); route_to_del->path.origin.type = lsa->header->type; @@ -603,7 +604,7 @@ void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa, memset(&prefix, 0, sizeof(struct prefix)); prefix.family = AF_INET6; prefix.prefixlen = external->prefix.prefix_length; - ospf6_prefix_in6_addr(&prefix.u.prefix6, &external->prefix); + ospf6_prefix_in6_addr(&prefix.u.prefix6, external, &external->prefix); route = ospf6_route_lookup(&prefix, ospf6->route_table); if (route == NULL) { @@ -1705,7 +1706,8 @@ static char *ospf6_as_external_lsa_get_prefix_str(struct ospf6_lsa *lsa, lsa->header); if (pos == 0) { - ospf6_prefix_in6_addr(&in6, &external->prefix); + ospf6_prefix_in6_addr(&in6, external, + &external->prefix); prefix_length = external->prefix.prefix_length; } else { in6 = *((struct in6_addr diff --git a/ospf6d/ospf6_flood.c b/ospf6d/ospf6_flood.c index 2059d84868..ae26668c8a 100644 --- a/ospf6d/ospf6_flood.c +++ b/ospf6d/ospf6_flood.c @@ -347,6 +347,7 @@ void ospf6_flood_interface(struct ospf6_neighbor *from, struct ospf6_lsa *lsa, "Received is newer, remove requesting"); if (req == on->last_ls_req) { ospf6_lsa_unlock(req); + req = NULL; on->last_ls_req = NULL; } if (req) diff --git a/ospf6d/ospf6_intra.c b/ospf6d/ospf6_intra.c index d99541ebad..0ce08a61e2 100644 --- a/ospf6d/ospf6_intra.c +++ b/ospf6d/ospf6_intra.c @@ -1323,6 +1323,8 @@ static void ospf6_intra_prefix_update_route_origin(struct ospf6_route *oa_route) g_route = ospf6_route_lookup(&oa_route->prefix, ospf6->route_table); + assert(g_route); + for (ospf6_route_lock(g_route); g_route && ospf6_route_is_prefix(&oa_route->prefix, g_route); g_route = nroute) { @@ -1698,7 +1700,8 @@ void ospf6_intra_prefix_lsa_add(struct ospf6_lsa *lsa) memset(&route->prefix, 0, sizeof(struct prefix)); route->prefix.family = AF_INET6; route->prefix.prefixlen = op->prefix_length; - ospf6_prefix_in6_addr(&route->prefix.u.prefix6, op); + ospf6_prefix_in6_addr(&route->prefix.u.prefix6, + intra_prefix_lsa, op); route->type = OSPF6_DEST_TYPE_NETWORK; route->path.origin.type = lsa->header->type; @@ -1880,7 +1883,7 @@ void ospf6_intra_prefix_lsa_remove(struct ospf6_lsa *lsa) memset(&prefix, 0, sizeof(struct prefix)); prefix.family = AF_INET6; prefix.prefixlen = op->prefix_length; - ospf6_prefix_in6_addr(&prefix.u.prefix6, op); + ospf6_prefix_in6_addr(&prefix.u.prefix6, intra_prefix_lsa, op); route = ospf6_route_lookup(&prefix, oa->route_table); if (route == NULL) diff --git a/ospf6d/ospf6_proto.c b/ospf6d/ospf6_proto.c index 4b56a64b7f..864974c9a4 100644 --- a/ospf6d/ospf6_proto.c +++ b/ospf6d/ospf6_proto.c @@ -24,6 +24,16 @@ #include "ospf6_proto.h" +void ospf6_prefix_in6_addr(struct in6_addr *in6, const void *prefix_buf, + const struct ospf6_prefix *p) +{ + ptrdiff_t in6_off = (caddr_t)p->addr - (caddr_t)prefix_buf; + + memset(in6, 0, sizeof(struct in6_addr)); + memcpy(in6, (uint8_t *)prefix_buf + in6_off, + OSPF6_PREFIX_SPACE(p->prefix_length)); +} + void ospf6_prefix_apply_mask(struct ospf6_prefix *op) { uint8_t *pnt, mask; diff --git a/ospf6d/ospf6_proto.h b/ospf6d/ospf6_proto.h index ca2804c476..c9e7b549db 100644 --- a/ospf6d/ospf6_proto.h +++ b/ospf6d/ospf6_proto.h @@ -84,13 +84,8 @@ struct ospf6_prefix { #define OSPF6_PREFIX_NEXT(x) \ ((struct ospf6_prefix *)((caddr_t)(x) + OSPF6_PREFIX_SIZE(x))) -#define ospf6_prefix_in6_addr(in6, op) \ - do { \ - memset(in6, 0, sizeof(struct in6_addr)); \ - memcpy(in6, (caddr_t)(op) + sizeof(struct ospf6_prefix), \ - OSPF6_PREFIX_SPACE((op)->prefix_length)); \ - } while (0) - +extern void ospf6_prefix_in6_addr(struct in6_addr *in6, const void *prefix_buf, + const struct ospf6_prefix *p); extern void ospf6_prefix_apply_mask(struct ospf6_prefix *op); extern void ospf6_prefix_options_printbuf(uint8_t prefix_options, char *buf, int size); diff --git a/ospf6d/ospf6_spf.c b/ospf6d/ospf6_spf.c index 28c3459825..5b6691e6bf 100644 --- a/ospf6d/ospf6_spf.c +++ b/ospf6d/ospf6_spf.c @@ -677,6 +677,10 @@ void ospf6_spf_schedule(struct ospf6 *ospf6, unsigned int reason) { unsigned long delay, elapsed, ht; + /* OSPF instance does not exist. */ + if (ospf6 == NULL) + return; + ospf6_set_spf_reason(ospf6, reason); if (IS_OSPF6_DEBUG_SPF(PROCESS) || IS_OSPF6_DEBUG_SPF(TIME)) { @@ -686,10 +690,6 @@ void ospf6_spf_schedule(struct ospf6 *ospf6, unsigned int reason) rbuf); } - /* OSPF instance does not exist. */ - if (ospf6 == NULL) - return; - /* SPF calculation timer is already scheduled. */ if (ospf6->t_spf_calc) { if (IS_OSPF6_DEBUG_SPF(PROCESS) || IS_OSPF6_DEBUG_SPF(TIME)) diff --git a/ospfd/ospf_api.c b/ospfd/ospf_api.c index 8369dde822..b1175a2f68 100644 --- a/ospfd/ospf_api.c +++ b/ospfd/ospf_api.c @@ -510,17 +510,18 @@ struct msg *new_msg_originate_request(uint32_t seqnum, struct in_addr ifaddr, struct msg_originate_request *omsg; unsigned int omsglen; char buf[OSPF_API_MAX_MSG_SIZE]; + size_t off_data = offsetof(struct msg_originate_request, data); + size_t data_maxs = sizeof(buf) - off_data; + struct lsa_header *omsg_data = (struct lsa_header *)&buf[off_data]; omsg = (struct msg_originate_request *)buf; omsg->ifaddr = ifaddr; omsg->area_id = area_id; omsglen = ntohs(data->length); - if (omsglen - > sizeof(buf) - offsetof(struct msg_originate_request, data)) - omsglen = sizeof(buf) - - offsetof(struct msg_originate_request, data); - memcpy(&omsg->data, data, omsglen); + if (omsglen > data_maxs) + omsglen = data_maxs; + memcpy(omsg_data, data, omsglen); omsglen += sizeof(struct msg_originate_request) - sizeof(struct lsa_header); @@ -630,6 +631,9 @@ struct msg *new_msg_lsa_change_notify(uint8_t msgtype, uint32_t seqnum, uint8_t buf[OSPF_API_MAX_MSG_SIZE]; struct msg_lsa_change_notify *nmsg; unsigned int len; + size_t off_data = offsetof(struct msg_lsa_change_notify, data); + size_t data_maxs = sizeof(buf) - off_data; + struct lsa_header *nmsg_data = (struct lsa_header *)&buf[off_data]; assert(data); @@ -640,10 +644,9 @@ struct msg *new_msg_lsa_change_notify(uint8_t msgtype, uint32_t seqnum, memset(&nmsg->pad, 0, sizeof(nmsg->pad)); len = ntohs(data->length); - if (len > sizeof(buf) - offsetof(struct msg_lsa_change_notify, data)) - len = sizeof(buf) - - offsetof(struct msg_lsa_change_notify, data); - memcpy(&nmsg->data, data, len); + if (len > data_maxs) + len = data_maxs; + memcpy(nmsg_data, data, len); len += sizeof(struct msg_lsa_change_notify) - sizeof(struct lsa_header); return msg_new(msgtype, nmsg, seqnum, len); diff --git a/ospfd/ospf_apiserver.c b/ospfd/ospf_apiserver.c index 37735e3611..8f8900e147 100644 --- a/ospfd/ospf_apiserver.c +++ b/ospfd/ospf_apiserver.c @@ -1741,6 +1741,8 @@ struct ospf_lsa *ospf_apiserver_lsa_refresher(struct ospf_lsa *lsa) struct ospf_lsa *new = NULL; struct ospf *ospf; + assert(lsa); + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); assert(ospf); @@ -1751,6 +1753,7 @@ struct ospf_lsa *ospf_apiserver_lsa_refresher(struct ospf_lsa *lsa) dump_lsa_key(lsa)); lsa->data->ls_age = htons(OSPF_LSA_MAXAGE); /* Flush it anyway. */ + goto out; } if (IS_LSA_MAXAGE(lsa)) { diff --git a/ospfd/ospf_ase.c b/ospfd/ospf_ase.c index d425624862..c799a4b30f 100644 --- a/ospfd/ospf_ase.c +++ b/ospfd/ospf_ase.c @@ -582,7 +582,7 @@ static int ospf_ase_route_match_same(struct route_table *rt, /* Check each path. */ for (n1 = listhead(or->paths), n2 = listhead(newor->paths); n1 && n2; - n1 = listnextnode(n1), n2 = listnextnode(n2)) { + n1 = listnextnode_unchecked(n1), n2 = listnextnode_unchecked(n2)) { op = listgetdata(n1); newop = listgetdata(n2); diff --git a/ospfd/ospf_ri.c b/ospfd/ospf_ri.c index 93267156f2..fa7dd04d19 100644 --- a/ospfd/ospf_ri.c +++ b/ospfd/ospf_ri.c @@ -1649,7 +1649,7 @@ DEFUN (pce_domain, if (!ospf_ri_enabled(vty)) return CMD_WARNING_CONFIG_FAILED; - if (sscanf(argv[idx_number]->arg, "%d", &as) != 1) { + if (sscanf(argv[idx_number]->arg, "%" SCNu32, &as) != 1) { vty_out(vty, "pce_domain: fscanf: %s\n", safe_strerror(errno)); return CMD_WARNING_CONFIG_FAILED; } @@ -1684,7 +1684,7 @@ DEFUN (no_pce_domain, uint32_t as; struct ospf_pce_info *pce = &OspfRI.pce_info; - if (sscanf(argv[idx_number]->arg, "%d", &as) != 1) { + if (sscanf(argv[idx_number]->arg, "%" SCNu32, &as) != 1) { vty_out(vty, "no_pce_domain: fscanf: %s\n", safe_strerror(errno)); return CMD_WARNING_CONFIG_FAILED; @@ -1718,7 +1718,7 @@ DEFUN (pce_neigbhor, if (!ospf_ri_enabled(vty)) return CMD_WARNING_CONFIG_FAILED; - if (sscanf(argv[idx_number]->arg, "%d", &as) != 1) { + if (sscanf(argv[idx_number]->arg, "%" SCNu32, &as) != 1) { vty_out(vty, "pce_neighbor: fscanf: %s\n", safe_strerror(errno)); return CMD_WARNING_CONFIG_FAILED; @@ -1754,7 +1754,7 @@ DEFUN (no_pce_neighbor, uint32_t as; struct ospf_pce_info *pce = &OspfRI.pce_info; - if (sscanf(argv[idx_number]->arg, "%d", &as) != 1) { + if (sscanf(argv[idx_number]->arg, "%" SCNu32, &as) != 1) { vty_out(vty, "no_pce_neighbor: fscanf: %s\n", safe_strerror(errno)); return CMD_WARNING_CONFIG_FAILED; diff --git a/ospfd/ospf_route.c b/ospfd/ospf_route.c index b964bbab74..b36f2f4652 100644 --- a/ospfd/ospf_route.c +++ b/ospfd/ospf_route.c @@ -170,8 +170,8 @@ int ospf_route_match_same(struct route_table *rt, struct prefix_ipv4 *prefix, /* Check each path. */ for (n1 = listhead(or->paths), n2 = listhead(newor->paths); - n1 && n2; - n1 = listnextnode(n1), n2 = listnextnode(n2)) { + n1 && n2; n1 = listnextnode_unchecked(n1), + n2 = listnextnode_unchecked(n2)) { op = listgetdata(n1); newop = listgetdata(n2); diff --git a/ospfd/ospf_te.c b/ospfd/ospf_te.c index 2b1b328617..86125d0c76 100644 --- a/ospfd/ospf_te.c +++ b/ospfd/ospf_te.c @@ -2051,12 +2051,11 @@ static uint16_t ospf_mpls_te_show_link_subtlv(struct vty *vty, struct tlv_header *tlvh0, uint16_t subtotal, uint16_t total) { - struct tlv_header *tlvh, *next; + struct tlv_header *tlvh; uint16_t sum = subtotal; for (tlvh = tlvh0; sum < total; - tlvh = (next ? next : TLV_HDR_NEXT(tlvh))) { - next = NULL; + tlvh = TLV_HDR_NEXT(tlvh)) { switch (ntohs(tlvh->type)) { case TE_LINK_SUBTLV_LINK_TYPE: sum += show_vty_link_subtlv_link_type(vty, tlvh); diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c index 31cffea7f2..ddf9133ed9 100644 --- a/ospfd/ospf_vty.c +++ b/ospfd/ospf_vty.c @@ -2412,8 +2412,8 @@ DEFUN (ospf_neighbor_poll_interval, int idx_poll = 3; int idx_pri = 5; struct in_addr nbr_addr; - unsigned int priority = OSPF_NEIGHBOR_PRIORITY_DEFAULT; - unsigned int interval = OSPF_POLL_INTERVAL_DEFAULT; + unsigned int priority; + unsigned int interval; if (!inet_aton(argv[idx_ipv4]->arg, &nbr_addr)) { vty_out(vty, "Please specify Neighbor ID by A.B.C.D\n"); @@ -2422,8 +2422,8 @@ DEFUN (ospf_neighbor_poll_interval, interval = strtoul(argv[idx_poll]->arg, NULL, 10); - if (argc > 4) - priority = strtoul(argv[idx_pri]->arg, NULL, 10); + priority = argc > 4 ? strtoul(argv[idx_pri]->arg, NULL, 10) + : OSPF_NEIGHBOR_PRIORITY_DEFAULT; ospf_nbr_nbma_set(ospf, nbr_addr); ospf_nbr_nbma_poll_interval_set(ospf, nbr_addr, interval); @@ -8166,6 +8166,11 @@ DEFUN (ospf_redistribute_instance_source, source = proto_redistnum(AFI_IP, argv[idx_ospf_table]->text); + if (source < 0) { + vty_out(vty, "Unknown instance redistribution\n"); + return CMD_WARNING_CONFIG_FAILED; + } + instance = strtoul(argv[idx_number]->arg, NULL, 10); if ((source == ZEBRA_ROUTE_OSPF) && !ospf->instance) { diff --git a/ospfd/ospf_zebra.c b/ospfd/ospf_zebra.c index 141ece9c7a..0a7776cced 100644 --- a/ospfd/ospf_zebra.c +++ b/ospfd/ospf_zebra.c @@ -670,6 +670,16 @@ int ospf_redistribute_set(struct ospf *ospf, int type, unsigned short instance, struct ospf_redist *red; red = ospf_redist_lookup(ospf, type, instance); + + if (red == NULL) { + zlog_err( + "Redistribute[%s][%d]: Lookup failed Type[%d] , Metric[%d]", + ospf_redist_string(type), instance, + metric_type(ospf, type, instance), + metric_value(ospf, type, instance)); + return CMD_WARNING_CONFIG_FAILED; + } + if (ospf_is_type_redistributed(ospf, type, instance)) { if (mtype != red->dmetric.type) { red->dmetric.type = mtype; diff --git a/ospfd/ospfd.c b/ospfd/ospfd.c index 4cf38439c6..f315421843 100644 --- a/ospfd/ospfd.c +++ b/ospfd/ospfd.c @@ -243,13 +243,14 @@ static struct ospf *ospf_new(unsigned short instance, const char *name) zlog_debug( "%s: Create new ospf instance with vrf_name %s vrf_id %u", __PRETTY_FUNCTION__, name, new->vrf_id); - if (vrf) - ospf_vrf_link(new, vrf); } else { new->vrf_id = VRF_DEFAULT; vrf = vrf_lookup_by_id(VRF_DEFAULT); - ospf_vrf_link(new, vrf); } + + if (vrf) + ospf_vrf_link(new, vrf); + ospf_zebra_vrf_register(new); new->abr_type = OSPF_ABR_DEFAULT; diff --git a/pbrd/pbr_nht.c b/pbrd/pbr_nht.c index a4a9233f72..a8cefce84f 100644 --- a/pbrd/pbr_nht.c +++ b/pbrd/pbr_nht.c @@ -192,7 +192,7 @@ static void *pbr_nhgc_alloc(void *p) new = XCALLOC(MTYPE_PBR_NHG, sizeof(*new)); strcpy(new->name, pnhgc->name); - new->table_id = pbr_nht_get_next_tableid(); + new->table_id = pbr_nht_get_next_tableid(false); DEBUGD(&pbr_dbg_nht, "%s: NHT: %s assigned Table ID: %u", __PRETTY_FUNCTION__, new->name, new->table_id); @@ -218,6 +218,9 @@ void pbr_nhgroup_add_cb(const char *name) pnhgc = pbr_nht_add_group(name); + if (!pnhgc) + return; + DEBUGD(&pbr_dbg_nht, "%s: Added nexthop-group %s", __PRETTY_FUNCTION__, name); @@ -234,6 +237,13 @@ void pbr_nhgroup_add_nexthop_cb(const struct nexthop_group_cmd *nhgc, struct pbr_nexthop_cache pnhc_find = {}; struct pbr_nexthop_cache *pnhc; + if (!pbr_nht_get_next_tableid(true)) { + zlog_warn( + "%s: Exhausted all table identifiers; cannot create nexthop-group cache for nexthop-group '%s'", + __PRETTY_FUNCTION__, nhgc->name); + return; + } + /* find pnhgc by name */ strlcpy(pnhgc_find.name, nhgc->name, sizeof(pnhgc_find.name)); pnhgc = hash_get(pbr_nhg_hash, &pnhgc_find, pbr_nhgc_alloc); @@ -268,7 +278,7 @@ void pbr_nhgroup_del_nexthop_cb(const struct nexthop_group_cmd *nhgc, /* find pnhgc by name */ strlcpy(pnhgc_find.name, nhgc->name, sizeof(pnhgc_find.name)); - pnhgc = hash_get(pbr_nhg_hash, &pnhgc_find, pbr_nhgc_alloc); + pnhgc = hash_lookup(pbr_nhg_hash, &pnhgc_find); /* delete pnhc from pnhgc->nhh */ pnhc_find.nexthop = (struct nexthop *)nhop; @@ -487,6 +497,14 @@ void pbr_nht_add_individual_nexthop(struct pbr_map_sequence *pbrms) memset(&find, 0, sizeof(find)); pbr_nht_nexthop_make_name(pbrms->parent->name, PBR_NHC_NAMELEN, pbrms->seqno, find.name); + + if (!pbr_nht_get_next_tableid(true)) { + zlog_warn( + "%s: Exhausted all table identifiers; cannot create nexthop-group cache for nexthop-group '%s'", + __PRETTY_FUNCTION__, find.name); + return; + } + if (!pbrms->internal_nhg_name) pbrms->internal_nhg_name = XSTRDUP(MTYPE_TMP, find.name); @@ -547,11 +565,18 @@ struct pbr_nexthop_group_cache *pbr_nht_add_group(const char *name) struct pbr_nexthop_group_cache *pnhgc; struct pbr_nexthop_group_cache lookup; + if (!pbr_nht_get_next_tableid(true)) { + zlog_warn( + "%s: Exhausted all table identifiers; cannot create nexthop-group cache for nexthop-group '%s'", + __PRETTY_FUNCTION__, name); + return NULL; + } + nhgc = nhgc_find(name); if (!nhgc) { - zlog_warn("%s: Could not find group %s to add", - __PRETTY_FUNCTION__, name); + DEBUGD(&pbr_dbg_nht, "%s: Could not find nhgc with name: %s\n", + __PRETTY_FUNCTION__, name); return NULL; } @@ -709,8 +734,7 @@ static int pbr_nhg_hash_equal(const void *arg1, const void *arg2) return !strcmp(nhgc1->name, nhgc2->name); } - -uint32_t pbr_nht_get_next_tableid(void) +uint32_t pbr_nht_get_next_tableid(bool peek) { uint32_t i; bool found = false; @@ -723,7 +747,7 @@ uint32_t pbr_nht_get_next_tableid(void) } if (found) { - nhg_tableid[i] = true; + nhg_tableid[i] = !peek; return i; } else return 0; diff --git a/pbrd/pbr_nht.h b/pbrd/pbr_nht.h index e6fdbfd04c..d37803fbe3 100644 --- a/pbrd/pbr_nht.h +++ b/pbrd/pbr_nht.h @@ -56,9 +56,13 @@ extern void pbr_nht_write_table_range(struct vty *vty); extern void pbr_nht_set_tableid_range(uint32_t low, uint32_t high); /* - * Get the next tableid to use for installation + * Get the next tableid to use for installation. + * + * peek + * If set to true, retrieves the next ID without marking it used. The next + * call will return the same ID. */ -extern uint32_t pbr_nht_get_next_tableid(void); +extern uint32_t pbr_nht_get_next_tableid(bool peek); /* * Get the next rule number to use for installation */ diff --git a/pbrd/pbr_vty.c b/pbrd/pbr_vty.c index 7e0e8d632b..44e14c5477 100644 --- a/pbrd/pbr_vty.c +++ b/pbrd/pbr_vty.c @@ -26,8 +26,8 @@ #include "nexthop.h" #include "nexthop_group.h" #include "log.h" -#include "json.h" #include "debug.h" +#include "pbr.h" #include "pbrd/pbr_nht.h" #include "pbrd/pbr_map.h" @@ -85,6 +85,34 @@ DEFUN_NOSH(no_pbr_map, no_pbr_map_cmd, "no pbr-map WORD [seq (1-700)]", return CMD_SUCCESS; } +DEFPY(pbr_set_table_range, + pbr_set_table_range_cmd, + "[no] pbr table range (10000-4294966272)$lb (10000-4294966272)$ub", + NO_STR + PBR_STR + "Set table ID range\n" + "Set table ID range\n" + "Lower bound for table ID range\n" + "Upper bound for table ID range\n") +{ + /* upper bound is 2^32 - 2^10 */ + int ret = CMD_WARNING; + const int minrange = 1000; + + /* validate given bounds */ + if (lb > ub) + vty_out(vty, "%% Lower bound must be less than upper bound\n"); + else if (ub - lb < minrange) + vty_out(vty, "%% Range breadth must be at least %d\n", minrange); + else { + ret = CMD_SUCCESS; + pbr_nht_set_tableid_range((uint32_t) lb, (uint32_t) ub); + } + + return ret; +} + + DEFPY(pbr_map_match_src, pbr_map_match_src_cmd, "[no] match src-ip <A.B.C.D/M|X:X::X:X/M>$prefix", NO_STR @@ -355,10 +383,9 @@ DEFPY (pbr_policy, DEFPY (show_pbr, show_pbr_cmd, - "show pbr [json$json]", + "show pbr", SHOW_STR - "Policy Based Routing\n" - JSON_STR) + PBR_STR) { pbr_nht_write_table_range(vty); pbr_nht_write_rule_range(vty); @@ -368,13 +395,12 @@ DEFPY (show_pbr, DEFPY (show_pbr_map, show_pbr_map_cmd, - "show pbr map [NAME$name] [detail$detail] [json$json]", + "show pbr map [NAME$name] [detail$detail]", SHOW_STR - "Policy Based Routing\n" + PBR_STR "PBR Map\n" "PBR Map Name\n" - "Detailed information\n" - JSON_STR) + "Detailed information\n") { struct pbr_map_sequence *pbrms; struct pbr_map *pbrm; @@ -439,7 +465,7 @@ DEFPY(show_pbr_nexthop_group, show_pbr_nexthop_group_cmd, "show pbr nexthop-groups [WORD$word]", SHOW_STR - "Policy Based Routing\n" + PBR_STR "Nexthop Groups\n" "Optional Name of the nexthop group\n") { @@ -450,12 +476,11 @@ DEFPY(show_pbr_nexthop_group, DEFPY (show_pbr_interface, show_pbr_interface_cmd, - "show pbr interface [NAME$name] [json$json]", + "show pbr interface [NAME$name]", SHOW_STR - "Policy Based Routing\n" + PBR_STR "PBR Interface\n" - "PBR Interface Name\n" - JSON_STR) + "PBR Interface Name\n") { struct interface *ifp; struct vrf *vrf; @@ -489,7 +514,6 @@ DEFPY (show_pbr_interface, } /* PBR debugging CLI ------------------------------------------------------- */ -/* clang-format off */ static struct cmd_node debug_node = {DEBUG_NODE, "", 1}; @@ -498,7 +522,7 @@ DEFPY(debug_pbr, "[no] debug pbr [{map$map|zebra$zebra|nht$nht|events$events}]", NO_STR DEBUG_STR - "Policy Based Routing\n" + PBR_STR "Policy maps\n" "PBRD <-> Zebra communications\n" "Nexthop tracking\n" @@ -527,7 +551,7 @@ DEFUN_NOSH(show_debugging_pbr, "show debugging [pbr]", SHOW_STR DEBUG_STR - "Policy Based Routing\n") + PBR_STR) { vty_out(vty, "PBR debugging status:\n"); @@ -536,7 +560,6 @@ DEFUN_NOSH(show_debugging_pbr, return CMD_SUCCESS; } -/* clang-format on */ /* ------------------------------------------------------------------------- */ @@ -634,6 +657,7 @@ void pbr_vty_init(void) install_element(CONFIG_NODE, &pbr_map_cmd); install_element(CONFIG_NODE, &no_pbr_map_cmd); + install_element(CONFIG_NODE, &pbr_set_table_range_cmd); install_element(INTERFACE_NODE, &pbr_policy_cmd); install_element(PBRMAP_NODE, &pbr_map_match_src_cmd); install_element(PBRMAP_NODE, &pbr_map_match_dst_cmd); diff --git a/pimd/mtracebis.c b/pimd/mtracebis.c index 731fdb1beb..c0d95aeed9 100644 --- a/pimd/mtracebis.c +++ b/pimd/mtracebis.c @@ -266,6 +266,8 @@ static int recv_response(int fd, int *hops, struct igmp_mtrace *mtracer) int mtrace_len; int responses; unsigned short sum; + size_t mtrace_off; + size_t ip_len; recvd = recvfrom(fd, mtrace_buf, IP_AND_MTRACE_BUF_LEN, 0, NULL, 0); @@ -292,17 +294,20 @@ static int recv_response(int fd, int *hops, struct igmp_mtrace *mtracer) 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 ((char *)mtrace + mtrace_len - > (char *)mtrace_buf + IP_AND_MTRACE_BUF_LEN) + /* Header overflow check */ + mtrace_off = 4 * ip->ip_hl; + if (mtrace_off > MTRACE_BUF_LEN) return -1; - if (mtrace_len < (int)MTRACE_HDR_SIZE) + /* Underflow/overflow check */ + ip_len = ntohs(ip->ip_len); + if (ip_len < mtrace_off || ip_len < MTRACE_HDR_SIZE + || ip_len > MTRACE_BUF_LEN) return -1; + mtrace_len = ip_len - mtrace_off; + mtrace = (struct igmp_mtrace *)(mtrace_buf + mtrace_off); + sum = mtrace->checksum; mtrace->checksum = 0; if (sum != in_cksum(mtrace, mtrace_len)) { @@ -336,7 +341,7 @@ static int wait_for_response(int fd, int *hops, struct igmp_mtrace *mtrace, { fd_set readfds; struct timeval timeout; - int ret = -1; + int ret; long msec, rmsec, tmsec; FD_ZERO(&readfds); diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index 55222ecddb..123c47568c 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -4504,8 +4504,8 @@ static void show_mroute(struct pim_instance *pim, struct vty *vty, bool fill, json_object *json_source = NULL; json_object *json_oil = NULL; json_object *json_ifp_out = NULL; - int found_oif = 0; - int first = 1; + int found_oif; + int first; char grp_str[INET_ADDRSTRLEN]; char src_str[INET_ADDRSTRLEN]; char in_ifname[INTERFACE_NAMSIZ + 1]; diff --git a/pimd/pim_igmp_mtrace.c b/pimd/pim_igmp_mtrace.c index 673e2ca5b8..95d0278a34 100644 --- a/pimd/pim_igmp_mtrace.c +++ b/pimd/pim_igmp_mtrace.c @@ -817,7 +817,7 @@ int igmp_mtrace_recv_qry_req(struct igmp_sock *igmp, struct ip *ip_hdr, * Previous-hop router not known, * packet is sent to an appropriate multicast address */ - inet_aton(MCAST_ALL_ROUTERS, &nh_addr); + (void)inet_aton(MCAST_ALL_ROUTERS, &nh_addr); } /* 6.2.2 8. If this router is the Rendez-vous Point */ diff --git a/pimd/pim_pim.c b/pimd/pim_pim.c index de09b070f4..f506875282 100644 --- a/pimd/pim_pim.c +++ b/pimd/pim_pim.c @@ -521,7 +521,7 @@ int pim_msg_send(int fd, struct in_addr src, struct in_addr dst, socklen_t tolen; unsigned char buffer[10000]; unsigned char *msg_start; - uint8_t ttl = MAXTTL; + uint8_t ttl; struct pim_msg_header *header; struct ip *ip; diff --git a/pimd/pim_zebra.c b/pimd/pim_zebra.c index ae7f61c8eb..a58dfcdd5f 100644 --- a/pimd/pim_zebra.c +++ b/pimd/pim_zebra.c @@ -202,7 +202,7 @@ static int pim_zebra_if_state_up(int command, struct zclient *zclient, * If we have a pimreg device callback and it's for a specific * table set the master appropriately */ - if (sscanf(ifp->name, "pimreg%d", &table_id) == 1) { + if (sscanf(ifp->name, "pimreg%" SCNu32, &table_id) == 1) { struct vrf *vrf; RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { if ((table_id == vrf->data.l.table_id) @@ -737,8 +737,6 @@ static void pim_zebra_connected(struct zclient *zclient) void pim_zebra_init(void) { - int i; - /* Socket for receiving updates from Zebra daemon */ zclient = zclient_new_notify(master, &zclient_options_default); @@ -754,31 +752,7 @@ void pim_zebra_init(void) zclient_init(zclient, ZEBRA_ROUTE_PIM, 0, &pimd_privs); if (PIM_DEBUG_PIM_TRACE) { - zlog_info("zclient_init cleared redistribution request"); - } - - /* Request all redistribution */ - for (i = 0; i < ZEBRA_ROUTE_MAX; i++) { - if (i == zclient->redist_default) - continue; - vrf_bitmap_set(zclient->redist[AFI_IP][i], pimg->vrf_id); - ; - if (PIM_DEBUG_PIM_TRACE) { - zlog_debug("%s: requesting redistribution for %s (%i)", - __PRETTY_FUNCTION__, zebra_route_string(i), - i); - } - } - - /* Request default information */ - zclient_redistribute_default(ZEBRA_REDISTRIBUTE_DEFAULT_ADD, zclient, - pimg->vrf_id); - - if (PIM_DEBUG_PIM_TRACE) { - zlog_info("%s: requesting default information redistribution", - __PRETTY_FUNCTION__); - - zlog_notice("%s: zclient update socket initialized", + zlog_notice("%s: zclient socket initialized", __PRETTY_FUNCTION__); } diff --git a/ripd/ripd.c b/ripd/ripd.c index 92c27106d5..90dc7808eb 100644 --- a/ripd/ripd.c +++ b/ripd/ripd.c @@ -799,11 +799,11 @@ static int rip_auth_simple_password(struct rte *rte, struct sockaddr_in *from, struct interface *ifp) { struct rip_interface *ri; - char *auth_str = (char *)&rte->prefix; + char *auth_str = (char *)rte + offsetof(struct rte, prefix); int i; /* reject passwords with zeros in the middle of the string */ - for (i = strlen(auth_str); i < 16; i++) { + for (i = strnlen(auth_str, 16); i < 16; i++) { if (auth_str[i] != '\0') return 0; } diff --git a/ripngd/ripng_interface.c b/ripngd/ripng_interface.c index d1057bf53e..c463630b12 100644 --- a/ripngd/ripng_interface.c +++ b/ripngd/ripng_interface.c @@ -492,7 +492,7 @@ static int ripng_enable_network_lookup_if(struct interface *ifp) for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, connected)) { struct prefix *p; - struct route_node *node; + struct route_node *n; p = connected->address; @@ -501,10 +501,10 @@ static int ripng_enable_network_lookup_if(struct interface *ifp) address.prefix = p->u.prefix6; address.prefixlen = IPV6_MAX_BITLEN; - node = route_node_match(ripng_enable_network, - (struct prefix *)&address); - if (node) { - route_unlock_node(node); + n = route_node_match(ripng_enable_network, + (struct prefix *)&address); + if (n) { + route_unlock_node(n); return 1; } } diff --git a/sharpd/sharp_main.c b/sharpd/sharp_main.c index a478b416bf..22a19da0b3 100644 --- a/sharpd/sharp_main.c +++ b/sharpd/sharp_main.c @@ -51,7 +51,6 @@ uint32_t installed_routes = 0; uint32_t removed_routes = 0; zebra_capabilities_t _caps_p[] = { - ZCAP_NET_RAW, ZCAP_BIND, ZCAP_NET_ADMIN, }; struct zebra_privs_t sharp_privs = { diff --git a/tests/Makefile.am b/tests/Makefile.am index 9b60312cef..32d2db768a 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -102,8 +102,6 @@ lib/cli/test_commands_defun.c: ../vtysh/vtysh_cmd.c isisd/test_fuzz_isis_tlv_tests.h: $(top_srcdir)/tests/isisd/test_fuzz_isis_tlv_tests.h.gz gzip -d < $(top_srcdir)/tests/isisd/test_fuzz_isis_tlv_tests.h.gz > "$@" -isisd/isisd_test_fuzz_isis_tlv-test_fuzz_isis_tlv.$(OBJEXT): \ - isisd/test_fuzz_isis_tlv_tests.h noinst_HEADERS = \ ./helpers/c/prng.h \ @@ -146,6 +144,9 @@ bgpd_test_ecommunity_SOURCES = bgpd/test_ecommunity.c bgpd_test_mp_attr_SOURCES = bgpd/test_mp_attr.c bgpd_test_mpath_SOURCES = bgpd/test_mpath.c isisd_test_fuzz_isis_tlv_SOURCES = isisd/test_fuzz_isis_tlv.c +nodist_isisd_test_fuzz_isis_tlv_SOURCES = isisd/test_fuzz_isis_tlv_tests.h +BUILT_SOURCES=isisd/test_fuzz_isis_tlv_tests.h +CLEANFILES=isisd/test_fuzz_isis_tlv_tests.h isisd_test_fuzz_isis_tlv_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_builddir)/tests/isisd isisd_test_isis_vertex_queue_SOURCES = isisd/test_isis_vertex_queue.c diff --git a/tools/start-stop-daemon.c b/tools/start-stop-daemon.c index 8c3fe0c3c5..de58e0a20e 100644 --- a/tools/start-stop-daemon.c +++ b/tools/start-stop-daemon.c @@ -1024,8 +1024,10 @@ int main(int argc, char **argv) close(i); /* change tty */ fd = open("/dev/tty", O_RDWR); - ioctl(fd, TIOCNOTTY, 0); - close(fd); + if (fd >= 0) { + ioctl(fd, TIOCNOTTY, 0); + close(fd); + } chdir("/"); umask(022); /* set a default for dumb programs */ setpgid(0, 0); /* set the process group */ diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index 309493b13e..b56eaa899f 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -2415,10 +2415,11 @@ DEFUNSH(VTYSH_ALL, vtysh_log_syslog, vtysh_log_syslog_cmd, } DEFUNSH(VTYSH_ALL, no_vtysh_log_syslog, no_vtysh_log_syslog_cmd, - "no log syslog [LEVEL]", NO_STR + "no log syslog [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]", + NO_STR "Logging control\n" "Cancel logging to syslog\n" - "Logging level\n") + LOG_LEVEL_DESC) { return CMD_SUCCESS; } @@ -2440,24 +2441,6 @@ DEFUNSH(VTYSH_ALL, no_vtysh_log_facility, no_vtysh_log_facility_cmd, return CMD_SUCCESS; } -DEFUNSH_DEPRECATED( - VTYSH_ALL, vtysh_log_trap, vtysh_log_trap_cmd, - "log trap <emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>", - "Logging control\n" - "(Deprecated) Set logging level and default for all destinations\n" LOG_LEVEL_DESC) -{ - return CMD_SUCCESS; -} - -DEFUNSH_DEPRECATED(VTYSH_ALL, no_vtysh_log_trap, no_vtysh_log_trap_cmd, - "no log trap [LEVEL]", NO_STR - "Logging control\n" - "Permit all logging information\n" - "Logging level\n") -{ - return CMD_SUCCESS; -} - DEFUNSH(VTYSH_ALL, vtysh_log_record_priority, vtysh_log_record_priority_cmd, "log record-priority", "Logging control\n" @@ -2634,8 +2617,13 @@ static void backup_config_file(const char *fbackup) strcat(integrate_sav, CONF_BACKUP_EXT); /* Move current configuration file to backup config file. */ - unlink(integrate_sav); - rename(fbackup, integrate_sav); + if (unlink(integrate_sav) != 0) { + vty_out(vty, "Warning: %s unlink failed\n", integrate_sav); + } + if (rename(fbackup, integrate_sav) != 0) { + vty_out(vty, "Error renaming %s to %s\n", fbackup, + integrate_sav); + } free(integrate_sav); } @@ -3369,9 +3357,12 @@ static void vtysh_autocomplete(vector comps, struct cmd_token *token) snprintf(accmd, sizeof(accmd), "autocomplete %d %s %s", token->type, token->text, token->varname ? token->varname : "-"); + vty->of_saved = vty->of; + vty->of = NULL; for (i = 0; i < array_size(vtysh_client); i++) vtysh_client_run_all(&vtysh_client[i], accmd, 1, vtysh_ac_line, comps); + vty->of = vty->of_saved; } static const struct cmd_variable_handler vtysh_var_handler[] = { @@ -3760,8 +3751,6 @@ void vtysh_init_vty(void) install_element(CONFIG_NODE, &no_vtysh_log_monitor_cmd); install_element(CONFIG_NODE, &vtysh_log_syslog_cmd); install_element(CONFIG_NODE, &no_vtysh_log_syslog_cmd); - install_element(CONFIG_NODE, &vtysh_log_trap_cmd); - install_element(CONFIG_NODE, &no_vtysh_log_trap_cmd); install_element(CONFIG_NODE, &vtysh_log_facility_cmd); install_element(CONFIG_NODE, &no_vtysh_log_facility_cmd); install_element(CONFIG_NODE, &vtysh_log_record_priority_cmd); diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c index e6d324ab6a..5c84219418 100644 --- a/zebra/if_netlink.c +++ b/zebra/if_netlink.c @@ -586,8 +586,13 @@ static int netlink_interface(struct nlmsghdr *h, ns_id_t ns_id, int startup) return 0; len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct ifinfomsg)); - if (len < 0) + if (len < 0) { + zlog_err("%s: Message received from netlink is of a broken size: %d %zu", + __PRETTY_FUNCTION__, + h->nlmsg_len, + (size_t)NLMSG_LENGTH(sizeof(struct ifinfomsg))); return -1; + } /* We are interested in some AF_BRIDGE notifications. */ if (ifi->ifi_family == AF_BRIDGE) @@ -893,8 +898,13 @@ int netlink_interface_addr(struct nlmsghdr *h, ns_id_t ns_id, int startup) return 0; len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct ifaddrmsg)); - if (len < 0) + if (len < 0) { + zlog_err("%s: Message received from netlink is of a broken size: %d %zu", + __PRETTY_FUNCTION__, + h->nlmsg_len, + (size_t)NLMSG_LENGTH(sizeof(struct ifaddrmsg))); return -1; + } memset(tb, 0, sizeof tb); netlink_parse_rtattr(tb, IFA_MAX, IFA_RTA(ifa), len); @@ -1105,8 +1115,12 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) } len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct ifinfomsg)); - if (len < 0) + if (len < 0) { + zlog_err("%s: Message received from netlink is of a broken size %d %zu", + __PRETTY_FUNCTION__, h->nlmsg_len, + (size_t)NLMSG_LENGTH(sizeof(struct ifinfomsg))); return -1; + } /* We are interested in some AF_BRIDGE notifications. */ if (ifi->ifi_family == AF_BRIDGE) diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c index 0e79b82533..d9c6631845 100644 --- a/zebra/kernel_netlink.c +++ b/zebra/kernel_netlink.c @@ -498,6 +498,75 @@ const char *nl_rttype_to_str(uint8_t rttype) return lookup_msg(rttype_str, rttype, ""); } +#define NL_OK(nla, len) \ + ((len) >= (int)sizeof(struct nlattr) \ + && (nla)->nla_len >= sizeof(struct nlattr) \ + && (nla)->nla_len <= (len)) +#define NL_NEXT(nla, attrlen) \ + ((attrlen) -= RTA_ALIGN((nla)->nla_len), \ + (struct nlattr *)(((char *)(nla)) + RTA_ALIGN((nla)->nla_len))) +#define NL_RTA(r) \ + ((struct nlattr *)(((char *)(r)) \ + + NLMSG_ALIGN(sizeof(struct nlmsgerr)))) + +static void netlink_parse_nlattr(struct nlattr **tb, int max, + struct nlattr *nla, int len) +{ + while (NL_OK(nla, len)) { + if (nla->nla_type <= max) + tb[nla->nla_type] = nla; + nla = NL_NEXT(nla, len); + } +} + +static void netlink_parse_extended_ack(struct nlmsghdr *h) +{ + struct nlattr *tb[NLMSGERR_ATTR_MAX + 1]; + const struct nlmsgerr *err = + (const struct nlmsgerr *)((uint8_t *)h + + NLMSG_ALIGN( + sizeof(struct nlmsghdr))); + const struct nlmsghdr *err_nlh = NULL; + uint32_t hlen = sizeof(*err); + const char *msg = NULL; + uint32_t off = 0; + + if (!(h->nlmsg_flags & NLM_F_CAPPED)) + hlen += h->nlmsg_len - NLMSG_ALIGN(sizeof(struct nlmsghdr)); + + memset(tb, 0, sizeof(tb)); + netlink_parse_nlattr(tb, NLMSGERR_ATTR_MAX, NL_RTA(h), hlen); + + if (tb[NLMSGERR_ATTR_MSG]) + msg = (const char *)RTA_DATA(tb[NLMSGERR_ATTR_MSG]); + + if (tb[NLMSGERR_ATTR_OFFS]) { + off = *(uint32_t *)RTA_DATA(tb[NLMSGERR_ATTR_OFFS]); + + if (off > h->nlmsg_len) { + zlog_err("Invalid offset for NLMSGERR_ATTR_OFFS\n"); + } else if (!(h->nlmsg_flags & NLM_F_CAPPED)) { + /* + * Header of failed message + * we are not doing anything currently with it + * but noticing it for later. + */ + err_nlh = &err->msg; + zlog_warn("%s: Received %d extended Ack", + __PRETTY_FUNCTION__, err_nlh->nlmsg_type); + } + } + + if (msg && *msg != '\0') { + bool is_err = !!err->error; + + if (is_err) + zlog_err("Extended Error: %s", msg); + else + zlog_warn("Extended Warning: %s", msg); + } +} + /* * netlink_parse_info * @@ -582,6 +651,23 @@ int netlink_parse_info(int (*filter)(struct nlmsghdr *, ns_id_t, int), int errnum = err->error; int msg_type = err->msg.nlmsg_type; + if (h->nlmsg_len + < NLMSG_LENGTH(sizeof(struct nlmsgerr))) { + zlog_err("%s error: message truncated", + nl->name); + return -1; + } + + /* + * Parse the extended information before + * we actually handle it. + * At this point in time we do not + * do anything other than report the + * issue. + */ + if (h->nlmsg_flags & NLM_F_ACK_TLVS) + netlink_parse_extended_ack(h); + /* If the error field is zero, then this is an * ACK */ if (err->error == 0) { @@ -603,13 +689,6 @@ int netlink_parse_info(int (*filter)(struct nlmsghdr *, ns_id_t, int), continue; } - if (h->nlmsg_len - < NLMSG_LENGTH(sizeof(struct nlmsgerr))) { - zlog_err("%s error: message truncated", - nl->name); - return -1; - } - /* Deal with errors that occur because of races * in link handling */ if (nl == &zns->netlink_cmd @@ -692,6 +771,7 @@ int netlink_parse_info(int (*filter)(struct nlmsghdr *, ns_id_t, int), error = (*filter)(h, zns->ns_id, startup); if (error < 0) { zlog_err("%s filter function error", nl->name); + zlog_backtrace(LOG_ERR); ret = error; } } @@ -836,6 +916,9 @@ int netlink_request(struct nlsock *nl, struct nlmsghdr *n) void kernel_init(struct zebra_ns *zns) { unsigned long groups; +#if defined SOL_NETLINK + int one, ret; +#endif /* * Initialize netlink sockets @@ -866,6 +949,25 @@ void kernel_init(struct zebra_ns *zns) zns->netlink_cmd.sock = -1; netlink_socket(&zns->netlink_cmd, 0, zns->ns_id); + /* + * SOL_NETLINK is not available on all platforms yet + * apparently. It's in bits/socket.h which I am not + * sure that we want to pull into our build system. + */ +#if defined SOL_NETLINK + /* + * Let's tell the kernel that we want to receive extended + * ACKS over our command socket + */ + one = 1; + ret = setsockopt(zns->netlink_cmd.sock, SOL_NETLINK, NETLINK_EXT_ACK, + &one, sizeof(one)); + + if (ret < 0) + zlog_notice("Registration for extended ACK failed : %d %s", + errno, safe_strerror(errno)); +#endif + /* Register kernel socket. */ if (zns->netlink.sock > 0) { /* Only want non-blocking on the netlink event socket */ @@ -880,6 +982,7 @@ void kernel_init(struct zebra_ns *zns) netlink_install_filter(zns->netlink.sock, zns->netlink_cmd.snl.nl_pid); zns->t_netlink = NULL; + thread_add_read(zebrad.master, kernel_read, zns, zns->netlink.sock, &zns->t_netlink); } diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index a5f288f541..9033491549 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -295,8 +295,12 @@ static int netlink_route_change_read_unicast(struct nlmsghdr *h, ns_id_t ns_id, } len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct rtmsg)); - if (len < 0) + if (len < 0) { + zlog_err("%s: Message received from netlink is of a broken size %d %zu", + __PRETTY_FUNCTION__, h->nlmsg_len, + (size_t)NLMSG_LENGTH(sizeof(struct rtmsg))); return -1; + } memset(tb, 0, sizeof tb); netlink_parse_rtattr(tb, RTA_MAX, RTM_RTA(rtm), len); @@ -747,8 +751,13 @@ int netlink_route_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) return 0; len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct rtmsg)); - if (len < 0) + if (len < 0) { + zlog_err("%s: Message received from netlink is of a broken size: %d %zu", + __PRETTY_FUNCTION__, + h->nlmsg_len, + (size_t)NLMSG_LENGTH(sizeof(struct rtmsg))); return -1; + } if (rtm->rtm_type == RTN_MULTICAST) netlink_route_change_read_multicast(h, ns_id, startup); @@ -2356,8 +2365,12 @@ int netlink_neigh_change(struct nlmsghdr *h, ns_id_t ns_id) /* Length validity. */ len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct ndmsg)); - if (len < 0) + if (len < 0) { + zlog_err("%s: Message received from netlink is of a broken size %d %zu", + __PRETTY_FUNCTION__, h->nlmsg_len, + (size_t)NLMSG_LENGTH(sizeof(struct ndmsg))); return -1; + } /* Is this a notification for the MAC FDB or IP neighbor table? */ ndm = NLMSG_DATA(h); diff --git a/zebra/rule_netlink.c b/zebra/rule_netlink.c index bcffdf4722..c7a8517e17 100644 --- a/zebra/rule_netlink.c +++ b/zebra/rule_netlink.c @@ -196,8 +196,12 @@ int netlink_rule_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) return 0; len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct fib_rule_hdr)); - if (len < 0) + if (len < 0) { + zlog_err("%s: Message received from netlink is of a broken size: %d %zu", + __PRETTY_FUNCTION__, h->nlmsg_len, + (size_t)NLMSG_LENGTH(sizeof(struct fib_rule_hdr))); return -1; + } frh = NLMSG_DATA(h); if (frh->family != AF_INET && frh->family != AF_INET6) diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index 853a83373d..6e0d86d668 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -521,6 +521,7 @@ int zsend_redistribute_route(int cmd, struct zserv *client, struct prefix *p, struct zapi_nexthop *api_nh; struct nexthop *nexthop; int count = 0; + afi_t afi; memset(&api, 0, sizeof(api)); api.vrf_id = re->vrf_id; @@ -528,6 +529,24 @@ int zsend_redistribute_route(int cmd, struct zserv *client, struct prefix *p, api.instance = re->instance; api.flags = re->flags; + afi = family2afi(p->family); + switch (afi) { + case AFI_IP: + if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD) + client->redist_v4_add_cnt++; + else + client->redist_v4_del_cnt++; + break; + case AFI_IP6: + if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD) + client->redist_v6_add_cnt++; + else + client->redist_v6_del_cnt++; + break; + default: + break; + } + /* Prefix. */ api.prefix = *p; if (src_p) { @@ -2896,9 +2915,9 @@ static inline void zread_ipset_entry(ZAPI_HANDLER_ARGS) if (!is_default_prefix(&zpi.dst)) zpi.filter_bm |= PBR_FILTER_DST_IP; - if (zpi.dst_port_min != 0) + if (zpi.dst_port_min != 0 || zpi.proto == IPPROTO_ICMP) zpi.filter_bm |= PBR_FILTER_DST_PORT; - if (zpi.src_port_min != 0) + if (zpi.src_port_min != 0 || zpi.proto == IPPROTO_ICMP) zpi.filter_bm |= PBR_FILTER_SRC_PORT; if (zpi.dst_port_max != 0) zpi.filter_bm |= PBR_FILTER_DST_PORT_RANGE; @@ -2938,6 +2957,12 @@ static inline void zread_iptable(ZAPI_HANDLER_ARGS) STREAM_GETL(s, zpi.action); STREAM_GETL(s, zpi.fwmark); STREAM_GET(&zpi.ipset_name, s, ZEBRA_IPSET_NAME_SIZE); + STREAM_GETW(s, zpi.pkt_len_min); + STREAM_GETW(s, zpi.pkt_len_max); + STREAM_GETW(s, zpi.tcp_flags); + STREAM_GETW(s, zpi.tcp_mask_flags); + STREAM_GETC(s, zpi.dscp_value); + STREAM_GETC(s, zpi.fragment); STREAM_GETL(s, zpi.nb_interface); zebra_pbr_iptable_update_interfacelist(s, &zpi); diff --git a/zebra/zebra_netns_id.c b/zebra/zebra_netns_id.c index 317b02f601..96e6df34da 100644 --- a/zebra/zebra_netns_id.c +++ b/zebra/zebra_netns_id.c @@ -307,7 +307,7 @@ ns_id_t zebra_ns_id_get(const char *netnspath) nlh = (struct nlmsghdr *)((char *)nlh + NETLINK_ALIGN( nlh->nlmsg_len)); - } while (len != 0 && return_nsid != NS_UNKNOWN && ret == 0); + } while (len != 0 && ret == 0); } close(fd); diff --git a/zebra/zebra_netns_notify.c b/zebra/zebra_netns_notify.c index 30f850597c..d0ea661403 100644 --- a/zebra/zebra_netns_notify.c +++ b/zebra/zebra_netns_notify.c @@ -212,6 +212,18 @@ static int zebra_ns_notify_read(struct thread *t) continue; if (event->mask & IN_DELETE) return zebra_ns_delete(event->name); + + if (offsetof(struct inotify_event, name) + event->len + >= sizeof(buf)) { + zlog_err("NS notify read: buffer underflow"); + break; + } + + if (strnlen(event->name, event->len) == event->len) { + zlog_err("NS notify error: bad event name"); + break; + } + netnspath = ns_netns_pathname(NULL, event->name); if (!netnspath) continue; diff --git a/zebra/zebra_pbr.c b/zebra/zebra_pbr.c index d511c8c6ec..74ef25b031 100644 --- a/zebra/zebra_pbr.c +++ b/zebra/zebra_pbr.c @@ -44,6 +44,63 @@ static const struct message ipset_type_msg[] = { {0} }; +const struct message icmp_typecode_str[] = { + { 0 << 8, "echo-reply"}, + { 0 << 8, "pong"}, + { 3 << 8, "network-unreachable"}, + { (3 << 8) + 1, "host-unreachable"}, + { (3 << 8) + 2, "protocol-unreachable"}, + { (3 << 8) + 3, "port-unreachable"}, + { (3 << 8) + 4, "fragmentation-needed"}, + { (3 << 8) + 5, "source-route-failed"}, + { (3 << 8) + 6, "network-unknown"}, + { (3 << 8) + 7, "host-unknown"}, + { (3 << 8) + 9, "network-prohibited"}, + { (3 << 8) + 10, "host-prohibited"}, + { (3 << 8) + 11, "TOS-network-unreachable"}, + { (3 << 8) + 12, "TOS-host-unreachable"}, + { (3 << 8) + 13, "communication-prohibited"}, + { (3 << 8) + 14, "host-precedence-violation"}, + { (3 << 8) + 15, "precedence-cutoff"}, + { 4 << 8, "source-quench"}, + { 5 << 8, "network-redirect"}, + { (5 << 8) + 1, "host-redirect"}, + { (5 << 8) + 2, "TOS-network-redirect"}, + { (5 << 8) + 3, "TOS-host-redirect"}, + { 8 << 8, "echo-request"}, + { 8 << 8, "ping"}, + { 9 << 8, "router-advertisement"}, + { 10 << 8, "router-solicitation"}, + { 11 << 8, "ttl-zero-during-transit"}, + { (11 << 8) + 1, "ttl-zero-during-reassembly"}, + { 12 << 8, "ip-header-bad"}, + { (12 << 8) + 1, "required-option-missing"}, + { 13 << 8, "timestamp-request"}, + { 14 << 8, "timestamp-reply"}, + { 17 << 8, "address-mask-request"}, + { 18 << 8, "address-mask-reply"}, + {0} +}; + +/* definitions */ +static const struct message tcp_value_str[] = { + {TCP_HEADER_FIN, "FIN"}, + {TCP_HEADER_SYN, "SYN"}, + {TCP_HEADER_RST, "RST"}, + {TCP_HEADER_PSH, "PSH"}, + {TCP_HEADER_ACK, "ACK"}, + {TCP_HEADER_URG, "URG"}, + {0} +}; + +static const struct message fragment_value_str[] = { + {1, "dont-fragment"}, + {2, "is-fragment"}, + {4, "first-fragment"}, + {8, "last-fragment"}, + {0} +}; + /* static function declarations */ DEFINE_HOOK(zebra_pbr_ipset_entry_wrap_script_get_stat, (struct zebra_ns *zns, struct zebra_pbr_ipset_entry *ipset, @@ -322,6 +379,12 @@ uint32_t zebra_pbr_iptable_hash_key(void *arg) key = jhash2(pnt, ZEBRA_IPSET_NAME_HASH_SIZE, 0x63ab42de); key = jhash_1word(iptable->fwmark, key); + key = jhash_1word(iptable->pkt_len_min, key); + key = jhash_1word(iptable->pkt_len_max, key); + key = jhash_1word(iptable->tcp_flags, key); + key = jhash_1word(iptable->tcp_mask_flags, key); + key = jhash_1word(iptable->dscp_value, key); + key = jhash_1word(iptable->fragment, key); return jhash_3words(iptable->filter_bm, iptable->type, iptable->unique, key); } @@ -346,6 +409,18 @@ int zebra_pbr_iptable_hash_equal(const void *arg1, const void *arg2) if (strncmp(r1->ipset_name, r2->ipset_name, ZEBRA_IPSET_NAME_SIZE)) return 0; + if (r1->pkt_len_min != r2->pkt_len_min) + return 0; + if (r1->pkt_len_max != r2->pkt_len_max) + return 0; + if (r1->tcp_flags != r2->tcp_flags) + return 0; + if (r1->tcp_mask_flags != r2->tcp_mask_flags) + return 0; + if (r1->dscp_value != r2->dscp_value) + return 0; + if (r1->fragment != r2->fragment) + return 0; return 1; } @@ -515,7 +590,7 @@ struct pbr_ipset_name_lookup { char ipset_name[ZEBRA_IPSET_NAME_SIZE]; }; -static const char *zebra_pbr_ipset_type2str(uint32_t type) +const char *zebra_pbr_ipset_type2str(uint32_t type) { return lookup_msg(ipset_type_msg, type, "Unrecognized IPset Type"); @@ -773,6 +848,30 @@ static const char *zebra_pbr_prefix2str(union prefixconstptr pu, return prefix2str(pu, str, size); } +static void zebra_pbr_display_icmp(struct vty *vty, + struct zebra_pbr_ipset_entry *zpie) +{ + char decoded_str[20]; + uint16_t port; + + /* range icmp type */ + if (zpie->src_port_max || zpie->dst_port_max) { + vty_out(vty, ":icmp:[type <%d:%d>;code <%d:%d>", + zpie->src_port_min, zpie->src_port_max, + zpie->dst_port_min, zpie->dst_port_max); + } else { + port = ((zpie->src_port_min << 8) & 0xff00) + + (zpie->dst_port_min & 0xff); + memset(decoded_str, 0, sizeof(decoded_str)); + sprintf(decoded_str, "%d/%d", + zpie->src_port_min, + zpie->dst_port_min); + vty_out(vty, ":icmp:%s", + lookup_msg(icmp_typecode_str, + port, decoded_str)); + } +} + static void zebra_pbr_display_port(struct vty *vty, uint32_t filter_bm, uint16_t port_min, uint16_t port_max, uint8_t proto) @@ -816,7 +915,8 @@ static int zebra_pbr_show_ipset_entry_walkcb(struct hash_backet *backet, zebra_pbr_prefix2str(&(zpie->src), buf, sizeof(buf)); vty_out(vty, "\tfrom %s", buf); - if (zpie->filter_bm & PBR_FILTER_SRC_PORT) + if (zpie->filter_bm & PBR_FILTER_SRC_PORT && + zpie->proto != IPPROTO_ICMP) zebra_pbr_display_port(vty, zpie->filter_bm, zpie->src_port_min, zpie->src_port_max, @@ -824,11 +924,14 @@ static int zebra_pbr_show_ipset_entry_walkcb(struct hash_backet *backet, vty_out(vty, " to "); zebra_pbr_prefix2str(&(zpie->dst), buf, sizeof(buf)); vty_out(vty, "%s", buf); - if (zpie->filter_bm & PBR_FILTER_DST_PORT) + if (zpie->filter_bm & PBR_FILTER_DST_PORT && + zpie->proto != IPPROTO_ICMP) zebra_pbr_display_port(vty, zpie->filter_bm, zpie->dst_port_min, zpie->dst_port_max, zpie->proto); + if (zpie->proto == IPPROTO_ICMP) + zebra_pbr_display_icmp(vty, zpie); } else if ((zpi->type == IPSET_NET) || (zpi->type == IPSET_NET_PORT)) { char buf[PREFIX_STRLEN]; @@ -837,7 +940,8 @@ static int zebra_pbr_show_ipset_entry_walkcb(struct hash_backet *backet, zebra_pbr_prefix2str(&(zpie->src), buf, sizeof(buf)); vty_out(vty, "\tfrom %s", buf); } - if (zpie->filter_bm & PBR_FILTER_SRC_PORT) + if (zpie->filter_bm & PBR_FILTER_SRC_PORT && + zpie->proto != IPPROTO_ICMP) zebra_pbr_display_port(vty, zpie->filter_bm, zpie->src_port_min, zpie->src_port_max, @@ -846,11 +950,14 @@ static int zebra_pbr_show_ipset_entry_walkcb(struct hash_backet *backet, zebra_pbr_prefix2str(&(zpie->dst), buf, sizeof(buf)); vty_out(vty, "\tto %s", buf); } - if (zpie->filter_bm & PBR_FILTER_DST_PORT) + if (zpie->filter_bm & PBR_FILTER_DST_PORT && + zpie->proto != IPPROTO_ICMP) zebra_pbr_display_port(vty, zpie->filter_bm, zpie->dst_port_min, zpie->dst_port_max, zpie->proto); + if (zpie->proto == IPPROTO_ICMP) + zebra_pbr_display_icmp(vty, zpie); } vty_out(vty, " (%u)\n", zpie->unique); @@ -882,6 +989,26 @@ static int zebra_pbr_show_ipset_walkcb(struct hash_backet *backet, void *arg) return HASHWALK_CONTINUE; } +size_t zebra_pbr_tcpflags_snprintf(char *buffer, size_t len, + uint16_t tcp_val) +{ + size_t len_written = 0; + static struct message nt = {0}; + const struct message *pnt; + int incr = 0; + + for (pnt = tcp_value_str; + memcmp(pnt, &nt, sizeof(struct message)); pnt++) + if (pnt->key & tcp_val) { + len_written += snprintf(buffer + len_written, + len - len_written, + "%s%s", incr ? + ",":"", pnt->str); + incr++; + } + return len_written; +} + /* */ void zebra_pbr_show_ipset_list(struct vty *vty, char *ipsetname) @@ -946,7 +1073,42 @@ static int zebra_pbr_show_iptable_walkcb(struct hash_backet *backet, void *arg) vty_out(vty, "IPtable %s action %s (%u)\n", iptable->ipset_name, iptable->action == ZEBRA_IPTABLES_DROP ? "drop" : "redirect", iptable->unique); - + if (iptable->pkt_len_min || iptable->pkt_len_max) { + if (!iptable->pkt_len_max) + vty_out(vty, "\t pkt len %u\n", + iptable->pkt_len_min); + else + vty_out(vty, "\t pkt len [%u;%u]\n", + iptable->pkt_len_min, + iptable->pkt_len_max); + } + if (iptable->tcp_flags || iptable->tcp_mask_flags) { + char tcp_flag_str[64]; + char tcp_flag_mask_str[64]; + + zebra_pbr_tcpflags_snprintf(tcp_flag_str, + sizeof(tcp_flag_str), + iptable->tcp_flags); + zebra_pbr_tcpflags_snprintf(tcp_flag_mask_str, + sizeof(tcp_flag_mask_str), + iptable->tcp_mask_flags); + vty_out(vty, "\t tcpflags [%s/%s]\n", + tcp_flag_str, tcp_flag_mask_str); + } + if (iptable->filter_bm & (MATCH_DSCP_SET | MATCH_DSCP_INVERSE_SET)) { + vty_out(vty, "\t dscp %s %d\n", + iptable->filter_bm & MATCH_DSCP_INVERSE_SET ? + "not" : "", iptable->dscp_value); + } + if (iptable->fragment) { + char val_str[10]; + + sprintf(val_str, "%d", iptable->fragment); + vty_out(vty, "\t fragment%s %s\n", + iptable->filter_bm & MATCH_FRAGMENT_INVERSE_SET ? + " not" : "", lookup_msg(fragment_value_str, + iptable->fragment, val_str)); + } ret = hook_call(zebra_pbr_iptable_wrap_script_get_stat, zns, iptable, &pkts, &bytes); if (ret && pkts > 0) diff --git a/zebra/zebra_pbr.h b/zebra/zebra_pbr.h index 6cbafd6daa..fd83502ae1 100644 --- a/zebra/zebra_pbr.h +++ b/zebra/zebra_pbr.h @@ -91,8 +91,10 @@ struct zebra_pbr_ipset_entry { struct prefix src; struct prefix dst; + /* udp/tcp src port or icmp type */ uint16_t src_port_min; uint16_t src_port_max; + /* udp/tcp dst port or icmp code */ uint16_t dst_port_min; uint16_t dst_port_max; @@ -131,6 +133,13 @@ struct zebra_pbr_iptable { uint32_t action; + uint16_t pkt_len_min; + uint16_t pkt_len_max; + uint16_t tcp_flags; + uint16_t tcp_mask_flags; + uint8_t dscp_value; + uint8_t fragment; + uint32_t nb_interface; struct list *interface_name_list; @@ -138,6 +147,10 @@ struct zebra_pbr_iptable { char ipset_name[ZEBRA_IPSET_NAME_SIZE]; }; +extern const struct message icmp_typecode_str[]; + +const char *zebra_pbr_ipset_type2str(uint32_t type); + void zebra_pbr_add_rule(struct zebra_ns *zns, struct zebra_pbr_rule *rule); void zebra_pbr_del_rule(struct zebra_ns *zns, struct zebra_pbr_rule *rule); void zebra_pbr_create_ipset(struct zebra_ns *zns, @@ -225,6 +238,8 @@ extern void zebra_pbr_show_ipset_list(struct vty *vty, char *ipsetname); extern void zebra_pbr_show_iptable(struct vty *vty); extern void zebra_pbr_iptable_update_interfacelist(struct stream *s, struct zebra_pbr_iptable *zpi); +size_t zebra_pbr_tcpflags_snprintf(char *buffer, size_t len, + uint16_t tcp_val); DECLARE_HOOK(zebra_pbr_ipset_entry_wrap_script_get_stat, (struct zebra_ns *zns, struct zebra_pbr_ipset_entry *ipset, diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index bd00823ed8..8935956b25 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -1428,18 +1428,14 @@ static void rib_process_update_fib(struct zebra_vrf *zvrf, if (new != old) zlog_debug( "%u:%s: Deleting route rn %p, re %p (type %d) " - "old %p (type %d) - %s", + "old %p (type %d) - nexthop inactive", zvrf_id(zvrf), buf, rn, new, - new->type, old, old->type, - nh_active ? "install failed" - : "nexthop inactive"); + new->type, old, old->type); else zlog_debug( - "%u:%s: Deleting route rn %p, re %p (type %d) - %s", + "%u:%s: Deleting route rn %p, re %p (type %d) - nexthop inactive", zvrf_id(zvrf), buf, rn, new, - new->type, - nh_active ? "install failed" - : "nexthop inactive"); + new->type); } /* If labeled-unicast route, uninstall transit LSP. */ diff --git a/zebra/zserv.c b/zebra/zserv.c index 14e0db40bf..b297f75ed9 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -198,7 +198,7 @@ static int zserv_write(struct thread *thread) { struct zserv *client = THREAD_ARG(thread); struct stream *msg; - uint32_t wcmd; + uint32_t wcmd = 0; struct stream_fifo *cache; /* If we have any data pending, try to flush it first */ @@ -939,6 +939,11 @@ static void zebra_show_client_detail(struct vty *vty, struct zserv *client) vty_out(vty, "MAC-IP add notifications: %d\n", client->macipadd_cnt); vty_out(vty, "MAC-IP delete notifications: %d\n", client->macipdel_cnt); +#if defined DEV_BUILD + vty_out(vty, "Input Fifo: %zu:%zu Output Fifo: %zu:%zu\n", + client->ibuf_fifo->count, client->ibuf_fifo->max_count, + client->obuf_fifo->count, client->obuf_fifo->max_count); +#endif vty_out(vty, "\n"); return; } |
