diff options
61 files changed, 2097 insertions, 642 deletions
diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index 0557bbcce9..e2cf094cc1 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); } @@ -5215,7 +5216,7 @@ int bgp_evpn_local_l3vni_add(vni_t l3vni, vrf_id_t vrf_id, struct ethaddr *rmac, struct bgpevpn *vpn = NULL; as_t as = 0; - /* get the default instamce - required to get the AS number for VRF + /* get the default instance - required to get the AS number for VRF * auto-creatio */ bgp_def = bgp_get_default(); @@ -5318,11 +5319,12 @@ int bgp_evpn_local_l3vni_del(vni_t l3vni, vrf_id_t vrf_id) return -1; } - /* unimport remote routes from VRF, if it is AUTO vrf bgp_delete will - * take care of uninstalling the routes from zebra + /* Remove remote routes from BGT VRF even if BGP_VRF_AUTO is configured, + * bgp_delete would not remove/decrement bgp_info of the ip_prefix + * routes. This will uninstalling the routes from zebra and decremnt the + * bgp info count. */ - if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_AUTO)) - uninstall_routes_for_vrf(bgp_vrf); + uninstall_routes_for_vrf(bgp_vrf); /* delete/withdraw all type-5 routes */ delete_withdraw_vrf_routes(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..c695e7f125 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) { @@ -274,7 +274,7 @@ void route_vty_out_flowspec(struct vty *vty, struct prefix *p, else json_nlri_path = json_paths; } - if (display == NLRI_STRING_FORMAT_LARGE) + if (display == NLRI_STRING_FORMAT_LARGE && binfo) vty_out(vty, "BGP flowspec entry: (flags 0x%x)\n", binfo->flags); bgp_fs_nlri_get_string((unsigned char *) 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_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 0b1deba517..60d1b5f313 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -2168,7 +2168,6 @@ struct bgp_process_queue { static void bgp_process_main_one(struct bgp *bgp, struct bgp_node *rn, afi_t afi, safi_t safi) { - struct prefix *p = &rn->p; struct bgp_info *new_select; struct bgp_info *old_select; struct bgp_info_pair old_and_new; @@ -2191,6 +2190,8 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_node *rn, return; } + struct prefix *p = &rn->p; + debug = bgp_debug_bestpath(&rn->p); if (debug) { prefix2str(&rn->p, pfx_buf, sizeof(pfx_buf)); @@ -6282,7 +6283,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", @@ -6290,10 +6290,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 ? @@ -7301,9 +7297,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; @@ -7336,7 +7330,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]; @@ -7366,7 +7359,6 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct prefix *p, } } } -#endif attr = binfo->attr; @@ -8019,14 +8011,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) @@ -8597,9 +8584,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; @@ -8626,7 +8611,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)) @@ -8644,29 +8628,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..db182eef98 100644 --- a/bgpd/bgp_rpki.c +++ b/bgpd/bgp_rpki.c @@ -5,8 +5,8 @@ * Berlin * Copyright (C) 2016-2017 Colin Sames (colin.sames@haw-hamburg.de), for HAW * Hamburg - * Copyright (C) 2017 Marcel Röthke (marcel.roethke@haw-hamburg.de), for HAW - * Hamburg + * Copyright (C) 2017-2018 Marcel Röthke (marcel.roethke@haw-hamburg.de), + * for HAW Hamburg * * This file is part of FRRouting. * @@ -158,9 +158,41 @@ 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); +static void ipv6_addr_to_host_byte_order(const uint32_t *src, uint32_t *dest) +{ + int i; + + for (i = 0; i < 4; i++) + dest[i] = ntohl(src[i]); +} + static route_map_result_t route_match(void *rule, struct prefix *prefix, route_map_object_t type, void *object) { @@ -253,14 +285,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++; } @@ -462,13 +487,11 @@ static int rpki_validate_prefix(struct peer *peer, struct attr *attr, ip_addr_prefix.u.addr4.addr = ntohl(prefix->u.prefix4.s_addr); break; -#ifdef HAVE_IPV6 case AF_INET6: ip_addr_prefix.ver = LRTR_IPV6; ipv6_addr_to_host_byte_order(prefix->u.prefix6.s6_addr32, ip_addr_prefix.u.addr6.addr); break; -#endif /* HAVE_IPV6 */ default: return 0; @@ -517,9 +540,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_vty.c b/bgpd/bgp_vty.c index 3d1fdfd38d..86f3f97c49 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -7072,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); } @@ -7935,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", 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..d21c074ccc 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -1026,12 +1026,16 @@ static inline bgp_peer_sort_t peer_calc_sort(struct peer *peer) else if (peer->as_type == AS_EXTERNAL) return BGP_PEER_EBGP; - else if (peer->as_type == AS_SPECIFIED && peer->as) + else if (peer->as_type == AS_SPECIFIED && peer->as) { + assert(bgp); return (bgp->as == peer->as ? BGP_PEER_IBGP : BGP_PEER_EBGP); + } else { struct peer *peer1; + + assert(peer->group); peer1 = listnode_head(peer->group->peer); if (peer1) @@ -2733,9 +2737,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 491741a35b..8553846c90 100644 --- a/bgpd/rfapi/bgp_rfapi_cfg.c +++ b/bgpd/rfapi/bgp_rfapi_cfg.c @@ -4519,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.c b/bgpd/rfapi/rfapi.c index 177244d277..04531433ac 100644 --- a/bgpd/rfapi/rfapi.c +++ b/bgpd/rfapi/rfapi.c @@ -2336,7 +2336,7 @@ int rfapi_reopen(struct rfapi_descriptor *rfd, struct bgp *bgp) h = bgp->rfapi; - assert(!CHECK_FLAG(h->flags, RFAPI_INCALLBACK)); + assert(h != NULL && !CHECK_FLAG(h->flags, RFAPI_INCALLBACK)); if (CHECK_FLAG(rfd->flags, RFAPI_HD_FLAG_CLOSING_ADMINISTRATIVELY) diff --git a/bgpd/rfapi/rfapi_vty.c b/bgpd/rfapi/rfapi_vty.c index 2f8f132fcd..cd12edbccb 100644 --- a/bgpd/rfapi/rfapi_vty.c +++ b/bgpd/rfapi/rfapi_vty.c @@ -3017,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/bgpd/rfapi/vnc_import_bgp.c b/bgpd/rfapi/vnc_import_bgp.c index 4bc5535e1b..7182e952b5 100644 --- a/bgpd/rfapi/vnc_import_bgp.c +++ b/bgpd/rfapi/vnc_import_bgp.c @@ -2904,6 +2904,8 @@ void vnc_import_bgp_redist_disable(struct bgp *bgp, afi_t afi) struct rfapi_descriptor *rfd; vncHDBgpDirect.peer = bi->peer; + assert(bi->extra); + rfd = bi->extra->vnc.export .rfapi_handle; diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst index 13e0252210..0fb27441b2 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 @@ -2062,8 +2076,8 @@ Displaying Routes by AS Path .. index:: show ip bgp ipv4 vpn .. clicmd:: show ip bgp ipv4 vpn -.. index:: show ipv6 bgp ipv6 vpn -.. clicmd:: show ipv6 bgp ipv6 vpn +.. index:: show ip bgp ipv6 vpn +.. clicmd:: show ip bgp ipv6 vpn Print active IPV4 or IPV6 routes advertised via the VPN SAFI. @@ -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..f6af88cac8 --- /dev/null +++ b/doc/user/flowspec.rst @@ -0,0 +1,351 @@ +.. _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 monitoring & 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 + +.. index:: show ip route table TABLEID +.. clicmd:: show ip route table TABLEID + + ``TABLEID`` is the table number identifier referencing the non standard + routing table used in this example. + +.. index:: [no] debug bgp flowspec +.. clicmd:: [no] debug bgp flowspec + + 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 pbr [error] +.. clicmd:: [no] debug bgp pbr [error] + + 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. + + 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 as demonstrated 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 an ``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 an unexpected crash or other 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/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_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_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/isisd/isis_lsp.c b/isisd/isis_lsp.c index 4dcde0cba6..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; } diff --git a/isisd/isis_spf.c b/isisd/isis_spf.c index 2e2933db33..556f2890cf 100644 --- a/isisd/isis_spf.c +++ b/isisd/isis_spf.c @@ -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/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) @@ -89,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/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(); @@ -479,6 +479,8 @@ static int vty_command(struct vty *vty, char *buf) const char *protocolname; char *cp = NULL; + assert(vty); + /* * Log non empty command lines */ @@ -496,13 +498,13 @@ static int vty_command(struct vty *vty, char *buf) /* format the base vty info */ snprintf(vty_str, sizeof(vty_str), "vty[??]@%s", vty->address); - if (vty) - for (i = 0; i < vector_active(vtyvec); i++) - if (vty == vector_slot(vtyvec, i)) { - snprintf(vty_str, sizeof(vty_str), - "vty[%d]@%s", i, vty->address); - break; - } + + for (i = 0; i < vector_active(vtyvec); i++) + if (vty == vector_slot(vtyvec, i)) { + snprintf(vty_str, sizeof(vty_str), "vty[%d]@%s", + i, vty->address); + break; + } /* format the prompt */ snprintf(prompt_str, sizeof(prompt_str), cmd_prompt(vty->node), diff --git a/lib/workqueue.c b/lib/workqueue.c index 1af51c06c1..39dd142afb 100644 --- a/lib/workqueue.c +++ b/lib/workqueue.c @@ -245,10 +245,11 @@ int work_queue_run(struct thread *thread) char yielded = 0; wq = THREAD_ARG(thread); - wq->thread = NULL; assert(wq); + wq->thread = NULL; + /* calculate cycle granularity: * list iteration == 1 run * listnode processing == 1 cycle 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 5efe702457..7bccc78e00 100644 --- a/ospf6d/ospf6_abr.c +++ b/ospf6d/ospf6_abr.c @@ -871,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; @@ -1291,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_intra.c b/ospf6d/ospf6_intra.c index 7898b10905..0ce08a61e2 100644 --- a/ospf6d/ospf6_intra.c +++ b/ospf6d/ospf6_intra.c @@ -1700,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; @@ -1882,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/ospfd/ospf_flood.c b/ospfd/ospf_flood.c index 820b892176..002c6bba8d 100644 --- a/ospfd/ospf_flood.c +++ b/ospfd/ospf_flood.c @@ -91,7 +91,7 @@ struct external_info *ospf_external_info_check(struct ospf *ospf, p.prefix = lsa->data->id; p.prefixlen = ip_masklen(al->mask); - for (type = 0; type <= ZEBRA_ROUTE_MAX; type++) { + for (type = 0; type < ZEBRA_ROUTE_MAX; type++) { int redist_on = 0; redist_on = diff --git a/ospfd/ospf_lsa.c b/ospfd/ospf_lsa.c index 729e508865..502f233036 100644 --- a/ospfd/ospf_lsa.c +++ b/ospfd/ospf_lsa.c @@ -1889,7 +1889,7 @@ struct ospf_lsa *ospf_translated_nssa_refresh(struct ospf *ospf, zlog_debug( "ospf_translated_nssa_refresh(): no Type-7 found for " "Type-5 LSA Id %s", - inet_ntoa(type5->data->id)); + type5 ? inet_ntoa(type5->data->id) : "(null)"); return NULL; } @@ -1899,7 +1899,7 @@ struct ospf_lsa *ospf_translated_nssa_refresh(struct ospf *ospf, zlog_debug( "ospf_translated_nssa_refresh(): No translated Type-5 " "found for Type-7 with Id %s", - inet_ntoa(type7->data->id)); + type7 ? inet_ntoa(type7->data->id) : "(null)"); return NULL; } @@ -1912,7 +1912,7 @@ struct ospf_lsa *ospf_translated_nssa_refresh(struct ospf *ospf, zlog_debug( "ospf_translated_nssa_refresh(): Could not translate " "Type-7 for %s to Type-5", - inet_ntoa(type7->data->id)); + type7 ? inet_ntoa(type7->data->id) : "(null)"); return NULL; } @@ -1921,7 +1921,7 @@ struct ospf_lsa *ospf_translated_nssa_refresh(struct ospf *ospf, zlog_debug( "ospf_translated_nssa_refresh(): Could not install " "translated LSA, Id %s", - inet_ntoa(type7->data->id)); + type7 ? inet_ntoa(type7->data->id) : "(null)"); return NULL; } 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_vty.c b/ospfd/ospf_vty.c index ddf9133ed9..ec654b9e29 100644 --- a/ospfd/ospf_vty.c +++ b/ospfd/ospf_vty.c @@ -4809,16 +4809,19 @@ static void show_ip_ospf_nbr_nbma_detail_sub(struct vty *vty, vty_out(vty, " Poll interval %d\n", nbr_nbma->v_poll); /* Show poll-interval timer. */ - if (use_json) { - long time_store; - time_store = monotime_until(&nbr_nbma->t_poll->u.sands, NULL) - / 1000LL; - json_object_int_add(json_sub, "pollIntervalTimerDueMsec", - time_store); - } else - vty_out(vty, " Poll timer due in %s\n", - ospf_timer_dump(nbr_nbma->t_poll, timebuf, - sizeof(timebuf))); + if (nbr_nbma->t_poll) { + if (use_json) { + long time_store; + time_store = monotime_until(&nbr_nbma->t_poll->u.sands, + NULL) / 1000LL; + json_object_int_add(json_sub, + "pollIntervalTimerDueMsec", + time_store); + } else + vty_out(vty, " Poll timer due in %s\n", + ospf_timer_dump(nbr_nbma->t_poll, timebuf, + sizeof(timebuf))); + } /* Show poll-interval timer thread. */ if (use_json) { diff --git a/pimd/pim_mroute.c b/pimd/pim_mroute.c index 8462a4fdf8..c7e1a18d28 100644 --- a/pimd/pim_mroute.c +++ b/pimd/pim_mroute.c @@ -152,7 +152,7 @@ static int pim_mroute_msg_nocache(int fd, struct interface *ifp, struct pim_rpf *rpg; struct prefix_sg sg; - rpg = RP(pim_ifp->pim, msg->im_dst); + rpg = pim_ifp ? RP(pim_ifp->pim, msg->im_dst) : NULL; /* * If the incoming interface is unknown OR * the Interface type is SSM we don't need to @@ -278,7 +278,7 @@ static int pim_mroute_msg_wholepkt(int fd, struct interface *ifp, pim_ifp = up->rpf.source_nexthop.interface->info; - rpg = RP(pim_ifp->pim, sg.grp); + rpg = pim_ifp ? RP(pim_ifp->pim, sg.grp) : NULL; if ((pim_rpf_addr_is_inaddr_none(rpg)) || (!pim_ifp) || (!(PIM_I_am_DR(pim_ifp)))) { 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/redhat/frr.spec.in b/redhat/frr.spec.in index 064b78b260..e2be7050d7 100644 --- a/redhat/frr.spec.in +++ b/redhat/frr.spec.in @@ -10,41 +10,43 @@ #################### FRRouting (FRR) configure options ##################### # with-feature options -%{!?with_pam: %global with_pam 0 } -%{!?with_ospfclient: %global with_ospfclient 1 } -%{!?with_ospfapi: %global with_ospfapi 1 } -%{!?with_rtadv: %global with_rtadv 1 } -%{!?with_ldpd: %global with_ldpd 1 } -%{!?with_nhrpd: %global with_nhrpd 1 } -%{!?with_eigrpd: %global with_eigrpd 1 } %{!?with_babeld: %global with_babeld 1 } -%{!?with_shared: %global with_shared 1 } -%{!?with_multipath: %global with_multipath 256 } -%{!?frr_user: %global frr_user frr } -%{!?vty_group: %global vty_group frrvty } -%{!?with_fpm: %global with_fpm 1 } -%{!?with_watchfrr: %global with_watchfrr 1 } %{!?with_bgp_vnc: %global with_bgp_vnc 0 } +%{!?with_cumulus: %global with_cumulus 0 } +%{!?with_eigrpd: %global with_eigrpd 1 } +%{!?with_fpm: %global with_fpm 1 } +%{!?with_ldpd: %global with_ldpd 1 } +%{!?with_multipath: %global with_multipath 256 } +%{!?with_nhrpd: %global with_nhrpd 1 } +%{!?with_ospfapi: %global with_ospfapi 1 } +%{!?with_ospfclient: %global with_ospfclient 1 } +%{!?with_pam: %global with_pam 0 } +%{!?with_pbrd: %global with_pbrd 1 } %{!?with_pimd: %global with_pimd 1 } %{!?with_rpki: %global with_rpki 0 } -%{!?with_pbrd: %global with_pbrd 1 } +%{!?with_rtadv: %global with_rtadv 1 } +%{!?with_watchfrr: %global with_watchfrr 1 } + +# user and group +%{!?frr_user: %global frr_user frr } +%{!?vty_group: %global vty_group frrvty } # path defines -%define _sysconfdir /etc/frr -%define _sbindir /usr/lib/frr -%define zeb_src %{_builddir}/%{name}-%{frrversion} -%define zeb_rh_src %{zeb_src}/redhat -%define zeb_docs %{zeb_src}/doc -%define frr_tools %{zeb_src}/tools +%define configdir %{_sysconfdir}/%{name} +%define _sbindir /usr/lib/frr +%define zeb_src %{_builddir}/%{name}-%{frrversion} +%define zeb_rh_src %{zeb_src}/redhat +%define zeb_docs %{zeb_src}/doc +%define frr_tools %{zeb_src}/tools # defines for configure -%define _localstatedir /var/run/frr +%define rundir %{_localstatedir}/run/%{name} # define for sphinx-build binary %if 0%{?rhel} && 0%{?rhel} < 7 -%define sphinx sphinx-build2.7 + %define sphinx sphinx-build2.7 %else -%define sphinx sphinx-build + %define sphinx sphinx-build %endif ############################################################################ @@ -83,55 +85,52 @@ %{!?frr_gid: %global frr_gid 92 } %{!?vty_gid: %global vty_gid 85 } -%define daemon_list zebra ripd ospfd bgpd isisd ripngd ospf6d pbrd +%define daemon_list zebra ripd ospfd bgpd isisd ripngd ospf6d pbrd %if %{with_ldpd} -%define daemon_ldpd ldpd + %define daemon_ldpd ldpd %else -%define daemon_ldpd "" + %define daemon_ldpd "" %endif %if %{with_pimd} -%define daemon_pimd pimd + %define daemon_pimd pimd %else -%define daemon_pimd "" + %define daemon_pimd "" %endif %if %{with_pbrd} -%define daemon_pbrd pbrd + %define daemon_pbrd pbrd %else -%define daemon_pbrd "" + %define daemon_pbrd "" %endif %if %{with_nhrpd} -%define daemon_nhrpd nhrpd + %define daemon_nhrpd nhrpd %else -%define daemon_nhrpd "" + %define daemon_nhrpd "" %endif %if %{with_eigrpd} -%define daemon_eigrpd eigrpd + %define daemon_eigrpd eigrpd %else -%define daemon_eigrpd "" + %define daemon_eigrpd "" %endif %if %{with_babeld} -%define daemon_babeld babeld + %define daemon_babeld babeld %else -%define daemon_babeld "" + %define daemon_babeld "" %endif %if %{with_watchfrr} -%define daemon_watchfrr watchfrr + %define daemon_watchfrr watchfrr %else -%define daemon_watchfrr "" + %define daemon_watchfrr "" %endif %define all_daemons %{daemon_list} %{daemon_ldpd} %{daemon_pimd} %{daemon_nhrpd} %{daemon_eigrpd} %{daemon_babeld} %{daemon_watchfrr} %{daemon_pbrd} -# allow build dir to be kept -%{!?keep_build: %global keep_build 0 } - #release sub-revision (the two digits after the CONFDATE) %{!?release_rev: %global release_rev 01 } @@ -143,19 +142,27 @@ License: GPLv2+ Group: System Environment/Daemons Source0: https://github.com/FRRouting/frr/archive/%{name}-%{frrversion}.tar.gz URL: https://www.frrouting.org +Requires(pre): shadow-utils Requires(preun): info Requires(post): info -BuildRequires: gcc patch libcap-devel -BuildRequires: readline-devel ncurses-devel -BuildRequires: json-c-devel bison >= 2.7 flex make -BuildRequires: c-ares-devel texinfo +BuildRequires: bison >= 2.7 +BuildRequires: c-ares-devel +BuildRequires: flex +BuildRequires: gcc +BuildRequires: json-c-devel +BuildRequires: libcap-devel +BuildRequires: make +BuildRequires: ncurses-devel +BuildRequires: readline-devel +BuildRequires: texinfo %if 0%{?rhel} && 0%{?rhel} < 7 #python27-devel is available from ius community repo for RedHat/CentOS 6 -BuildRequires: python27-devel python27-sphinx +BuildRequires: python27-devel +BuildRequires: python27-sphinx %else -BuildRequires: python-devel >= 2.7 python-sphinx +BuildRequires: python-devel >= 2.7 +BuildRequires: python-sphinx %endif -Requires: json-c initscripts %if %{with_pam} BuildRequires: pam-devel %endif @@ -163,7 +170,8 @@ BuildRequires: pam-devel BuildRequires: librtr-devel >= 0.5 %endif %if "%{initsystem}" == "systemd" -BuildRequires: systemd systemd-devel +BuildRequires: systemd +BuildRequires: systemd-devel Requires(post): systemd Requires(preun): systemd Requires(postun): systemd @@ -172,12 +180,13 @@ Requires(post): chkconfig Requires(preun): chkconfig # Initscripts > 5.60 is required for IPv6 support Requires(pre): initscripts >= 5.60 +Requires: initscripts %endif Provides: routingdaemon = %{version}-%{release} -BuildRoot: %{_tmppath}/%{name}-%{version}-root Obsoletes: gated mrt zebra frr-sysvinit Conflicts: bird + %description FRRouting is a free software that manages TCP/IP based routing protocol. It takes multi-server and multi-thread approach to resolve @@ -188,6 +197,7 @@ NHRP, Babel, PBR and EIGRP. FRRouting is a fork of Quagga. + %package contrib Summary: contrib tools for frr Group: System Environment/Daemons @@ -195,6 +205,7 @@ Group: System Environment/Daemons %description contrib Contributed/3rd party tools which may be of use with frr. + %package pythontools Summary: python tools for frr BuildRequires: python @@ -204,6 +215,7 @@ Group: System Environment/Daemons %description pythontools Contributed python 2.7 tools which may be of use with frr. + %package devel Summary: Header and object files for frr development Group: System Environment/Daemons @@ -213,8 +225,10 @@ Requires: %{name} = %{version}-%{release} The frr-devel package contains the header and object files neccessary for developing OSPF-API and frr applications. + %prep -%setup -q -n frr-%{frrversion} +%setup -q -n frr-%{frrversion} + %build @@ -230,15 +244,11 @@ developing OSPF-API and frr applications. %configure \ --sbindir=%{_sbindir} \ - --sysconfdir=%{_sysconfdir} \ - --libdir=%{_libdir} \ - --libexecdir=%{_libexecdir} \ - --localstatedir=%{_localstatedir} \ + --sysconfdir=%{configdir} \ + --localstatedir=%{rundir} \ + --disable-static \ --disable-werror \ --enable-irdp \ -%if !%{with_shared} - --disable-shared \ -%endif %if %{with_multipath} --enable-multipath=%{with_multipath} \ %endif @@ -292,11 +302,11 @@ developing OSPF-API and frr applications. --with-libpam \ %endif %if 0%{?frr_user:1} - --enable-user=%frr_user \ - --enable-group=%frr_user \ + --enable-user=%{frr_user} \ + --enable-group=%{frr_user} \ %endif %if 0%{?vty_group:1} - --enable-vty-group=%vty_group \ + --enable-vty-group=%{vty_group} \ %endif %if %{with_fpm} --enable-fpm \ @@ -308,6 +318,9 @@ developing OSPF-API and frr applications. %else --disable-watchfrr \ %endif +%if %{with_cumulus} + --enable-cumulus \ +%endif %if %{with_bgp_vnc} --enable-bgp-vnc \ %else @@ -318,9 +331,10 @@ developing OSPF-API and frr applications. --enable-systemd \ %endif %if %{with_rpki} - --enable-rpki \ + --enable-rpki +%else + --disable-rpki %endif - --enable-poll make %{?_smp_mflags} MAKEINFO="makeinfo --no-split" SPHINXBUILD=%{sphinx} @@ -328,9 +342,10 @@ pushd doc make SPHINXBUILD=%{sphinx} info popd + %install -mkdir -p %{buildroot}/etc/{frr,sysconfig,logrotate.d,pam.d,default} \ - %{buildroot}/var/log/frr %{buildroot}%{_infodir} +mkdir -p %{buildroot}%{_sysconfdir}/{frr,sysconfig,logrotate.d,pam.d,default} \ + %{buildroot}%{_localstatedir}/log/frr %{buildroot}%{_infodir} make DESTDIR=%{buildroot} INSTALL="install -p" CP="cp -p" SPHINXBUILD=%{sphinx} install # Remove this file, as it is uninstalled and causes errors when building on RH9 @@ -339,53 +354,47 @@ rm -rf %{buildroot}/usr/share/info/dir # Remove debian init script if it was installed rm -f %{buildroot}%{_sbindir}/frr -# kill bogus libtool files for modules -rm -f %{buildroot}%{_libdir}/frr/modules/*.la +# kill bogus libtool files +rm -vf %{buildroot}%{_libdir}/frr/modules/*.la +rm -vf %{buildroot}%{_libdir}/*.la # install /etc sources %if "%{initsystem}" == "systemd" mkdir -p %{buildroot}%{_unitdir} -install -m644 %{zeb_rh_src}/frr.service \ - %{buildroot}%{_unitdir}/frr.service -install %{zeb_rh_src}/frr.init \ - %{buildroot}%{_sbindir}/frr +install -m644 %{zeb_rh_src}/frr.service %{buildroot}%{_unitdir}/frr.service +install %{zeb_rh_src}/frr.init %{buildroot}%{_sbindir}/frr %else -mkdir -p %{buildroot}/etc/rc.d/init.d -install %{zeb_rh_src}/frr.init \ - %{buildroot}%{_sbindir}/frr -ln -s %{_sbindir}/frr \ - %{buildroot}/etc/rc.d/init.d/frr +mkdir -p %{buildroot}%{_initddir} +install %{zeb_rh_src}/frr.init %{buildroot}%{_sbindir}/frr +ln -s %{_sbindir}/frr %{buildroot}%{_initddir}/frr %endif -install %{zeb_rh_src}/daemons %{buildroot}/etc/frr -install -m644 %{zeb_rh_src}/frr.pam \ - %{buildroot}/etc/pam.d/frr -install -m644 %{zeb_rh_src}/frr.logrotate \ - %{buildroot}/etc/logrotate.d/frr -install -d -m750 %{buildroot}/var/run/frr +install %{zeb_rh_src}/daemons %{buildroot}%{_sysconfdir}/frr +install -m644 %{zeb_rh_src}/frr.pam %{buildroot}%{_sysconfdir}/pam.d/frr +install -m644 %{zeb_rh_src}/frr.logrotate %{buildroot}%{_sysconfdir}/logrotate.d/frr +install -d -m750 %{buildroot}%{rundir} + %pre # add vty_group %if 0%{?vty_group:1} -if getent group %vty_group > /dev/null ; then : ; else \ - /usr/sbin/groupadd -r -g %vty_gid %vty_group > /dev/null || : ; fi + getent group %{vty_group} >/dev/null || groupadd -r -g %{vty_gid} %{vty_group} %endif # add frr user and group %if 0%{?frr_user:1} -# Ensure that frr_gid gets correctly allocated -if getent group %frr_user >/dev/null; then : ; else \ - /usr/sbin/groupadd -g %frr_gid %frr_user > /dev/null || : ; \ -fi -if getent passwd %frr_user >/dev/null ; then : ; else \ - /usr/sbin/useradd -u %frr_uid -g %frr_gid \ - -M -r -s /sbin/nologin -c "FRRouting suite" \ - -d %_localstatedir %frr_user 2> /dev/null || : ; \ -fi -%if 0%{?vty_group:1} -/usr/sbin/usermod -a -G %vty_group %frr_user -%endif + # Ensure that frr_gid gets correctly allocated + getent group %{frr_user} >/dev/null || groupadd -g %{frr_gid} %{frr_user} + getent passwd %{frr_user} >/dev/null || \ + useradd -r -u %{frr_uid} -g %{frr_user} \ + -s /sbin/nologin -c "FRRouting suite" \ + -d %{rundir} %{frr_user} + + %if 0%{?vty_group:1} + usermod -a -G %{vty_group} %{frr_user} + %endif %endif +exit 0 %post @@ -395,9 +404,9 @@ fi zebra_spec_add_service () { # Add port /etc/services entry if it isn't already there - if [ -f /etc/services ] && \ - ! %__sed -e 's/#.*$//' /etc/services | %__grep -wq $1 ; then - echo "$1 $2 # $3" >> /etc/services + if [ -f %{_sysconfdir}/services ] && \ + ! %__sed -e 's/#.*$//' %{_sysconfdir}/services | %__grep -wq $1 ; then + echo "$1 $2 # $3" >> %{_sysconfdir}/services fi } @@ -408,86 +417,87 @@ zebra_spec_add_service ripngd 2603/tcp "RIPngd vty" zebra_spec_add_service ospfd 2604/tcp "OSPFd vty" zebra_spec_add_service bgpd 2605/tcp "BGPd vty" zebra_spec_add_service ospf6d 2606/tcp "OSPF6d vty" +zebra_spec_add_service isisd 2608/tcp "ISISd vty" %if %{with_ospfapi} -zebra_spec_add_service ospfapi 2607/tcp "OSPF-API" + zebra_spec_add_service ospfapi 2607/tcp "OSPF-API" %endif -zebra_spec_add_service isisd 2608/tcp "ISISd vty" %if %{with_babeld} -zebra_spec_add_service babeld 2609/tcp "BABELd vty" + zebra_spec_add_service babeld 2609/tcp "BABELd vty" %endif %if %{with_nhrpd} -zebra_spec_add_service nhrpd 2610/tcp "NHRPd vty" + zebra_spec_add_service nhrpd 2610/tcp "NHRPd vty" %endif %if %{with_pimd} -zebra_spec_add_service pimd 2611/tcp "PIMd vty" + zebra_spec_add_service pimd 2611/tcp "PIMd vty" %endif %if %{with_pbrd} -zebra_spec_add_service pbrd 2615/tcp "PBRd vty" + zebra_spec_add_service pbrd 2615/tcp "PBRd vty" %endif %if %{with_ldpd} -zebra_spec_add_service ldpd 2612/tcp "LDPd vty" + zebra_spec_add_service ldpd 2612/tcp "LDPd vty" %endif %if %{with_eigrpd} -zebra_spec_add_service eigrpd 2613/tcp "EIGRPd vty" + zebra_spec_add_service eigrpd 2613/tcp "EIGRPd vty" %endif %if "%{initsystem}" == "systemd" -for daemon in %all_daemons ; do - %systemd_post frr.service -done + for daemon in %all_daemons ; do + %systemd_post frr.service + done %else -/sbin/chkconfig --add frr + /sbin/chkconfig --add frr %endif # Fix bad path in previous config files # Config files won't get replaced by default, so we do this ugly hack to fix it -%__sed -i 's|/etc/init.d/|%{_sbindir}/|g' %{_sysconfdir}/daemons 2> /dev/null || true +%__sed -i 's|/etc/init.d/|%{_sbindir}/|g' %{configdir}/daemons 2> /dev/null || true # With systemd, watchfrr is mandatory. Fix config to make sure it's enabled if # we install or upgrade to a frr built with systemd %if "%{initsystem}" == "systemd" - %__sed -i 's|watchfrr_enable=no|watchfrr_enable=yes|g' %{_sysconfdir}/daemons 2> /dev/null || true + %__sed -i 's|watchfrr_enable=no|watchfrr_enable=yes|g' %{configdir}/daemons 2> /dev/null || true %endif /sbin/install-info %{_infodir}/frr.info.gz %{_infodir}/dir # Create dummy files if they don't exist so basic functions can be used. -if [ ! -e %{_sysconfdir}/zebra.conf ]; then - echo "hostname `hostname`" > %{_sysconfdir}/zebra.conf +if [ ! -e %{configdir}/zebra.conf ]; then + echo "hostname `hostname`" > %{configdir}/zebra.conf %if 0%{?frr_user:1} - chown %frr_user:%frr_user %{_sysconfdir}/zebra.conf* + chown %{frr_user}:%{frr_user} %{configdir}/zebra.conf* %endif - chmod 640 %{_sysconfdir}/zebra.conf* + chmod 640 %{configdir}/zebra.conf* fi for daemon in %{all_daemons} ; do if [ x"${daemon}" != x"" ] ; then - if [ ! -e %{_sysconfdir}/${daemon}.conf ]; then - touch %{_sysconfdir}/${daemon}.conf + if [ ! -e %{configdir}/${daemon}.conf ]; then + touch %{configdir}/${daemon}.conf %if 0%{?frr_user:1} - chown %frr_user:%frr_user %{_sysconfdir}/${daemon}.conf* + chown %{frr_user}:%{frr_user} %{configdir}/${daemon}.conf* %endif fi fi done %if 0%{?frr_user:1} - chown %frr_user:%frr_user %{_sysconfdir}/daemons + chown %{frr_user}:%{frr_user} %{configdir}/daemons %endif %if %{with_watchfrr} # No config for watchfrr - this is part of /etc/sysconfig/frr - rm -f %{_sysconfdir}/watchfrr.* + rm -f %{configdir}/watchfrr.* %endif -if [ ! -e %{_sysconfdir}/vtysh.conf ]; then - touch %{_sysconfdir}/vtysh.conf - chmod 640 %{_sysconfdir}/vtysh.conf +if [ ! -e %{configdir}/vtysh.conf ]; then + touch %{configdir}/vtysh.conf + chmod 640 %{configdir}/vtysh.conf %if 0%{?frr_user:1} -%if 0%{?vty_group:1} - chown %{frr_user}:%{vty_group} %{_sysconfdir}/vtysh.conf* -%endif + %if 0%{?vty_group:1} + chown %{frr_user}:%{vty_group} %{configdir}/vtysh.conf* + %endif %endif fi + %postun if [ "$1" -ge 1 ]; then # @@ -502,11 +512,12 @@ if [ "$1" -ge 1 ]; then ## ## init.d Version ## - /etc/rc.d/init.d/frr restart >/dev/null 2>&1 + service frr restart >/dev/null 2>&1 %endif : fi + %preun %if "%{initsystem}" == "systemd" ## @@ -520,33 +531,28 @@ fi ## init.d Version ## if [ $1 -eq 0 ] ; then - /etc/rc.d/init.d/frr stop >/dev/null 2>&1 + service frr stop >/dev/null 2>&1 /sbin/chkconfig --del frr fi %endif /sbin/install-info --delete %{_infodir}/frr.info.gz %{_infodir}/dir -%clean -%if !0%{?keep_build:1} -rm -rf %{buildroot} -%endif %files -%defattr(-,root,root) %doc */*.sample* AUTHORS COPYING %doc doc/mpls %doc ChangeLog NEWS README %if 0%{?frr_user:1} -%dir %attr(751,%frr_user,%frr_user) %{_sysconfdir} -%dir %attr(750,%frr_user,%frr_user) /var/log/frr -%dir %attr(751,%frr_user,%frr_user) /var/run/frr + %dir %attr(751,%{frr_user},%{frr_user}) %{configdir} + %dir %attr(750,%{frr_user},%{frr_user}) %{_localstatedir}/log/frr + %dir %attr(751,%{frr_user},%{frr_user}) %{rundir} %else -%dir %attr(750,root,root) %{_sysconfdir} -%dir %attr(750,root,root) /var/log/frr -%dir %attr(750,root,root) /var/run/frr + %dir %attr(750,root,root) %{configdir} + %dir %attr(750,root,root) %{_localstatedir}/log/frr + %dir %attr(750,root,root) %{rundir} %endif %if 0%{?vty_group:1} -%attr(750,%frr_user,%vty_group) %{_sysconfdir}/vtysh.conf.sample + %attr(750,%{frr_user},%{vty_group}) %{configdir}/vtysh.conf.sample %endif %{_infodir}/frr.info.gz %{_mandir}/man*/* @@ -579,63 +585,60 @@ rm -rf %{buildroot} %if %{with_babeld} %{_sbindir}/babeld %endif -%if %{with_shared} -%{_libdir}/lib*.so %{_libdir}/lib*.so.0 -%attr(755,root,root) %{_libdir}/lib*.so.0.* -%endif +%{_libdir}/lib*.so.0.* %if %{with_fpm} -%attr(755,root,root) %{_libdir}/frr/modules/zebra_fpm.so + %{_libdir}/frr/modules/zebra_fpm.so %endif %if %{with_rpki} -%attr(755,root,root) %{_libdir}/frr/modules/bgpd_rpki.so + %{_libdir}/frr/modules/bgpd_rpki.so %endif -%attr(755,root,root) %{_libdir}/frr/modules/zebra_irdp.so +%{_libdir}/frr/modules/zebra_irdp.so %{_bindir}/* -%config(noreplace) /etc/frr/[!v]*.conf* -%config(noreplace) %attr(750,%frr_user,%frr_user) /etc/frr/daemons +%config(noreplace) %{configdir}/[!v]*.conf* +%config(noreplace) %attr(750,%{frr_user},%{frr_user}) %{configdir}/daemons %if "%{initsystem}" == "systemd" %{_unitdir}/frr.service %else - /etc/rc.d/init.d/frr + %{_initddir}/frr %endif %{_sbindir}/frr -%config(noreplace) /etc/pam.d/frr -%config(noreplace) /etc/logrotate.d/frr +%config(noreplace) %{_sysconfdir}/pam.d/frr +%config(noreplace) %{_sysconfdir}/logrotate.d/frr %{_sbindir}/frr-reload + %files contrib -%defattr(-,root,root) %doc tools + %files pythontools -%defattr(-,root,root) %{_sbindir}/frr-reload.py %{_sbindir}/frr-reload.pyc %{_sbindir}/frr-reload.pyo + %files devel -%defattr(-,root,root) +%{_libdir}/lib*.so %if %{with_ospfclient} -%{_sbindir}/ospfclient -%endif -%{_libdir}/*.a -%{_libdir}/*.la -%dir %attr(755,root,root) %{_includedir}/%{name} -%{_includedir}/%name/*.h -%dir %attr(755,root,root) %{_includedir}/%{name}/ospfd -%{_includedir}/%name/ospfd/*.h + %{_sbindir}/ospfclient +%endif +%dir %{_includedir}/%{name} +%{_includedir}/%{name}/*.h +%dir %{_includedir}/%{name}/ospfd +%{_includedir}/%{name}/ospfd/*.h %if %{with_ospfapi} -%dir %attr(755,root,root) %{_includedir}/%{name}/ospfapi -%{_includedir}/%name/ospfapi/*.h + %dir %{_includedir}/%{name}/ospfapi + %{_includedir}/%{name}/ospfapi/*.h %endif %if %{with_eigrpd} -%dir %attr(755,root,root) %{_includedir}/%{name}/eigrpd -%{_includedir}/%name/eigrpd/*.h + %dir %{_includedir}/%{name}/eigrpd + %{_includedir}/%{name}/eigrpd/*.h %endif + %changelog -* Sun May 20 2018 Martin Winter <mwinter@opensourcerouting.org> - %{version} +* Sun May 20 2018 Martin Winter <mwinter@opensourcerouting.org> - Fixed RPKI RPM build * Sun Mar 4 2018 Martin Winter <mwinter@opensourcerouting.org> 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/ripngd/ripngd.c b/ripngd/ripngd.c index 840157516f..565e151c53 100644 --- a/ripngd/ripngd.c +++ b/ripngd/ripngd.c @@ -1516,9 +1516,10 @@ int ripng_write_rte(int num, struct stream *s, struct prefix_ipv6 *p, } /* Write routing table entry. */ - if (!nexthop) + if (!nexthop) { + assert(p); stream_write(s, (uint8_t *)&p->prefix, sizeof(struct in6_addr)); - else + } else stream_write(s, (uint8_t *)nexthop, sizeof(struct in6_addr)); stream_putw(s, tag); if (p) diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index 0697cd8b75..b56eaa899f 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -3357,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[] = { diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c index 7334c8094a..d9c6631845 100644 --- a/zebra/kernel_netlink.c +++ b/zebra/kernel_netlink.c @@ -545,7 +545,6 @@ static void netlink_parse_extended_ack(struct nlmsghdr *h) if (off > h->nlmsg_len) { zlog_err("Invalid offset for NLMSGERR_ATTR_OFFS\n"); - off = 0; } else if (!(h->nlmsg_flags & NLM_F_CAPPED)) { /* * Header of failed message diff --git a/zebra/redistribute.c b/zebra/redistribute.c index be53b74b3f..1fee675cbf 100644 --- a/zebra/redistribute.c +++ b/zebra/redistribute.c @@ -274,7 +274,7 @@ void zebra_redistribute_add(ZAPI_HANDLER_ARGS) __func__, zebra_route_string(client->proto), afi, zebra_route_string(type), zvrf_id(zvrf), instance); - if (afi == 0 || afi > AFI_MAX) { + if (afi == 0 || afi >= AFI_MAX) { zlog_warn("%s: Specified afi %d does not exist", __PRETTY_FUNCTION__, afi); return; @@ -320,7 +320,7 @@ void zebra_redistribute_delete(ZAPI_HANDLER_ARGS) STREAM_GETC(msg, type); STREAM_GETW(msg, instance); - if (afi == 0 || afi > AFI_MAX) { + if (afi == 0 || afi >= AFI_MAX) { zlog_warn("%s: Specified afi %d does not exist", __PRETTY_FUNCTION__, afi); return; 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_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/zebra_vrf.c b/zebra/zebra_vrf.c index d443f725b0..3c21c3c1e5 100644 --- a/zebra/zebra_vrf.c +++ b/zebra/zebra_vrf.c @@ -134,14 +134,6 @@ static int zebra_vrf_enable(struct vrf *vrf) zvrf->import_check_table[afi] = table; } - static_fixup_vrf_ids(zvrf); - - /* - * We may have static routes that are now possible to - * insert into the appropriate tables - */ - static_config_install_delayed_routes(zvrf); - /* Kick off any VxLAN-EVPN processing. */ zebra_vxlan_vrf_enable(zvrf); diff --git a/zebra/zserv.c b/zebra/zserv.c index 14e0db40bf..434a1507ae 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 */ @@ -618,14 +618,6 @@ static int zserv_handle_client_close(struct thread *thread) { struct zserv *client = THREAD_ARG(thread); - /* - * Ensure these have been nulled. This does not equate to the - * associated task(s) being scheduled or unscheduled on the client - * pthread's threadmaster. - */ - assert(!client->t_read); - assert(!client->t_write); - /* synchronously stop thread */ frr_pthread_stop(client->pthread, NULL); @@ -939,6 +931,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; } |
