From 847b7ab16ed51d9d9cd1a8e521f5477a246e306d Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Thu, 21 Jun 2018 15:01:01 +0200 Subject: [PATCH 01/28] zebra: pbr ipset_type2_str command is externalised The API of that function that converts ipset types is externalised. Signed-off-by: Philippe Guibert --- zebra/zebra_pbr.c | 2 +- zebra/zebra_pbr.h | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/zebra/zebra_pbr.c b/zebra/zebra_pbr.c index 303ea44d080c..1b4a13ac8ee8 100644 --- a/zebra/zebra_pbr.c +++ b/zebra/zebra_pbr.c @@ -512,7 +512,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"); diff --git a/zebra/zebra_pbr.h b/zebra/zebra_pbr.h index 31fc553581f8..486dabf693af 100644 --- a/zebra/zebra_pbr.h +++ b/zebra/zebra_pbr.h @@ -138,6 +138,8 @@ struct zebra_pbr_iptable { char ipset_name[ZEBRA_IPSET_NAME_SIZE]; }; +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, From 6a04dacb2140bb65d30507e43932ec3085d4b357 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Tue, 5 Jun 2018 17:01:14 +0200 Subject: [PATCH 02/28] zebra: improve show zebra ipset output for icmp The icmp type/code is displayed. Also, the flags are correctly set in case ICMP protocol is elected. Signed-off-by: Philippe Guibert --- zebra/zapi_msg.c | 4 +-- zebra/zebra_pbr.c | 78 ++++++++++++++++++++++++++++++++++++++++++++--- zebra/zebra_pbr.h | 2 ++ 3 files changed, 78 insertions(+), 6 deletions(-) diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index 4703f406f98a..96f1e09d733c 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -2864,9 +2864,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; diff --git a/zebra/zebra_pbr.c b/zebra/zebra_pbr.c index 1b4a13ac8ee8..f1cff304e541 100644 --- a/zebra/zebra_pbr.c +++ b/zebra/zebra_pbr.c @@ -43,6 +43,44 @@ 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} +}; + /* static function declarations */ DEFINE_HOOK(zebra_pbr_ipset_entry_wrap_script_get_stat, (struct zebra_ns *zns, struct zebra_pbr_ipset_entry *ipset, @@ -770,6 +808,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) @@ -813,7 +875,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, @@ -821,11 +884,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]; @@ -834,7 +900,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, @@ -843,11 +910,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); diff --git a/zebra/zebra_pbr.h b/zebra/zebra_pbr.h index 486dabf693af..4c991124a4cd 100644 --- a/zebra/zebra_pbr.h +++ b/zebra/zebra_pbr.h @@ -138,6 +138,8 @@ 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); From 3b705172eb29884819580fd64c1f8a4b8e0e6d97 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Mon, 4 Jun 2018 18:11:04 +0200 Subject: [PATCH 03/28] bgpd: handle ICMP type and code from flowspec It is possible for flowspec entries containing ICMP rule to insert PBR entries based on ICMP type and ICMP code. Flowspec ICMP filtering can either have icmp type or icmp code or both. Not all combinations are permitted: - if icmp code is provided, then it is not possible to derive the correct icmp value. This will not be installed - range of ICMP is authorised or list of ICMP, but not both. - on receiving a list of ICMPtype/code, each ICMP type is attempted to be associated to ICMP code. If not found, then ICMPtype is combined with all known ICMP code values associated to that ICMP type. - if a specific ICMP type/code is needed, despite the ICMP code/type combination does not exist, then it is possible to do it by forging a FS ICMP type/code specific for that. Signed-off-by: Philippe Guibert --- bgpd/bgp_pbr.c | 332 ++++++++++++++++++++++++++++++++++++++++++++++--- bgpd/bgp_pbr.h | 1 + 2 files changed, 313 insertions(+), 20 deletions(-) diff --git a/bgpd/bgp_pbr.c b/bgpd/bgp_pbr.c index 1054ea402743..0a54183db777 100644 --- a/bgpd/bgp_pbr.c +++ b/bgpd/bgp_pbr.c @@ -180,6 +180,19 @@ struct bgp_pbr_range_port { uint16_t max_port; }; +static bool bgp_pbr_extract_enumerate(struct bgp_pbr_match_val list[], + int num) +{ + int i = 0; + + for (i = 0; i < num; i++) { + if (list[i].compare_operator != + OPERATOR_COMPARE_EQUAL_TO) + return false; + } + return true; +} + /* return true if extraction ok */ static bool bgp_pbr_extract(struct bgp_pbr_match_val list[], @@ -231,6 +244,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,9 +255,8 @@ 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_icmp_type_num - || api->match_packet_length_num || api->match_dscp_num - || api->match_tcpflags_num) { + if (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."); @@ -260,6 +274,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:" @@ -279,6 +294,32 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api) "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)) { + 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)) { + 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:" @@ -295,6 +336,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)) { @@ -985,7 +1034,7 @@ static void bgp_pbr_policyroute_remove_from_zebra(struct bgp *bgp, prefix_copy(&temp2.dst, dst); } else temp2.dst.family = AF_INET; - if (src_port) { + if (src_port && src_port->min_port) { temp.flags |= MATCH_PORT_SRC_SET; temp2.src_port_min = src_port->min_port; if (src_port->max_port) { @@ -993,7 +1042,7 @@ 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) { temp.flags |= MATCH_PORT_DST_SET; temp2.dst_port_min = dst_port->min_port; if (dst_port->max_port) { @@ -1208,6 +1257,183 @@ static void bgp_pbr_policyroute_add_to_zebra(struct bgp *bgp, } +static const struct message icmp_code_unreach_str[] = { + { 9, "communication-prohibited-by-filtering"}, + { 10, "destination-host-prohibited"}, + { 7, "destination-host-unknown"}, + { 6, "destination-network-unknown"}, + { 4, "fragmentation-needed"}, + { 14, "host-precedence-violation"}, + { 0, "network-unreachable"}, + { 12, "network-unreachable-for-tos"}, + { 3, "port-unreachable"}, + { 8, "source-host-isolated"}, + { 5, "source-route-failed"}, + {0} +}; + +static const struct message icmp_code_redirect_str[] = { + { 1, "redirect-for-host"}, + { 0, "redirect-for-network"}, + { 3, "redirect-for-tos-and-host"}, + { 2, "redirect-for-tos-and-net"}, + {0} +}; + +static const struct message icmp_code_exceed_str[] = { + { 1, "ttl-eq-zero-during-reassembly"}, + { 0, "ttl-eq-zero-during-transit"}, + {0} +}; + +static const struct message icmp_code_problem_str[] = { + { 1, "required-option-missing"}, + { 2, "ip-header-bad"}, + {0} +}; +static void bgp_pbr_enumerate_action_src_dst(struct bgp_pbr_match_val src[], + int src_num, + struct bgp_pbr_match_val dst[], + int dst_num, + struct prefix *src_address, + struct prefix *dst_address, + uint8_t proto, + struct bgp *bgp, + struct bgp_info *binfo, + bool add, + vrf_id_t vrf_id, + struct nexthop *nh, + float *rate) +{ + int i = 0, j; + struct bgp_pbr_range_port srcp, dstp; + + if (proto != IPPROTO_ICMP) + return; + /* combinatory forced. ignore icmp type / code combinatory */ + if (src_num == 1 && dst_num == 1) { + srcp.max_port = 0; + dstp.max_port = 0; + srcp.min_port = src[0].value; + dstp.min_port = dst[0].value; + if (add) + bgp_pbr_policyroute_add_to_zebra(bgp, binfo, + vrf_id, src_address, + dst_address, + nh, rate, proto, + &srcp, &dstp); + else + bgp_pbr_policyroute_remove_from_zebra( + bgp, + binfo, + vrf_id, + src_address, + dst_address, + proto, + &srcp, &dstp); + return; + } + /* 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 + */ + for (i = 0; i < src_num; i++) { + const struct message *pnt; + const struct message *pnt_code = NULL; + static struct message nt = {0}; + bool icmp_typecode_configured = false; + + srcp.min_port = src[i].value; + srcp.max_port = 0; + dstp.max_port = 0; + if (src[i].value == 3) + pnt_code = icmp_code_unreach_str; + else if (src[i].value == 5) + pnt_code = icmp_code_redirect_str; + else if (src[i].value == 11) + pnt_code = icmp_code_exceed_str; + else if (src[i].value == 12) + pnt_code = icmp_code_problem_str; + switch (src[i].value) { + case 3: + case 5: + case 11: + case 12: + for (j = 0; j < dst_num; j++) { + for (pnt = pnt_code; + pnt && memcmp(pnt, &nt, sizeof(struct message)); + pnt++) { + if (dst[i].value != pnt->key) + continue; + dstp.min_port = dst[i].value; + icmp_typecode_configured = true; + if (add) + bgp_pbr_policyroute_add_to_zebra(bgp, binfo, + vrf_id, src_address, + dst_address, + nh, rate, proto, + &srcp, &dstp); + else + bgp_pbr_policyroute_remove_from_zebra( + bgp, + binfo, + vrf_id, + src_address, + dst_address, + proto, + &srcp, &dstp); + } + } + /* create a list of ICMP type/code combinatories */ + if (!icmp_typecode_configured) { + for (pnt = pnt_code; + pnt && memcmp(pnt, &nt, sizeof(struct message)); + pnt++) { + dstp.min_port = pnt->key; + if (add) + bgp_pbr_policyroute_add_to_zebra(bgp, binfo, + vrf_id, src_address, + dst_address, + nh, rate, proto, + &srcp, &dstp); + else + bgp_pbr_policyroute_remove_from_zebra( + bgp, + binfo, + vrf_id, + src_address, + dst_address, + proto, + &srcp, &dstp); + } + + } + break; + default: + /* icmp type is not one of the above + * forge an entry only based on the icmp type + */ + dstp.min_port = 0; + if (add) + bgp_pbr_policyroute_add_to_zebra(bgp, binfo, + vrf_id, src_address, + dst_address, + nh, rate, proto, + &srcp, &dstp); + else + bgp_pbr_policyroute_remove_from_zebra( + bgp, + binfo, + vrf_id, + src_address, + dst_address, + proto, + &srcp, &dstp); + break; + } + } +} + static void bgp_pbr_handle_entry(struct bgp *bgp, struct bgp_info *binfo, struct bgp_pbr_entry_main *api, @@ -1220,7 +1446,8 @@ 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; + bool enum_icmp = false; memset(&nh, 0, sizeof(struct nexthop)); if (api->match_bitmask & PREFIX_SRC_PRESENT) @@ -1252,10 +1479,48 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, dstp = ⦥ srcp = NULL; } - if (!add) - return bgp_pbr_policyroute_remove_from_zebra(bgp, binfo, - api->vrf_id, src, dst, - proto, srcp, dstp); + if (api->match_icmp_type_num >= 1) { + proto = IPPROTO_ICMP; + if (bgp_pbr_extract_enumerate(api->icmp_type, + api->match_icmp_type_num)) + enum_icmp = true; + else { + bgp_pbr_extract(api->icmp_type, + api->match_icmp_type_num, + &range); + srcp = ⦥ + } + } + if (api->match_icmp_code_num >= 1) { + proto = IPPROTO_ICMP; + if (bgp_pbr_extract_enumerate(api->icmp_code, + api->match_icmp_code_num)) + enum_icmp = true; + else { + bgp_pbr_extract(api->icmp_code, + api->match_icmp_code_num, + &range_icmp_code); + dstp = &range_icmp_code; + } + } + + if (!add) { + if (enum_icmp) { + return bgp_pbr_enumerate_action_src_dst(api->icmp_type, + api->match_icmp_type_num, + api->icmp_code, + api->match_icmp_code_num, + src, dst, proto, + bgp, binfo, add, + api->vrf_id, NULL, NULL); + } + return bgp_pbr_policyroute_remove_from_zebra(bgp, + binfo, + api->vrf_id, + src, dst, + proto, + srcp, dstp); + } /* no action for add = true */ for (i = 0; i < api->action_num; i++) { switch (api->actions[i].action) { @@ -1264,10 +1529,19 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, if (api->actions[i].u.r.rate == 0) { 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); + if (enum_icmp) + bgp_pbr_enumerate_action_src_dst(api->icmp_type, + api->match_icmp_type_num, + api->icmp_code, + api->match_icmp_code_num, + src, dst, proto, + bgp, binfo, add, + api->vrf_id, &nh, &rate); + else + bgp_pbr_policyroute_add_to_zebra(bgp, binfo, + api->vrf_id, src, dst, + &nh, &rate, proto, + srcp, dstp); } else { /* update rate. can be reentrant */ rate = api->actions[i].u.r.rate; @@ -1307,11 +1581,20 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, nh.gate.ipv4.s_addr = 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); + if (enum_icmp) + bgp_pbr_enumerate_action_src_dst(api->icmp_type, + api->match_icmp_type_num, + api->icmp_code, + api->match_icmp_code_num, + src, dst, proto, + bgp, binfo, add, + api->vrf_id, &nh, &rate); + else + bgp_pbr_policyroute_add_to_zebra(bgp, binfo, + api->vrf_id, + src, dst, + &nh, &rate, proto, + srcp, dstp); /* XXX combination with REDIRECT_VRF * + REDIRECT_NH_IP not done */ @@ -1320,7 +1603,16 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, case ACTION_REDIRECT: nh.vrf_id = api->actions[i].u.redirect_vrf; nh.type = NEXTHOP_TYPE_IPV4; - bgp_pbr_policyroute_add_to_zebra(bgp, binfo, + if (enum_icmp) + bgp_pbr_enumerate_action_src_dst(api->icmp_type, + api->match_icmp_type_num, + api->icmp_code, + api->match_icmp_code_num, + src, dst, proto, + bgp, binfo, add, + api->vrf_id, &nh, &rate); + else + bgp_pbr_policyroute_add_to_zebra(bgp, binfo, api->vrf_id, src, dst, &nh, &rate, proto, diff --git a/bgpd/bgp_pbr.h b/bgpd/bgp_pbr.h index 20edaf30b8a5..e869d0106d6f 100644 --- a/bgpd/bgp_pbr.h +++ b/bgpd/bgp_pbr.h @@ -125,6 +125,7 @@ struct bgp_pbr_entry_main { #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]; From cea32b618cf229074f7d566f3368108b31b73dfa Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Mon, 4 Jun 2018 18:13:00 +0200 Subject: [PATCH 04/28] bgpd: add comment to inform that icmp can be stored in that struct Generic ipset entry structure will be reused to host icmp information. Signed-off-by: Philippe Guibert --- zebra/zebra_pbr.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/zebra/zebra_pbr.h b/zebra/zebra_pbr.h index 4c991124a4cd..09b91527fa5c 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; From c0b8c1abed5f2c4d8ba1226856312ab721625257 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Mon, 11 Jun 2018 15:35:37 +0200 Subject: [PATCH 05/28] bgpd: add support of bgp flowspec filtering per packet length It is possible to do filtering based on packet length value or a range of packet-length. Signed-off-by: Philippe Guibert --- bgpd/bgp_pbr.c | 66 +++++++++++++++++++++++++++++++++++------------- bgpd/bgp_pbr.h | 3 +++ bgpd/bgp_zebra.c | 2 ++ 3 files changed, 54 insertions(+), 17 deletions(-) diff --git a/bgpd/bgp_pbr.c b/bgpd/bgp_pbr.c index 0a54183db777..47f12d23d49e 100644 --- a/bgpd/bgp_pbr.c +++ b/bgpd/bgp_pbr.c @@ -255,8 +255,7 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api) * - combination src/dst => drop * - combination srcport + @IP */ - if (api->match_packet_length_num - || api->match_dscp_num || api->match_tcpflags_num) { + if (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."); @@ -326,6 +325,12 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api) "too complex. ignoring."); return 0; } + if (!bgp_pbr_extract(api->packet_length, api->match_packet_length_num, NULL)) { + if (BGP_DEBUG(pbr, PBR)) + zlog_debug("BGP: match packet length operations:" + "too complex. ignoring."); + return 0; + } /* no combinations with both src_port and dst_port * or port with src_port and dst_port */ @@ -568,6 +573,8 @@ 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); return jhash_1word(pbm->type, key); } @@ -590,6 +597,12 @@ 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; + return 1; } @@ -1009,6 +1022,7 @@ static void bgp_pbr_policyroute_remove_from_zebra(struct bgp *bgp, 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) { @@ -1052,6 +1066,11 @@ static void bgp_pbr_policyroute_remove_from_zebra(struct bgp *bgp, } temp2.proto = protocol; + if (pkt_len) + temp.pkt_len_min = pkt_len->min_port; + if (pkt_len && pkt_len->max_port) + temp.pkt_len_max = pkt_len->max_port; + if (src == NULL || dst == NULL) { if (temp.flags & (MATCH_PORT_DST_SET | MATCH_PORT_SRC_SET)) temp.type = IPSET_NET_PORT; @@ -1095,6 +1114,7 @@ static void bgp_pbr_policyroute_add_to_zebra(struct bgp *bgp, struct nexthop *nh, float *rate, uint8_t protocol, + struct bgp_pbr_range_port *pkt_len, struct bgp_pbr_range_port *src_port, struct bgp_pbr_range_port *dst_port) { @@ -1161,6 +1181,11 @@ static void bgp_pbr_policyroute_add_to_zebra(struct bgp *bgp, temp.flags |= MATCH_PORT_SRC_RANGE_SET; if (dst_port && dst_port->max_port) temp.flags |= MATCH_PORT_DST_RANGE_SET; + if (pkt_len) + temp.pkt_len_min = pkt_len->min_port; + if (pkt_len && pkt_len->max_port) + temp.pkt_len_max = pkt_len->max_port; + temp.action = bpa; bpm = hash_get(bgp->pbr_match_hash, &temp, bgp_pbr_match_alloc_intern); @@ -1298,6 +1323,7 @@ static void bgp_pbr_enumerate_action_src_dst(struct bgp_pbr_match_val src[], struct prefix *src_address, struct prefix *dst_address, uint8_t proto, + struct bgp_pbr_range_port *pkt_len, struct bgp *bgp, struct bgp_info *binfo, bool add, @@ -1321,7 +1347,7 @@ static void bgp_pbr_enumerate_action_src_dst(struct bgp_pbr_match_val src[], vrf_id, src_address, dst_address, nh, rate, proto, - &srcp, &dstp); + pkt_len, &srcp, &dstp); else bgp_pbr_policyroute_remove_from_zebra( bgp, @@ -1329,7 +1355,7 @@ static void bgp_pbr_enumerate_action_src_dst(struct bgp_pbr_match_val src[], vrf_id, src_address, dst_address, - proto, + proto, pkt_len, &srcp, &dstp); return; } @@ -1372,7 +1398,7 @@ static void bgp_pbr_enumerate_action_src_dst(struct bgp_pbr_match_val src[], vrf_id, src_address, dst_address, nh, rate, proto, - &srcp, &dstp); + pkt_len, &srcp, &dstp); else bgp_pbr_policyroute_remove_from_zebra( bgp, @@ -1381,6 +1407,7 @@ static void bgp_pbr_enumerate_action_src_dst(struct bgp_pbr_match_val src[], src_address, dst_address, proto, + pkt_len, &srcp, &dstp); } } @@ -1395,6 +1422,7 @@ static void bgp_pbr_enumerate_action_src_dst(struct bgp_pbr_match_val src[], vrf_id, src_address, dst_address, nh, rate, proto, + pkt_len, &srcp, &dstp); else bgp_pbr_policyroute_remove_from_zebra( @@ -1403,7 +1431,7 @@ static void bgp_pbr_enumerate_action_src_dst(struct bgp_pbr_match_val src[], vrf_id, src_address, dst_address, - proto, + proto, pkt_len, &srcp, &dstp); } @@ -1419,7 +1447,7 @@ static void bgp_pbr_enumerate_action_src_dst(struct bgp_pbr_match_val src[], vrf_id, src_address, dst_address, nh, rate, proto, - &srcp, &dstp); + pkt_len, &srcp, &dstp); else bgp_pbr_policyroute_remove_from_zebra( bgp, @@ -1427,7 +1455,7 @@ static void bgp_pbr_enumerate_action_src_dst(struct bgp_pbr_match_val src[], vrf_id, src_address, dst_address, - proto, + proto, pkt_len, &srcp, &dstp); break; } @@ -1447,6 +1475,7 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, uint8_t proto = 0; struct bgp_pbr_range_port *srcp = NULL, *dstp = NULL; struct bgp_pbr_range_port range, range_icmp_code; + struct bgp_pbr_range_port pkt_len; bool enum_icmp = false; memset(&nh, 0, sizeof(struct nexthop)); @@ -1503,14 +1532,17 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, dstp = &range_icmp_code; } } - + if (api->match_packet_length_num >= 1) + bgp_pbr_extract(api->packet_length, + api->match_packet_length_num, + &pkt_len); if (!add) { if (enum_icmp) { return bgp_pbr_enumerate_action_src_dst(api->icmp_type, api->match_icmp_type_num, api->icmp_code, api->match_icmp_code_num, - src, dst, proto, + src, dst, proto, &pkt_len, bgp, binfo, add, api->vrf_id, NULL, NULL); } @@ -1518,7 +1550,7 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, binfo, api->vrf_id, src, dst, - proto, + proto, &pkt_len, srcp, dstp); } /* no action for add = true */ @@ -1534,14 +1566,14 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, api->match_icmp_type_num, api->icmp_code, api->match_icmp_code_num, - src, dst, proto, + src, dst, proto, &pkt_len, bgp, binfo, add, api->vrf_id, &nh, &rate); else bgp_pbr_policyroute_add_to_zebra(bgp, binfo, api->vrf_id, src, dst, &nh, &rate, proto, - srcp, dstp); + &pkt_len, srcp, dstp); } else { /* update rate. can be reentrant */ rate = api->actions[i].u.r.rate; @@ -1586,7 +1618,7 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, api->match_icmp_type_num, api->icmp_code, api->match_icmp_code_num, - src, dst, proto, + src, dst, proto, &pkt_len, bgp, binfo, add, api->vrf_id, &nh, &rate); else @@ -1594,7 +1626,7 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, api->vrf_id, src, dst, &nh, &rate, proto, - srcp, dstp); + &pkt_len, srcp, dstp); /* XXX combination with REDIRECT_VRF * + REDIRECT_NH_IP not done */ @@ -1608,7 +1640,7 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, api->match_icmp_type_num, api->icmp_code, api->match_icmp_code_num, - src, dst, proto, + src, dst, proto, &pkt_len, bgp, binfo, add, api->vrf_id, &nh, &rate); else @@ -1616,7 +1648,7 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, api->vrf_id, src, dst, &nh, &rate, proto, - srcp, dstp); + &pkt_len, srcp, dstp); continue_loop = 0; break; case ACTION_MARKING: diff --git a/bgpd/bgp_pbr.h b/bgpd/bgp_pbr.h index e869d0106d6f..9d578a183b58 100644 --- a/bgpd/bgp_pbr.h +++ b/bgpd/bgp_pbr.h @@ -185,6 +185,9 @@ struct bgp_pbr_match { #define MATCH_PORT_DST_RANGE_SET (1 << 5) uint32_t flags; + uint16_t pkt_len_min; + uint16_t pkt_len_max; + vrf_id_t vrf_id; /* unique identifier for ipset create transaction diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index 48aa24ddad15..a8bc7e953310 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -2233,6 +2233,8 @@ 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); } /* BGP has established connection with Zebra. */ From fa73fdcb1bd83211056434144cc9c7b371dd45a1 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Mon, 11 Jun 2018 15:41:57 +0200 Subject: [PATCH 06/28] bgpd: add debug routine to display which PBR entry is handled To know which entry is set/unset, a debug handler is present, that displays which entry is injected/removed to/from zebra. Signed-off-by: Philippe Guibert --- bgpd/bgp_pbr.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/bgpd/bgp_pbr.c b/bgpd/bgp_pbr.c index 47f12d23d49e..5baba4cbb222 100644 --- a/bgpd/bgp_pbr.c +++ b/bgpd/bgp_pbr.c @@ -1126,6 +1126,56 @@ static void bgp_pbr_policyroute_add_to_zebra(struct bgp *bgp, struct bgp_pbr_action *bpa = NULL; struct bgp_pbr_match_entry_remain bpmer; + 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 (protocol) + snprintf(protocol_str, sizeof(protocol), + "proto %d", protocol); + buffer[0] = '\0'; + if (protocol == IPPROTO_ICMP && src_port && dst_port) + remaining_len += snprintf(buffer, 64, "type %d, code %d", + src_port->min_port, dst_port->min_port); + else if ( protocol == IPPROTO_UDP || 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); + } + zlog_info("BGP: adding FS PBR from %s to %s, %s %s", + src == NULL ? "" : prefix2str(src, bufsrc, 64), + dst == NULL ? "" : prefix2str(dst, bufdst, 64), + protocol_str, buffer); + } /* look for bpa first */ memset(&temp3, 0, sizeof(temp3)); if (rate) From a0ca133fa1e7b3617496efd4ab9a2fb559a6393d Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Mon, 11 Jun 2018 15:30:11 +0200 Subject: [PATCH 07/28] zebra: add packet length into pbr support The packet length is added to iptable zapi message. Then the iptable structure is taking into account the pkt_len field. The show pbr iptable command displays the packet length used if any. Signed-off-by: Philippe Guibert --- zebra/zapi_msg.c | 2 ++ zebra/zebra_pbr.c | 16 +++++++++++++++- zebra/zebra_pbr.h | 3 +++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index 96f1e09d733c..65cf4ba8be8b 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -2906,6 +2906,8 @@ 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_GETL(s, zpi.nb_interface); zebra_pbr_iptable_update_interfacelist(s, &zpi); diff --git a/zebra/zebra_pbr.c b/zebra/zebra_pbr.c index f1cff304e541..07b29bc40399 100644 --- a/zebra/zebra_pbr.c +++ b/zebra/zebra_pbr.c @@ -359,6 +359,8 @@ 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); return jhash_3words(iptable->filter_bm, iptable->type, iptable->unique, key); } @@ -383,6 +385,10 @@ 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; return 1; } @@ -1013,7 +1019,15 @@ 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); + } 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 09b91527fa5c..09004e795330 100644 --- a/zebra/zebra_pbr.h +++ b/zebra/zebra_pbr.h @@ -133,6 +133,9 @@ struct zebra_pbr_iptable { uint32_t action; + uint16_t pkt_len_min; + uint16_t pkt_len_max; + uint32_t nb_interface; struct list *interface_name_list; From db237932281a9e1e50cd14f0300ff6d8be74de63 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Tue, 12 Jun 2018 14:45:35 +0200 Subject: [PATCH 08/28] bgpd: simplify API in BGP policy-routing to handle Flowspec To handle FS params between FS RIB and BGP PBR entities, a structure intermediate named bgp_pbr_filter is used, and contains all filtering information that was before passed as a parameter. Signed-off-by: Philippe Guibert --- bgpd/bgp_pbr.c | 231 +++++++++++++++++++++++-------------------------- 1 file changed, 106 insertions(+), 125 deletions(-) diff --git a/bgpd/bgp_pbr.c b/bgpd/bgp_pbr.c index 5baba4cbb222..c798a11a1941 100644 --- a/bgpd/bgp_pbr.c +++ b/bgpd/bgp_pbr.c @@ -175,11 +175,27 @@ 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 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; +}; + static bool bgp_pbr_extract_enumerate(struct bgp_pbr_match_val list[], int num) { @@ -1017,20 +1033,23 @@ static int bgp_pbr_get_remaining_entry(struct hash_backet *backet, void *arg) } 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 *pkt_len, - struct bgp_pbr_range_port *src_port, - struct bgp_pbr_range_port *dst_port) + 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 @@ -1038,14 +1057,14 @@ 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 && src_port->min_port) { @@ -1064,14 +1083,14 @@ 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 && pkt_len->max_port) temp.pkt_len_max = pkt_len->max_port; - 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 @@ -1082,10 +1101,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; @@ -1108,15 +1127,9 @@ static void bgp_pbr_policyroute_remove_from_zebra(struct bgp *bgp, static void bgp_pbr_policyroute_add_to_zebra(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 *pkt_len, - 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; @@ -1125,6 +1138,15 @@ 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]; @@ -1133,14 +1155,15 @@ static void bgp_pbr_policyroute_add_to_zebra(struct bgp *bgp, char protocol_str[16]; protocol_str[0] = '\0'; - if (protocol) - snprintf(protocol_str, sizeof(protocol), - "proto %d", protocol); + if (bpf->protocol) + snprintf(protocol_str, sizeof(protocol_str), + "proto %d", bpf->protocol); buffer[0] = '\0'; - if (protocol == IPPROTO_ICMP && src_port && dst_port) + if (bpf->protocol == IPPROTO_ICMP && src_port && dst_port) remaining_len += snprintf(buffer, 64, "type %d, code %d", src_port->min_port, dst_port->min_port); - else if ( protocol == IPPROTO_UDP || protocol == IPPROTO_TCP) { + else if (bpf->protocol == IPPROTO_UDP || + bpf->protocol == IPPROTO_TCP) { if (src_port && src_port->min_port) remaining_len += snprintf(buffer, @@ -1172,8 +1195,10 @@ static void bgp_pbr_policyroute_add_to_zebra(struct bgp *bgp, pkt_len->min_port); } zlog_info("BGP: adding FS PBR from %s to %s, %s %s", - src == NULL ? "" : prefix2str(src, bufsrc, 64), - dst == NULL ? "" : prefix2str(dst, bufdst, 64), + bpf->src == NULL ? "" : + prefix2str(bpf->src, bufsrc, sizeof(bufsrc)), + bpf->dst == NULL ? "" : + prefix2str(bpf->dst, bufdst, sizeof(bufdst)), protocol_str, buffer); } /* look for bpa first */ @@ -1182,7 +1207,7 @@ static void bgp_pbr_policyroute_add_to_zebra(struct bgp *bgp, 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); @@ -1204,7 +1229,7 @@ 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 (bpf->src == NULL || bpf->dst == NULL) { if ((src_port && src_port->min_port) || (dst_port && dst_port->min_port)) temp.type = IPSET_NET_PORT; @@ -1217,10 +1242,10 @@ static void bgp_pbr_policyroute_add_to_zebra(struct bgp *bgp, 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) @@ -1259,19 +1284,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); @@ -1370,22 +1395,22 @@ static void bgp_pbr_enumerate_action_src_dst(struct bgp_pbr_match_val src[], int src_num, struct bgp_pbr_match_val dst[], int dst_num, - struct prefix *src_address, - struct prefix *dst_address, - uint8_t proto, - struct bgp_pbr_range_port *pkt_len, + struct bgp_pbr_filter *bpf, struct bgp *bgp, struct bgp_info *binfo, bool add, - vrf_id_t vrf_id, struct nexthop *nh, float *rate) { int i = 0, j; struct bgp_pbr_range_port srcp, dstp; - if (proto != IPPROTO_ICMP) + if (!bpf) return; + if (bpf->protocol != IPPROTO_ICMP) + return; + bpf->src_port = &srcp; + bpf->dst_port = &dstp; /* combinatory forced. ignore icmp type / code combinatory */ if (src_num == 1 && dst_num == 1) { srcp.max_port = 0; @@ -1394,19 +1419,10 @@ static void bgp_pbr_enumerate_action_src_dst(struct bgp_pbr_match_val src[], dstp.min_port = dst[0].value; if (add) bgp_pbr_policyroute_add_to_zebra(bgp, binfo, - vrf_id, src_address, - dst_address, - nh, rate, proto, - pkt_len, &srcp, &dstp); + bpf, nh, rate); else - bgp_pbr_policyroute_remove_from_zebra( - bgp, - binfo, - vrf_id, - src_address, - dst_address, - proto, pkt_len, - &srcp, &dstp); + bgp_pbr_policyroute_remove_from_zebra(bgp, + binfo, bpf); return; } /* parse icmp type and lookup appropriate icmp code @@ -1445,20 +1461,10 @@ static void bgp_pbr_enumerate_action_src_dst(struct bgp_pbr_match_val src[], icmp_typecode_configured = true; if (add) bgp_pbr_policyroute_add_to_zebra(bgp, binfo, - vrf_id, src_address, - dst_address, - nh, rate, proto, - pkt_len, &srcp, &dstp); + bpf, nh, rate); else bgp_pbr_policyroute_remove_from_zebra( - bgp, - binfo, - vrf_id, - src_address, - dst_address, - proto, - pkt_len, - &srcp, &dstp); + bgp, binfo, bpf); } } /* create a list of ICMP type/code combinatories */ @@ -1469,20 +1475,10 @@ static void bgp_pbr_enumerate_action_src_dst(struct bgp_pbr_match_val src[], dstp.min_port = pnt->key; if (add) bgp_pbr_policyroute_add_to_zebra(bgp, binfo, - vrf_id, src_address, - dst_address, - nh, rate, proto, - pkt_len, - &srcp, &dstp); + bpf, nh, rate); else - bgp_pbr_policyroute_remove_from_zebra( - bgp, - binfo, - vrf_id, - src_address, - dst_address, - proto, pkt_len, - &srcp, &dstp); + bgp_pbr_policyroute_remove_from_zebra(bgp, + binfo, bpf); } } @@ -1494,19 +1490,10 @@ static void bgp_pbr_enumerate_action_src_dst(struct bgp_pbr_match_val src[], dstp.min_port = 0; if (add) bgp_pbr_policyroute_add_to_zebra(bgp, binfo, - vrf_id, src_address, - dst_address, - nh, rate, proto, - pkt_len, &srcp, &dstp); + bpf, nh, rate); else - bgp_pbr_policyroute_remove_from_zebra( - bgp, - binfo, - vrf_id, - src_address, - dst_address, - proto, pkt_len, - &srcp, &dstp); + bgp_pbr_policyroute_remove_from_zebra(bgp, + binfo, bpf); break; } } @@ -1527,8 +1514,10 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, struct bgp_pbr_range_port range, range_icmp_code; struct bgp_pbr_range_port pkt_len; bool enum_icmp = false; + struct bgp_pbr_filter bpf; memset(&nh, 0, sizeof(struct nexthop)); + memset(&bpf, 0, sizeof(struct bgp_pbr_filter)); if (api->match_bitmask & PREFIX_SRC_PRESENT) src = &api->src_prefix; if (api->match_bitmask & PREFIX_DST_PRESENT) @@ -1586,22 +1575,25 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, bgp_pbr_extract(api->packet_length, api->match_packet_length_num, &pkt_len); + bpf.vrf_id = api->vrf_id; + bpf.src = src; + bpf.dst = dst; + bpf.protocol = proto; + bpf.pkt_len = &pkt_len; + bpf.src_port = srcp; + bpf.dst_port = dstp; if (!add) { if (enum_icmp) { return bgp_pbr_enumerate_action_src_dst(api->icmp_type, api->match_icmp_type_num, api->icmp_code, api->match_icmp_code_num, - src, dst, proto, &pkt_len, - bgp, binfo, add, - api->vrf_id, NULL, NULL); + &bpf, bgp, binfo, add, + NULL, NULL); } return bgp_pbr_policyroute_remove_from_zebra(bgp, binfo, - api->vrf_id, - src, dst, - proto, &pkt_len, - srcp, dstp); + &bpf); } /* no action for add = true */ for (i = 0; i < api->action_num; i++) { @@ -1616,14 +1608,11 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, api->match_icmp_type_num, api->icmp_code, api->match_icmp_code_num, - src, dst, proto, &pkt_len, - bgp, binfo, add, - api->vrf_id, &nh, &rate); + &bpf, bgp, binfo, add, + &nh, &rate); else - bgp_pbr_policyroute_add_to_zebra(bgp, binfo, - api->vrf_id, src, dst, - &nh, &rate, proto, - &pkt_len, srcp, dstp); + bgp_pbr_policyroute_add_to_zebra(bgp, binfo, &bpf, + &nh, &rate); } else { /* update rate. can be reentrant */ rate = api->actions[i].u.r.rate; @@ -1668,15 +1657,11 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, api->match_icmp_type_num, api->icmp_code, api->match_icmp_code_num, - src, dst, proto, &pkt_len, - bgp, binfo, add, - api->vrf_id, &nh, &rate); + &bpf, bgp, binfo, add, + &nh, &rate); else - bgp_pbr_policyroute_add_to_zebra(bgp, binfo, - api->vrf_id, - src, dst, - &nh, &rate, proto, - &pkt_len, srcp, dstp); + bgp_pbr_policyroute_add_to_zebra(bgp, binfo, &bpf, + &nh, &rate); /* XXX combination with REDIRECT_VRF * + REDIRECT_NH_IP not done */ @@ -1690,15 +1675,11 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, api->match_icmp_type_num, api->icmp_code, api->match_icmp_code_num, - src, dst, proto, &pkt_len, - bgp, binfo, add, - api->vrf_id, &nh, &rate); + &bpf, bgp, binfo, add, + &nh, &rate); else bgp_pbr_policyroute_add_to_zebra(bgp, binfo, - api->vrf_id, - src, dst, - &nh, &rate, proto, - &pkt_len, srcp, dstp); + &bpf, &nh, &rate); continue_loop = 0; break; case ACTION_MARKING: From e38991c1fa1d8494eb50e8ef8db9618a23cf5f29 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Tue, 12 Jun 2018 18:26:35 +0200 Subject: [PATCH 09/28] bgpd: fix display with flowspec tcp flags option When displaying RIB FS summary, the TCP option is not displayed. Signed-off-by: Philippe Guibert --- bgpd/bgp_flowspec_util.c | 27 ++++++++++++++------------- bgpd/bgp_flowspec_vty.c | 2 +- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/bgpd/bgp_flowspec_util.c b/bgpd/bgp_flowspec_util.c index 956cf28c211d..db85c642aacb 100644 --- a/bgpd/bgp_flowspec_util.c +++ b/bgpd/bgp_flowspec_util.c @@ -348,32 +348,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; diff --git a/bgpd/bgp_flowspec_vty.c b/bgpd/bgp_flowspec_vty.c index b21e5ae0dcc4..6b0390ed0fb3 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"}, From d9e3799612f8cfd93dc7a333763aca12d82d3872 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Tue, 12 Jun 2018 18:31:52 +0200 Subject: [PATCH 10/28] bgpd: support for flowspec tcp flags Ability to handle flowspec tcp flags. Signed-off-by: Philippe Guibert --- bgpd/bgp_pbr.c | 103 +++++++++++++++++++++++++++++++++++++++++------ bgpd/bgp_pbr.h | 3 ++ bgpd/bgp_zebra.c | 2 + lib/pbr.h | 14 +++++++ 4 files changed, 110 insertions(+), 12 deletions(-) diff --git a/bgpd/bgp_pbr.c b/bgpd/bgp_pbr.c index c798a11a1941..a55486064799 100644 --- a/bgpd/bgp_pbr.c +++ b/bgpd/bgp_pbr.c @@ -183,6 +183,15 @@ struct bgp_pbr_range_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 */ @@ -194,18 +203,44 @@ struct bgp_pbr_filter { 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; }; +/* TCP : FIN and SYN -> val = ALL; mask = 3 + * TCP : not (FIN and SYN) -> val = ALL; mask = ALL & ~(FIN|RST) + */ static bool bgp_pbr_extract_enumerate(struct bgp_pbr_match_val list[], - int num) + int num, uint8_t unary_operator, + struct bgp_pbr_val_mask *valmask) { int i = 0; + if (valmask) + memset(valmask, 0, sizeof(struct bgp_pbr_val_mask)); for (i = 0; i < num; i++) { - if (list[i].compare_operator != - OPERATOR_COMPARE_EQUAL_TO) + 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)) { + if (valmask) + valmask->mask |= + TCP_HEADER_ALL_FLAGS & + ~(list[i].value); + continue; + } + return false; + } + if (valmask) + valmask->mask |= list[i].value; } + valmask->mask = TCP_HEADER_ALL_FLAGS; return true; } @@ -271,7 +306,7 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api) * - combination src/dst => drop * - combination srcport + @IP */ - if (api->match_dscp_num || api->match_tcpflags_num) { + if (api->match_dscp_num) { if (BGP_DEBUG(pbr, PBR)) { bgp_pbr_print_policy_route(api); zlog_debug("BGP: some SET actions not supported by Zebra. ignoring."); @@ -309,9 +344,18 @@ 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, NULL)) { + 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)) { + api->match_icmp_type_num, + OPERATOR_UNARY_OR, NULL)) { if (BGP_DEBUG(pbr, PBR)) zlog_debug("BGP: match icmp type operations:" "too complex. ignoring."); @@ -321,7 +365,8 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api) } 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)) { + api->match_icmp_code_num, + OPERATOR_UNARY_OR, NULL)) { if (BGP_DEBUG(pbr, PBR)) zlog_debug("BGP: match icmp code operations:" "too complex. ignoring."); @@ -591,6 +636,8 @@ uint32_t bgp_pbr_match_hash_key(void *arg) 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); return jhash_1word(pbm->type, key); } @@ -619,6 +666,12 @@ int bgp_pbr_match_hash_equal(const void *arg1, const void *arg2) 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; + return 1; } @@ -1089,6 +1142,10 @@ static void bgp_pbr_policyroute_remove_from_zebra(struct bgp *bgp, temp.pkt_len_min = pkt_len->min_port; if (pkt_len && pkt_len->max_port) temp.pkt_len_max = pkt_len->max_port; + if (bpf->tcp_flags) { + temp.tcp_flags = bpf->tcp_flags->val; + temp.tcp_mask_flags = bpf->tcp_flags->mask; + } if (bpf->src == NULL || bpf->dst == NULL) { if (temp.flags & (MATCH_PORT_DST_SET | MATCH_PORT_SRC_SET)) @@ -1155,12 +1212,15 @@ static void bgp_pbr_policyroute_add_to_zebra(struct bgp *bgp, 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, 64, "type %d, code %d", + 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) { @@ -1194,6 +1254,14 @@ static void bgp_pbr_policyroute_add_to_zebra(struct bgp *bgp, pkt_len->max_port : pkt_len->min_port); } + 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); + } zlog_info("BGP: adding FS PBR from %s to %s, %s %s", bpf->src == NULL ? "" : prefix2str(bpf->src, bufsrc, sizeof(bufsrc)), @@ -1260,7 +1328,10 @@ static void bgp_pbr_policyroute_add_to_zebra(struct bgp *bgp, temp.pkt_len_min = pkt_len->min_port; if (pkt_len && pkt_len->max_port) temp.pkt_len_max = pkt_len->max_port; - + if (bpf->tcp_flags) { + temp.tcp_flags = bpf->tcp_flags->val; + temp.tcp_mask_flags = bpf->tcp_flags->mask; + } temp.action = bpa; bpm = hash_get(bgp->pbr_match_hash, &temp, bgp_pbr_match_alloc_intern); @@ -1550,7 +1621,8 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, if (api->match_icmp_type_num >= 1) { proto = IPPROTO_ICMP; if (bgp_pbr_extract_enumerate(api->icmp_type, - api->match_icmp_type_num)) + api->match_icmp_type_num, + OPERATOR_UNARY_OR, NULL)) enum_icmp = true; else { bgp_pbr_extract(api->icmp_type, @@ -1562,7 +1634,8 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, if (api->match_icmp_code_num >= 1) { proto = IPPROTO_ICMP; if (bgp_pbr_extract_enumerate(api->icmp_code, - api->match_icmp_code_num)) + api->match_icmp_code_num, + OPERATOR_UNARY_OR, NULL)) enum_icmp = true; else { bgp_pbr_extract(api->icmp_code, @@ -1571,15 +1644,21 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, dstp = &range_icmp_code; } } - if (api->match_packet_length_num >= 1) + + if (api->match_tcpflags_num) + bgp_pbr_extract_enumerate(api->tcpflags, + api->match_tcpflags_num, + OPERATOR_UNARY_AND, bpf.tcp_flags); + if (api->match_packet_length_num >= 1) { bgp_pbr_extract(api->packet_length, api->match_packet_length_num, &pkt_len); + bpf.pkt_len = &pkt_len; + } bpf.vrf_id = api->vrf_id; bpf.src = src; bpf.dst = dst; bpf.protocol = proto; - bpf.pkt_len = &pkt_len; bpf.src_port = srcp; bpf.dst_port = dstp; if (!add) { diff --git a/bgpd/bgp_pbr.h b/bgpd/bgp_pbr.h index 9d578a183b58..a39fba82c175 100644 --- a/bgpd/bgp_pbr.h +++ b/bgpd/bgp_pbr.h @@ -134,6 +134,7 @@ 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; @@ -187,6 +188,8 @@ struct bgp_pbr_match { uint16_t pkt_len_min; uint16_t pkt_len_max; + uint16_t tcp_flags; + uint16_t tcp_mask_flags; vrf_id_t vrf_id; diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index a8bc7e953310..9387a0d777fb 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -2235,6 +2235,8 @@ static void bgp_encode_pbr_iptable_match(struct stream *s, 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); } /* BGP has established connection with Zebra. */ diff --git a/lib/pbr.h b/lib/pbr.h index 401cfb08133d..832788d78434 100644 --- a/lib/pbr.h +++ b/lib/pbr.h @@ -83,6 +83,20 @@ 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) + extern int zapi_pbr_rule_encode(uint8_t cmd, struct stream *s, struct pbr_rule *zrule); From 8a18a5cbbe9f344eba6a722a45b67bece4fe7ac5 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Tue, 12 Jun 2018 18:32:21 +0200 Subject: [PATCH 11/28] zebra: handling of policy routing iptable tcpflags Signed-off-by: Philippe Guibert --- zebra/zapi_msg.c | 2 ++ zebra/zebra_pbr.c | 50 +++++++++++++++++++++++++++++++++++++++++++++++ zebra/zebra_pbr.h | 4 ++++ 3 files changed, 56 insertions(+) diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index 65cf4ba8be8b..119476a38285 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -2908,6 +2908,8 @@ static inline void zread_iptable(ZAPI_HANDLER_ARGS) 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_GETL(s, zpi.nb_interface); zebra_pbr_iptable_update_interfacelist(s, &zpi); diff --git a/zebra/zebra_pbr.c b/zebra/zebra_pbr.c index 07b29bc40399..ea66689fad92 100644 --- a/zebra/zebra_pbr.c +++ b/zebra/zebra_pbr.c @@ -81,6 +81,17 @@ const struct message icmp_typecode_str[] = { {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 function declarations */ DEFINE_HOOK(zebra_pbr_ipset_entry_wrap_script_get_stat, (struct zebra_ns *zns, struct zebra_pbr_ipset_entry *ipset, @@ -361,6 +372,8 @@ uint32_t zebra_pbr_iptable_hash_key(void *arg) 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); return jhash_3words(iptable->filter_bm, iptable->type, iptable->unique, key); } @@ -389,6 +402,10 @@ int zebra_pbr_iptable_hash_equal(const void *arg1, const void *arg2) 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; return 1; } @@ -955,6 +972,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) @@ -1028,6 +1065,19 @@ static int zebra_pbr_show_iptable_walkcb(struct hash_backet *backet, void *arg) 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); + } 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 09004e795330..8d06aa85949c 100644 --- a/zebra/zebra_pbr.h +++ b/zebra/zebra_pbr.h @@ -135,6 +135,8 @@ struct zebra_pbr_iptable { uint16_t pkt_len_min; uint16_t pkt_len_max; + uint16_t tcp_flags; + uint16_t tcp_mask_flags; uint32_t nb_interface; @@ -234,6 +236,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, From 7b2c731331434137df18460fc7d6ffbde7fa31e8 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Wed, 13 Jun 2018 11:12:08 +0200 Subject: [PATCH 12/28] bgpd, lib: share flags values for iptable configuration Those flags can be shared between BGP and Zebra. That is why those flags are moved to common pbr.h header file. Signed-off-by: Philippe Guibert --- bgpd/bgp_pbr.c | 1 + bgpd/bgp_pbr.h | 6 ------ lib/pbr.h | 10 ++++++++++ 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/bgpd/bgp_pbr.c b/bgpd/bgp_pbr.c index a55486064799..bf09a999d477 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" diff --git a/bgpd/bgp_pbr.h b/bgpd/bgp_pbr.h index a39fba82c175..6a06ba3893ed 100644 --- a/bgpd/bgp_pbr.h +++ b/bgpd/bgp_pbr.h @@ -178,12 +178,6 @@ 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; diff --git a/lib/pbr.h b/lib/pbr.h index 832788d78434..72a870f1f3b2 100644 --- a/lib/pbr.h +++ b/lib/pbr.h @@ -97,6 +97,16 @@ struct pbr_rule { | 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) + extern int zapi_pbr_rule_encode(uint8_t cmd, struct stream *s, struct pbr_rule *zrule); From f43d659eee85297f583e18e9d594459e3fc0f1d6 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Wed, 13 Jun 2018 11:56:35 +0200 Subject: [PATCH 13/28] bgpd: do not add default route for flowspec for each FS entry Because the Flowspec entries are parsed first, then injected to Zebra, there are cases where the install feedback from zebra is not received. This leads to unnecessary add route events, whereas one should be enough. Signed-off-by: Philippe Guibert --- bgpd/bgp_pbr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bgpd/bgp_pbr.c b/bgpd/bgp_pbr.c index bf09a999d477..779bf2de6dd8 100644 --- a/bgpd/bgp_pbr.c +++ b/bgpd/bgp_pbr.c @@ -1393,7 +1393,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); From 6d47669ac11b045c9adba9dcfc1161f6de7180a6 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Mon, 18 Jun 2018 11:18:21 +0200 Subject: [PATCH 14/28] bgpd: extend enumerate API to handle or operations The flowspec enumerate list can either be and values or or values. In the latter case, a list is created that will be used later. Also, the API supports the check for both and or or operations. This API does not permit to handle both and and or operations at the same time. The list will have to be either and or or. An other API retrieves the operator unary value that is used: and or or. or 0 is the two operators are used at the same time. Signed-off-by: Philippe Guibert --- bgpd/bgp_pbr.c | 101 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 90 insertions(+), 11 deletions(-) diff --git a/bgpd/bgp_pbr.c b/bgpd/bgp_pbr.c index 779bf2de6dd8..2065d03356d8 100644 --- a/bgpd/bgp_pbr.c +++ b/bgpd/bgp_pbr.c @@ -37,6 +37,7 @@ 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); @@ -210,14 +211,22 @@ struct bgp_pbr_filter { /* TCP : FIN and SYN -> val = ALL; mask = 3 * TCP : not (FIN and SYN) -> val = ALL; mask = ALL & ~(FIN|RST) */ -static bool bgp_pbr_extract_enumerate(struct bgp_pbr_match_val list[], - int num, uint8_t unary_operator, - struct bgp_pbr_val_mask *valmask) +static bool bgp_pbr_extract_enumerate_unary(struct bgp_pbr_match_val list[], + int num, uint8_t unary_operator, + void *valmask) { int i = 0; - - if (valmask) - memset(valmask, 0, sizeof(struct bgp_pbr_val_mask)); + struct bgp_pbr_val_mask *and_valmask = NULL; + struct list *or_valmask = NULL; + + 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) @@ -230,21 +239,91 @@ static bool bgp_pbr_extract_enumerate(struct bgp_pbr_match_val list[], OPERATOR_COMPARE_LESS_THAN) && (list[i].compare_operator & OPERATOR_COMPARE_GREATER_THAN)) { - if (valmask) - valmask->mask |= + if (unary_operator == OPERATOR_UNARY_AND && and_valmask) + 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)); + and_valmask->val = TCP_HEADER_ALL_FLAGS; + and_valmask->mask |= TCP_HEADER_ALL_FLAGS & ~(list[i].value); + listnode_add (or_valmask, and_valmask); + } continue; } return false; } - if (valmask) - valmask->mask |= list[i].value; + if (unary_operator == OPERATOR_UNARY_AND && and_valmask) + 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)); + and_valmask->val = TCP_HEADER_ALL_FLAGS; + and_valmask->mask |= + TCP_HEADER_ALL_FLAGS & list[i].value; + listnode_add(or_valmask, and_valmask); + } } - valmask->mask = TCP_HEADER_ALL_FLAGS; + if (unary_operator == OPERATOR_UNARY_AND && and_valmask) + and_valmask->mask = 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) +{ + bool ret; + uint8_t unary_operator_val; + 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); + if (!ret && double_check) + ret = bgp_pbr_extract_enumerate_unary(list, num, + OPERATOR_UNARY_OR, + valmask); + 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[], From fd36d122cd7286bda3ad9a2fe9d6f401e87f65d3 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Mon, 18 Jun 2018 11:50:16 +0200 Subject: [PATCH 15/28] bgpd: use a bgp_pbr_or_filter structure to host tcpflags combinations tcp flags combinations ( or enumerates) are hosted in a structure that will be analysed later, when wanting to inject that information to zebra. Signed-off-by: Philippe Guibert --- bgpd/bgp_pbr.c | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/bgpd/bgp_pbr.c b/bgpd/bgp_pbr.c index 2065d03356d8..c8d5eeff6037 100644 --- a/bgpd/bgp_pbr.c +++ b/bgpd/bgp_pbr.c @@ -208,6 +208,14 @@ struct bgp_pbr_filter { struct bgp_pbr_val_mask *tcp_flags; }; +/* 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; +}; + /* TCP : FIN and SYN -> val = ALL; mask = 3 * TCP : not (FIN and SYN) -> val = ALL; mask = ALL & ~(FIN|RST) */ @@ -426,7 +434,8 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api) } if (!bgp_pbr_extract_enumerate(api->tcpflags, api->match_tcpflags_num, - OPERATOR_UNARY_AND, NULL)) { + OPERATOR_UNARY_AND | + OPERATOR_UNARY_OR, NULL)) { if (BGP_DEBUG(pbr, PBR)) zlog_debug("BGP: match tcp flags:" "too complex. ignoring."); @@ -1666,9 +1675,12 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, struct bgp_pbr_range_port pkt_len; bool enum_icmp = false; struct bgp_pbr_filter bpf; + uint8_t kind_enum; + struct bgp_pbr_or_filter bpof; 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) @@ -1725,10 +1737,20 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, } } - if (api->match_tcpflags_num) - bgp_pbr_extract_enumerate(api->tcpflags, - api->match_tcpflags_num, - OPERATOR_UNARY_AND, bpf.tcp_flags); + 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) { + bgp_pbr_extract_enumerate(api->tcpflags, + api->match_tcpflags_num, + OPERATOR_UNARY_AND, bpf.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); + } + } if (api->match_packet_length_num >= 1) { bgp_pbr_extract(api->packet_length, api->match_packet_length_num, From 674a711a265a89332e6e93656881bef66934530c Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Mon, 18 Jun 2018 11:52:19 +0200 Subject: [PATCH 16/28] bgpd: add a parameter to handle param or combinations Before adding/removing to zebra, flowspec entries parses the list of combinations or avaialble and creates contexts in order to be injected to zebra. Signed-off-by: Philippe Guibert --- bgpd/bgp_pbr.c | 76 ++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 62 insertions(+), 14 deletions(-) diff --git a/bgpd/bgp_pbr.c b/bgpd/bgp_pbr.c index c8d5eeff6037..75605cd10cf8 100644 --- a/bgpd/bgp_pbr.c +++ b/bgpd/bgp_pbr.c @@ -1174,7 +1174,7 @@ 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, +static void bgp_pbr_policyroute_remove_from_zebra_unit(struct bgp *bgp, struct bgp_info *binfo, struct bgp_pbr_filter *bpf) { @@ -1271,7 +1271,28 @@ static void bgp_pbr_policyroute_remove_from_zebra(struct bgp *bgp, } } -static void bgp_pbr_policyroute_add_to_zebra(struct bgp *bgp, +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 && bpof->tcpflags) { + struct listnode *node, *nnode; + struct bgp_pbr_val_mask *valmask; + + for (ALL_LIST_ELEMENTS(bpof->tcpflags, node, nnode, valmask)) { + bpf->tcp_flags = valmask; + bgp_pbr_policyroute_remove_from_zebra_unit(bgp, binfo, bpf); + XFREE(MTYPE_PBR_VALMASK, valmask); + listnode_delete(bpof->tcpflags, node); + } + list_delete_all_node(bpof->tcpflags); + bpof->tcpflags = NULL; + } else + bgp_pbr_policyroute_remove_from_zebra_unit(bgp, binfo, bpf); +} + +static void bgp_pbr_policyroute_add_to_zebra_unit(struct bgp *bgp, struct bgp_info *binfo, struct bgp_pbr_filter *bpf, struct nexthop *nh, @@ -1517,6 +1538,30 @@ static void bgp_pbr_policyroute_add_to_zebra(struct bgp *bgp, } +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 && bpof->tcpflags) { + struct listnode *node, *nnode; + struct bgp_pbr_val_mask *valmask; + + for (ALL_LIST_ELEMENTS(bpof->tcpflags, node, nnode, valmask)) { + bpf->tcp_flags = valmask; + bgp_pbr_policyroute_add_to_zebra_unit(bgp, binfo, bpf, nh, rate); + XFREE(MTYPE_PBR_VALMASK, valmask); + listnode_delete(bpof->tcpflags, node); + } + list_delete_all_node(bpof->tcpflags); + bpof->tcpflags = NULL; + } else + return bgp_pbr_policyroute_add_to_zebra_unit(bgp, binfo, + bpf, nh, rate); +} + static const struct message icmp_code_unreach_str[] = { { 9, "communication-prohibited-by-filtering"}, { 10, "destination-host-prohibited"}, @@ -1579,10 +1624,10 @@ static void bgp_pbr_enumerate_action_src_dst(struct bgp_pbr_match_val src[], dstp.min_port = dst[0].value; if (add) bgp_pbr_policyroute_add_to_zebra(bgp, binfo, - bpf, nh, rate); + bpf, NULL, nh, rate); else bgp_pbr_policyroute_remove_from_zebra(bgp, - binfo, bpf); + binfo, bpf, NULL); return; } /* parse icmp type and lookup appropriate icmp code @@ -1621,10 +1666,11 @@ static void bgp_pbr_enumerate_action_src_dst(struct bgp_pbr_match_val src[], icmp_typecode_configured = true; if (add) bgp_pbr_policyroute_add_to_zebra(bgp, binfo, - bpf, nh, rate); + bpf, NULL, + nh, rate); else bgp_pbr_policyroute_remove_from_zebra( - bgp, binfo, bpf); + bgp, binfo, bpf, NULL); } } /* create a list of ICMP type/code combinatories */ @@ -1635,10 +1681,10 @@ static void bgp_pbr_enumerate_action_src_dst(struct bgp_pbr_match_val src[], dstp.min_port = pnt->key; if (add) bgp_pbr_policyroute_add_to_zebra(bgp, binfo, - bpf, nh, rate); + bpf, NULL, nh, rate); else bgp_pbr_policyroute_remove_from_zebra(bgp, - binfo, bpf); + binfo, bpf, NULL); } } @@ -1650,10 +1696,10 @@ static void bgp_pbr_enumerate_action_src_dst(struct bgp_pbr_match_val src[], dstp.min_port = 0; if (add) bgp_pbr_policyroute_add_to_zebra(bgp, binfo, - bpf, nh, rate); + bpf, NULL, nh, rate); else bgp_pbr_policyroute_remove_from_zebra(bgp, - binfo, bpf); + binfo, bpf, NULL); break; } } @@ -1677,6 +1723,7 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, 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)); @@ -1741,6 +1788,7 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, 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); @@ -1774,7 +1822,7 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, } return bgp_pbr_policyroute_remove_from_zebra(bgp, binfo, - &bpf); + &bpf, &bpof); } /* no action for add = true */ for (i = 0; i < api->action_num; i++) { @@ -1793,7 +1841,7 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, &nh, &rate); else bgp_pbr_policyroute_add_to_zebra(bgp, binfo, &bpf, - &nh, &rate); + &bpof, &nh, &rate); } else { /* update rate. can be reentrant */ rate = api->actions[i].u.r.rate; @@ -1841,7 +1889,7 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, &bpf, bgp, binfo, add, &nh, &rate); else - bgp_pbr_policyroute_add_to_zebra(bgp, binfo, &bpf, + bgp_pbr_policyroute_add_to_zebra(bgp, binfo, &bpf, &bpof, &nh, &rate); /* XXX combination with REDIRECT_VRF * + REDIRECT_NH_IP not done @@ -1860,7 +1908,7 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, &nh, &rate); else bgp_pbr_policyroute_add_to_zebra(bgp, binfo, - &bpf, &nh, &rate); + &bpf, &bpof, &nh, &rate); continue_loop = 0; break; case ACTION_MARKING: From d7e3bf1b65195fb36e84e93b93587047546b1063 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Wed, 13 Jun 2018 11:59:07 +0200 Subject: [PATCH 17/28] *: add flowspec dscp handling Only one dscp value is accepted as filtering option. Signed-off-by: Philippe Guibert --- bgpd/bgp_pbr.c | 50 ++++++++++++++++++++++++++++++++++++++--------- bgpd/bgp_pbr.h | 1 + bgpd/bgp_zebra.c | 1 + lib/pbr.h | 2 ++ zebra/zapi_msg.c | 1 + zebra/zebra_pbr.c | 3 +++ zebra/zebra_pbr.h | 1 + 7 files changed, 50 insertions(+), 9 deletions(-) diff --git a/bgpd/bgp_pbr.c b/bgpd/bgp_pbr.c index 75605cd10cf8..20aa6773eebb 100644 --- a/bgpd/bgp_pbr.c +++ b/bgpd/bgp_pbr.c @@ -206,6 +206,7 @@ struct bgp_pbr_filter { 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; }; /* this structure is used to contain OR instructions @@ -394,15 +395,6 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api) * - combination src/dst => drop * - combination srcport + @IP */ - if (api->match_dscp_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:" @@ -481,6 +473,15 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api) "too complex. ignoring."); return 0; } + if (api->match_dscp_num > 1 || + !bgp_pbr_extract_enumerate(api->dscp, + api->match_dscp_num, + OPERATOR_UNARY_OR, NULL)) { + if (BGP_DEBUG(pbr, PBR)) + zlog_debug("BGP: match DSCP operations:" + "too complex. ignoring."); + return 0; + } /* no combinations with both src_port and dst_port * or port with src_port and dst_port */ @@ -727,6 +728,7 @@ uint32_t bgp_pbr_match_hash_key(void *arg) 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); return jhash_1word(pbm->type, key); } @@ -761,6 +763,8 @@ int bgp_pbr_match_hash_equal(const void *arg1, const void *arg2) if (r1->tcp_mask_flags != r2->tcp_mask_flags) return 0; + if (r1->dscp_value != r2->dscp_value) + return 0; return 1; } @@ -1235,6 +1239,13 @@ static void bgp_pbr_policyroute_remove_from_zebra_unit(struct bgp *bgp, 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->src == NULL || bpf->dst == NULL) { if (temp.flags & (MATCH_PORT_DST_SET | MATCH_PORT_SRC_SET)) @@ -1372,6 +1383,15 @@ static void bgp_pbr_policyroute_add_to_zebra_unit(struct bgp *bgp, 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 ? "" : prefix2str(bpf->src, bufsrc, sizeof(bufsrc)), @@ -1442,6 +1462,13 @@ static void bgp_pbr_policyroute_add_to_zebra_unit(struct bgp *bgp, 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; + } temp.action = bpa; bpm = hash_get(bgp->pbr_match_hash, &temp, bgp_pbr_match_alloc_intern); @@ -1805,6 +1832,11 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, &pkt_len); bpf.pkt_len = &pkt_len; } + if (api->match_dscp_num >= 1) { + bpf.dscp_presence = true; + bpf.dscp_value = api->dscp[0].value; + + } bpf.vrf_id = api->vrf_id; bpf.src = src; bpf.dst = dst; diff --git a/bgpd/bgp_pbr.h b/bgpd/bgp_pbr.h index 6a06ba3893ed..8a95b85df3e6 100644 --- a/bgpd/bgp_pbr.h +++ b/bgpd/bgp_pbr.h @@ -184,6 +184,7 @@ struct bgp_pbr_match { uint16_t pkt_len_max; uint16_t tcp_flags; uint16_t tcp_mask_flags; + uint8_t dscp_value; vrf_id_t vrf_id; diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index 9387a0d777fb..9036bfbab622 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -2237,6 +2237,7 @@ static void bgp_encode_pbr_iptable_match(struct stream *s, 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); } /* BGP has established connection with Zebra. */ diff --git a/lib/pbr.h b/lib/pbr.h index 72a870f1f3b2..8bc189edad6a 100644 --- a/lib/pbr.h +++ b/lib/pbr.h @@ -106,6 +106,8 @@ struct pbr_rule { #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) extern int zapi_pbr_rule_encode(uint8_t cmd, struct stream *s, struct pbr_rule *zrule); diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index 119476a38285..65626b79cde8 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -2910,6 +2910,7 @@ static inline void zread_iptable(ZAPI_HANDLER_ARGS) 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_GETL(s, zpi.nb_interface); zebra_pbr_iptable_update_interfacelist(s, &zpi); diff --git a/zebra/zebra_pbr.c b/zebra/zebra_pbr.c index ea66689fad92..2e232f4db13b 100644 --- a/zebra/zebra_pbr.c +++ b/zebra/zebra_pbr.c @@ -374,6 +374,7 @@ uint32_t zebra_pbr_iptable_hash_key(void *arg) 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); return jhash_3words(iptable->filter_bm, iptable->type, iptable->unique, key); } @@ -406,6 +407,8 @@ int zebra_pbr_iptable_hash_equal(const void *arg1, const void *arg2) return 0; if (r1->tcp_mask_flags != r2->tcp_mask_flags) return 0; + if (r1->dscp_value != r2->dscp_value) + return 0; return 1; } diff --git a/zebra/zebra_pbr.h b/zebra/zebra_pbr.h index 8d06aa85949c..a1509091a592 100644 --- a/zebra/zebra_pbr.h +++ b/zebra/zebra_pbr.h @@ -137,6 +137,7 @@ struct zebra_pbr_iptable { uint16_t pkt_len_max; uint16_t tcp_flags; uint16_t tcp_mask_flags; + uint8_t dscp_value; uint32_t nb_interface; From ccf48116200c8406b73f84372b54504c5eebf63b Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Tue, 19 Jun 2018 14:54:59 +0200 Subject: [PATCH 18/28] zebra: add show pbr iptable dscp information The iptable configured with dscp displays the dscp value configured. Signed-off-by: Philippe Guibert --- zebra/zebra_pbr.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/zebra/zebra_pbr.c b/zebra/zebra_pbr.c index 2e232f4db13b..ea103f9b7b0b 100644 --- a/zebra/zebra_pbr.c +++ b/zebra/zebra_pbr.c @@ -1081,6 +1081,11 @@ static int zebra_pbr_show_iptable_walkcb(struct hash_backet *backet, void *arg) 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); + } ret = hook_call(zebra_pbr_iptable_wrap_script_get_stat, zns, iptable, &pkts, &bytes); if (ret && pkts > 0) From 77bfc3d6694857f2b65b1a2288992f2eff643252 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Tue, 19 Jun 2018 15:02:15 +0200 Subject: [PATCH 19/28] bgpd: enumerate support for dscp values If one dscp value or an enumerate list of or values of dscp are provided, then the bgp pbr entries created will take into account the dscp values. Signed-off-by: Philippe Guibert --- bgpd/bgp_pbr.c | 110 +++++++++++++++++++++++++++++++------------------ 1 file changed, 69 insertions(+), 41 deletions(-) diff --git a/bgpd/bgp_pbr.c b/bgpd/bgp_pbr.c index 20aa6773eebb..539c421e5899 100644 --- a/bgpd/bgp_pbr.c +++ b/bgpd/bgp_pbr.c @@ -32,6 +32,7 @@ #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") @@ -222,7 +223,7 @@ struct bgp_pbr_or_filter { */ static bool bgp_pbr_extract_enumerate_unary(struct bgp_pbr_match_val list[], int num, uint8_t unary_operator, - void *valmask) + void *valmask, uint8_t type_entry) { int i = 0; struct bgp_pbr_val_mask *and_valmask = NULL; @@ -248,37 +249,52 @@ static bool bgp_pbr_extract_enumerate_unary(struct bgp_pbr_match_val list[], OPERATOR_COMPARE_LESS_THAN) && (list[i].compare_operator & OPERATOR_COMPARE_GREATER_THAN)) { - if (unary_operator == OPERATOR_UNARY_AND && and_valmask) - and_valmask->mask |= - TCP_HEADER_ALL_FLAGS & - ~(list[i].value); - else if (unary_operator == OPERATOR_UNARY_OR && or_valmask) { + 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 (type_entry == FLOWSPEC_DSCP) { + and_valmask->val = list[i].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)); - and_valmask->val = TCP_HEADER_ALL_FLAGS; - and_valmask->mask |= - TCP_HEADER_ALL_FLAGS & - ~(list[i].value); + 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) { + and_valmask->val = list[i].value; + and_valmask->mask = 1; /* inverse */ + } listnode_add (or_valmask, and_valmask); } continue; } return false; } - if (unary_operator == OPERATOR_UNARY_AND && and_valmask) - and_valmask->mask |= - TCP_HEADER_ALL_FLAGS & list[i].value; - else if (unary_operator == OPERATOR_UNARY_OR && or_valmask) { + 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)); - and_valmask->val = TCP_HEADER_ALL_FLAGS; - and_valmask->mask |= - TCP_HEADER_ALL_FLAGS & list[i].value; + 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) + and_valmask->val = list[i].value; listnode_add(or_valmask, and_valmask); } } - if (unary_operator == OPERATOR_UNARY_AND && and_valmask) - and_valmask->mask = TCP_HEADER_ALL_FLAGS; + if (unary_operator == OPERATOR_UNARY_AND && and_valmask + && type_entry == FLOWSPEC_TCP_FLAGS) + and_valmask->val = TCP_HEADER_ALL_FLAGS; return true; } @@ -287,10 +303,10 @@ static bool bgp_pbr_extract_enumerate_unary(struct bgp_pbr_match_val list[], */ static bool bgp_pbr_extract_enumerate(struct bgp_pbr_match_val list[], int num, uint8_t unary_operator, - void *valmask) + void *valmask, uint8_t type_entry) { bool ret; - uint8_t unary_operator_val; + uint8_t unary_operator_val = unary_operator; bool double_check = false; if ((unary_operator & OPERATOR_UNARY_OR) && @@ -300,11 +316,12 @@ static bool bgp_pbr_extract_enumerate(struct bgp_pbr_match_val list[], } else unary_operator_val = unary_operator; ret = bgp_pbr_extract_enumerate_unary(list, num, unary_operator_val, - valmask); + valmask, type_entry); if (!ret && double_check) ret = bgp_pbr_extract_enumerate_unary(list, num, OPERATOR_UNARY_OR, - valmask); + valmask, + type_entry); return ret; } @@ -427,7 +444,8 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api) if (!bgp_pbr_extract_enumerate(api->tcpflags, api->match_tcpflags_num, OPERATOR_UNARY_AND | - OPERATOR_UNARY_OR, NULL)) { + OPERATOR_UNARY_OR, NULL, + FLOWSPEC_TCP_FLAGS)) { if (BGP_DEBUG(pbr, PBR)) zlog_debug("BGP: match tcp flags:" "too complex. ignoring."); @@ -436,7 +454,8 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api) 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)) { + OPERATOR_UNARY_OR, NULL, + FLOWSPEC_ICMP_TYPE)) { if (BGP_DEBUG(pbr, PBR)) zlog_debug("BGP: match icmp type operations:" "too complex. ignoring."); @@ -447,7 +466,8 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api) 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)) { + OPERATOR_UNARY_OR, NULL, + FLOWSPEC_ICMP_CODE)) { if (BGP_DEBUG(pbr, PBR)) zlog_debug("BGP: match icmp code operations:" "too complex. ignoring."); @@ -473,14 +493,15 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api) "too complex. ignoring."); return 0; } - if (api->match_dscp_num > 1 || - !bgp_pbr_extract_enumerate(api->dscp, - api->match_dscp_num, - OPERATOR_UNARY_OR, NULL)) { - if (BGP_DEBUG(pbr, PBR)) - zlog_debug("BGP: match DSCP 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; + } } /* no combinations with both src_port and dst_port * or port with src_port and dst_port @@ -1788,7 +1809,8 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, proto = IPPROTO_ICMP; if (bgp_pbr_extract_enumerate(api->icmp_type, api->match_icmp_type_num, - OPERATOR_UNARY_OR, NULL)) + OPERATOR_UNARY_OR, NULL, + FLOWSPEC_ICMP_TYPE)) enum_icmp = true; else { bgp_pbr_extract(api->icmp_type, @@ -1801,7 +1823,8 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, proto = IPPROTO_ICMP; if (bgp_pbr_extract_enumerate(api->icmp_code, api->match_icmp_code_num, - OPERATOR_UNARY_OR, NULL)) + OPERATOR_UNARY_OR, NULL, + FLOWSPEC_ICMP_CODE)) enum_icmp = true; else { bgp_pbr_extract(api->icmp_code, @@ -1818,12 +1841,16 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, bpf.tcp_flags = &bpvm; bgp_pbr_extract_enumerate(api->tcpflags, api->match_tcpflags_num, - OPERATOR_UNARY_AND, bpf.tcp_flags); + 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); + OPERATOR_UNARY_OR, + bpof.tcpflags, + FLOWSPEC_TCP_FLAGS); } } if (api->match_packet_length_num >= 1) { @@ -1833,9 +1860,10 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, bpf.pkt_len = &pkt_len; } if (api->match_dscp_num >= 1) { - bpf.dscp_presence = true; - bpf.dscp_value = api->dscp[0].value; - + bpof.dscp = list_new(); + bgp_pbr_extract_enumerate(api->dscp, api->match_dscp_num, + OPERATOR_UNARY_OR, + bpof.dscp, FLOWSPEC_DSCP); } bpf.vrf_id = api->vrf_id; bpf.src = src; From 45b266618fad5cf27428dc0dc50dbf18203db2b5 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Tue, 19 Jun 2018 15:02:26 +0200 Subject: [PATCH 20/28] bgpd: introduce recursive operations for or flowspec operations So as to add or remove entries with flowspec or operations like tcp flags or dscp enum list, a mechanism is put in place that adds recursivity. Signed-off-by: Philippe Guibert --- bgpd/bgp_pbr.c | 137 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 111 insertions(+), 26 deletions(-) diff --git a/bgpd/bgp_pbr.c b/bgpd/bgp_pbr.c index 539c421e5899..cfea83242116 100644 --- a/bgpd/bgp_pbr.c +++ b/bgpd/bgp_pbr.c @@ -216,6 +216,7 @@ struct bgp_pbr_filter { */ struct bgp_pbr_or_filter { struct list *tcpflags; + struct list *dscp; }; /* TCP : FIN and SYN -> val = ALL; mask = 3 @@ -1303,25 +1304,65 @@ static void bgp_pbr_policyroute_remove_from_zebra_unit(struct bgp *bgp, } } +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); + if (type_entry == FLOWSPEC_TCP_FLAGS && bpof->tcpflags) { + next_type_entry = FLOWSPEC_DSCP; + orig_list = bpof->tcpflags; + target_val = &bpf->tcp_flags; + } else if (type_entry == FLOWSPEC_DSCP && bpof->dscp) { + next_type_entry = 0; + orig_list = bpof->dscp; + target_val = &bpf->dscp; + } else { + return bgp_pbr_policyroute_remove_from_zebra_recursive(bgp, binfo, + bpf, bpof, 0); + } + 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 && bpof->tcpflags) { - struct listnode *node, *nnode; - struct bgp_pbr_val_mask *valmask; - - for (ALL_LIST_ELEMENTS(bpof->tcpflags, node, nnode, valmask)) { - bpf->tcp_flags = valmask; - bgp_pbr_policyroute_remove_from_zebra_unit(bgp, binfo, bpf); - XFREE(MTYPE_PBR_VALMASK, valmask); - listnode_delete(bpof->tcpflags, node); - } - list_delete_all_node(bpof->tcpflags); - bpof->tcpflags = NULL; - } else + 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 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); } static void bgp_pbr_policyroute_add_to_zebra_unit(struct bgp *bgp, @@ -1586,6 +1627,44 @@ static void bgp_pbr_policyroute_add_to_zebra_unit(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); + if (type_entry == FLOWSPEC_TCP_FLAGS && bpof->tcpflags) { + next_type_entry = FLOWSPEC_DSCP; + orig_list = bpof->tcpflags; + target_val = &bpf->tcp_flags; + } else if (type_entry == FLOWSPEC_DSCP && bpof->dscp) { + next_type_entry = 0; + orig_list = bpof->dscp; + target_val = &bpf->dscp; + } else { + return bgp_pbr_policyroute_add_to_zebra_recursive(bgp, binfo, + bpf, bpof, nh, rate, 0); + } + 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, @@ -1593,21 +1672,27 @@ static void bgp_pbr_policyroute_add_to_zebra(struct bgp *bgp, struct nexthop *nh, float *rate) { - if (bpof && bpof->tcpflags) { - struct listnode *node, *nnode; - struct bgp_pbr_val_mask *valmask; - - for (ALL_LIST_ELEMENTS(bpof->tcpflags, node, nnode, valmask)) { - bpf->tcp_flags = valmask; - bgp_pbr_policyroute_add_to_zebra_unit(bgp, binfo, bpf, nh, rate); - XFREE(MTYPE_PBR_VALMASK, valmask); - listnode_delete(bpof->tcpflags, node); - } - list_delete_all_node(bpof->tcpflags); - bpof->tcpflags = NULL; - } else + 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 + 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); } static const struct message icmp_code_unreach_str[] = { From 28fad153b003bae6136d8f8965b105fa8639188b Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Tue, 19 Jun 2018 16:02:55 +0200 Subject: [PATCH 21/28] bgpd: support for enumerate pkt len The packet length can be injected from fs entry with an enumerate list; the negation of the value is also taken into account. Signed-off-by: Philippe Guibert --- bgpd/bgp_pbr.c | 107 ++++++++++++++++++++++++++++++++++++++++--------- lib/pbr.h | 1 + 2 files changed, 88 insertions(+), 20 deletions(-) diff --git a/bgpd/bgp_pbr.c b/bgpd/bgp_pbr.c index cfea83242116..ac56369bf1cd 100644 --- a/bgpd/bgp_pbr.c +++ b/bgpd/bgp_pbr.c @@ -208,6 +208,7 @@ struct bgp_pbr_filter { 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; }; /* this structure is used to contain OR instructions @@ -217,6 +218,7 @@ struct bgp_pbr_filter { struct bgp_pbr_or_filter { struct list *tcpflags; struct list *dscp; + struct list *pkt_len; }; /* TCP : FIN and SYN -> val = ALL; mask = 3 @@ -255,7 +257,8 @@ static bool bgp_pbr_extract_enumerate_unary(struct bgp_pbr_match_val list[], and_valmask->mask |= TCP_HEADER_ALL_FLAGS & ~(list[i].value); - } else if (type_entry == FLOWSPEC_DSCP) { + } else if (type_entry == FLOWSPEC_DSCP || + type_entry == FLOWSPEC_PKT_LEN) { and_valmask->val = list[i].value; and_valmask->mask = 1; /* inverse */ } @@ -267,7 +270,8 @@ static bool bgp_pbr_extract_enumerate_unary(struct bgp_pbr_match_val list[], and_valmask->mask |= TCP_HEADER_ALL_FLAGS & ~(list[i].value); - } else if (type_entry == FLOWSPEC_DSCP) { + } else if (type_entry == FLOWSPEC_DSCP || + type_entry == FLOWSPEC_PKT_LEN) { and_valmask->val = list[i].value; and_valmask->mask = 1; /* inverse */ } @@ -288,7 +292,8 @@ static bool bgp_pbr_extract_enumerate_unary(struct bgp_pbr_match_val list[], and_valmask->val = TCP_HEADER_ALL_FLAGS; and_valmask->mask |= TCP_HEADER_ALL_FLAGS & list[i].value; - } else if (type_entry == FLOWSPEC_DSCP) + } else if (type_entry == FLOWSPEC_DSCP || + type_entry == FLOWSPEC_PKT_LEN) and_valmask->val = list[i].value; listnode_add(or_valmask, and_valmask); } @@ -488,11 +493,23 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api) "too complex. ignoring."); return 0; } - if (!bgp_pbr_extract(api->packet_length, api->match_packet_length_num, NULL)) { - if (BGP_DEBUG(pbr, PBR)) - zlog_debug("BGP: match packet length operations:" + 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; + return 0; + } } if (api->match_dscp_num) { if (!bgp_pbr_extract_enumerate(api->dscp, api->match_dscp_num, @@ -1253,10 +1270,15 @@ static void bgp_pbr_policyroute_remove_from_zebra_unit(struct bgp *bgp, } temp2.proto = bpf->protocol; - if (pkt_len) + if (pkt_len) { temp.pkt_len_min = pkt_len->min_port; - if (pkt_len && pkt_len->max_port) - temp.pkt_len_max = pkt_len->max_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; @@ -1324,9 +1346,13 @@ static void bgp_pbr_policyroute_remove_from_zebra_recursive(struct bgp *bgp, orig_list = bpof->tcpflags; target_val = &bpf->tcp_flags; } else if (type_entry == FLOWSPEC_DSCP && bpof->dscp) { - next_type_entry = 0; + next_type_entry = FLOWSPEC_PKT_LEN; orig_list = bpof->dscp; target_val = &bpf->dscp; + } else if (type_entry == FLOWSPEC_PKT_LEN && bpof->pkt_len) { + next_type_entry = 0; + orig_list = bpof->pkt_len; + target_val = &bpf->pkt_len_val; } else { return bgp_pbr_policyroute_remove_from_zebra_recursive(bgp, binfo, bpf, bpof, 0); @@ -1356,6 +1382,10 @@ static void bgp_pbr_policyroute_remove_from_zebra(struct bgp *bgp, 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 bgp_pbr_policyroute_remove_from_zebra_unit(bgp, binfo, bpf); /* flush bpof */ @@ -1363,6 +1393,8 @@ static void bgp_pbr_policyroute_remove_from_zebra(struct bgp *bgp, 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); } static void bgp_pbr_policyroute_add_to_zebra_unit(struct bgp *bgp, @@ -1436,6 +1468,14 @@ static void bgp_pbr_policyroute_add_to_zebra_unit(struct bgp *bgp, 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, @@ -1516,10 +1556,15 @@ static void bgp_pbr_policyroute_add_to_zebra_unit(struct bgp *bgp, temp.flags |= MATCH_PORT_SRC_RANGE_SET; if (dst_port && dst_port->max_port) temp.flags |= MATCH_PORT_DST_RANGE_SET; - if (pkt_len) + if (pkt_len) { temp.pkt_len_min = pkt_len->min_port; - if (pkt_len && pkt_len->max_port) - temp.pkt_len_max = pkt_len->max_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; @@ -1649,9 +1694,13 @@ static void bgp_pbr_policyroute_add_to_zebra_recursive(struct bgp *bgp, orig_list = bpof->tcpflags; target_val = &bpf->tcp_flags; } else if (type_entry == FLOWSPEC_DSCP && bpof->dscp) { - next_type_entry = 0; + next_type_entry = FLOWSPEC_PKT_LEN; orig_list = bpof->dscp; target_val = &bpf->dscp; + } else if (type_entry == FLOWSPEC_PKT_LEN && bpof->pkt_len) { + next_type_entry = 0; + orig_list = bpof->pkt_len; + target_val = &bpf->pkt_len_val; } else { return bgp_pbr_policyroute_add_to_zebra_recursive(bgp, binfo, bpf, bpof, nh, rate, 0); @@ -1685,6 +1734,11 @@ static void bgp_pbr_policyroute_add_to_zebra(struct bgp *bgp, 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 bgp_pbr_policyroute_add_to_zebra_unit(bgp, binfo, bpf, nh, rate); @@ -1693,6 +1747,8 @@ static void bgp_pbr_policyroute_add_to_zebra(struct bgp *bgp, 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); } static const struct message icmp_code_unreach_str[] = { @@ -1938,11 +1994,22 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, FLOWSPEC_TCP_FLAGS); } } - if (api->match_packet_length_num >= 1) { - bgp_pbr_extract(api->packet_length, - api->match_packet_length_num, - &pkt_len); - bpf.pkt_len = &pkt_len; + 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(); diff --git a/lib/pbr.h b/lib/pbr.h index 8bc189edad6a..3da7aba23e2c 100644 --- a/lib/pbr.h +++ b/lib/pbr.h @@ -108,6 +108,7 @@ struct pbr_rule { #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) extern int zapi_pbr_rule_encode(uint8_t cmd, struct stream *s, struct pbr_rule *zrule); From 8fdd7f7d5c450dab21e25d6afa228c80319e22d8 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Wed, 20 Jun 2018 08:32:43 +0200 Subject: [PATCH 22/28] bgpd: align fragment flowspec decoding with tcpflags As fragment bitmask and tcpflags bitmask in flowspec protocol is encoded in the same way, it is not necessary to differentiate those two fields. Moreover, it overrides the initial fragment limit set to 1. It is now possible to handle multiple framgent values. Signed-off-by: Philippe Guibert --- bgpd/bgp_flowspec.c | 9 +--- bgpd/bgp_flowspec_util.c | 106 +++------------------------------------ bgpd/bgp_flowspec_util.h | 6 +-- bgpd/bgp_flowspec_vty.c | 12 ++--- bgpd/bgp_pbr.c | 7 +-- bgpd/bgp_pbr.h | 4 +- 6 files changed, 22 insertions(+), 122 deletions(-) diff --git a/bgpd/bgp_flowspec.c b/bgpd/bgp_flowspec.c index 5db7e37089bf..5f13e6a9dc23 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 db85c642aacb..1b874276613c 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) @@ -420,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) { @@ -624,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, @@ -638,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, @@ -647,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 e4454ab4dba3..2d16e57a36fd 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 6b0390ed0fb3..90acd8fcb16f 100644 --- a/bgpd/bgp_flowspec_vty.c +++ b/bgpd/bgp_flowspec_vty.c @@ -173,7 +173,7 @@ void bgp_fs_nlri_get_string(unsigned char *nlri_content, size_t len, ptr += len_written; break; case FLOWSPEC_TCP_FLAGS: - ret = bgp_flowspec_tcpflags_decode( + ret = bgp_flowspec_bitmask_decode( type_util, nlri_content+offset, len - offset, @@ -221,11 +221,11 @@ void bgp_fs_nlri_get_string(unsigned char *nlri_content, size_t len, ptr += len_written; break; case FLOWSPEC_FRAGMENT: - ret = bgp_flowspec_fragment_type_decode( - type_util, - nlri_content + offset, - len - offset, local_string, - &error); + ret = bgp_flowspec_bitmask_decode( + type_util, + nlri_content+offset, + len - offset, + local_string, &error); if (ret <= 0) break; if (json_path) { diff --git a/bgpd/bgp_pbr.c b/bgpd/bgp_pbr.c index ac56369bf1cd..1fbc9826b294 100644 --- a/bgpd/bgp_pbr.c +++ b/bgpd/bgp_pbr.c @@ -1071,10 +1071,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 diff --git a/bgpd/bgp_pbr.h b/bgpd/bgp_pbr.h index 8a95b85df3e6..d63d3c89c8f7 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,6 +118,7 @@ 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; @@ -136,7 +136,7 @@ struct bgp_pbr_entry_main { 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]; From a197ea1e10b26f5b209b0660deb1a802a750012d Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Wed, 20 Jun 2018 13:55:20 +0200 Subject: [PATCH 23/28] bgpd: support for flowspec fragment list into policy routing The flowspec fragment attribute is taken into account to be pushed in BGP policy routing entries. Valid values are enumerate list of 1, 2, 4, or 8 values. no combined value is supported yet. Signed-off-by: Philippe Guibert --- bgpd/bgp_pbr.c | 91 ++++++++++++++++++++++++++++++++++++++++++++++-- bgpd/bgp_pbr.h | 1 + bgpd/bgp_zebra.c | 1 + lib/pbr.h | 1 + 4 files changed, 91 insertions(+), 3 deletions(-) diff --git a/bgpd/bgp_pbr.c b/bgpd/bgp_pbr.c index 1fbc9826b294..6ab155573c18 100644 --- a/bgpd/bgp_pbr.c +++ b/bgpd/bgp_pbr.c @@ -209,6 +209,7 @@ struct bgp_pbr_filter { 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 @@ -219,10 +220,14 @@ struct bgp_pbr_or_filter { struct list *tcpflags; struct list *dscp; struct list *pkt_len; + struct list *fragment; }; /* 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, @@ -258,7 +263,8 @@ static bool bgp_pbr_extract_enumerate_unary(struct bgp_pbr_match_val list[], TCP_HEADER_ALL_FLAGS & ~(list[i].value); } else if (type_entry == FLOWSPEC_DSCP || - type_entry == FLOWSPEC_PKT_LEN) { + type_entry == FLOWSPEC_PKT_LEN || + type_entry == FLOWSPEC_FRAGMENT) { and_valmask->val = list[i].value; and_valmask->mask = 1; /* inverse */ } @@ -271,6 +277,7 @@ static bool bgp_pbr_extract_enumerate_unary(struct bgp_pbr_match_val list[], TCP_HEADER_ALL_FLAGS & ~(list[i].value); } else if (type_entry == FLOWSPEC_DSCP || + type_entry == FLOWSPEC_FRAGMENT || type_entry == FLOWSPEC_PKT_LEN) { and_valmask->val = list[i].value; and_valmask->mask = 1; /* inverse */ @@ -293,6 +300,7 @@ static bool bgp_pbr_extract_enumerate_unary(struct bgp_pbr_match_val list[], and_valmask->mask |= TCP_HEADER_ALL_FLAGS & list[i].value; } else if (type_entry == FLOWSPEC_DSCP || + type_entry == FLOWSPEC_FRAGMENT || type_entry == FLOWSPEC_PKT_LEN) and_valmask->val = list[i].value; listnode_add(or_valmask, and_valmask); @@ -521,6 +529,40 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api) 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 */ @@ -768,6 +810,7 @@ uint32_t bgp_pbr_match_hash_key(void *arg) 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); } @@ -804,6 +847,9 @@ int bgp_pbr_match_hash_equal(const void *arg1, const void *arg2) if (r1->dscp_value != r2->dscp_value) return 0; + + if (r1->fragment != r2->fragment) + return 0; return 1; } @@ -1291,6 +1337,11 @@ static void bgp_pbr_policyroute_remove_from_zebra_unit(struct bgp *bgp, 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 (bpf->src == NULL || bpf->dst == NULL) { if (temp.flags & (MATCH_PORT_DST_SET | MATCH_PORT_SRC_SET)) @@ -1351,9 +1402,13 @@ static void bgp_pbr_policyroute_remove_from_zebra_recursive(struct bgp *bgp, orig_list = bpof->dscp; target_val = &bpf->dscp; } else if (type_entry == FLOWSPEC_PKT_LEN && bpof->pkt_len) { - next_type_entry = 0; + next_type_entry = FLOWSPEC_FRAGMENT; orig_list = bpof->pkt_len; target_val = &bpf->pkt_len_val; + } else if (type_entry == FLOWSPEC_FRAGMENT && bpof->fragment) { + next_type_entry = 0; + orig_list = bpof->fragment; + target_val = &bpf->fragment; } else { return bgp_pbr_policyroute_remove_from_zebra_recursive(bgp, binfo, bpf, bpof, 0); @@ -1387,6 +1442,10 @@ static void bgp_pbr_policyroute_remove_from_zebra(struct bgp *bgp, 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 bgp_pbr_policyroute_remove_from_zebra_unit(bgp, binfo, bpf); /* flush bpof */ @@ -1396,6 +1455,8 @@ static void bgp_pbr_policyroute_remove_from_zebra(struct bgp *bgp, 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, @@ -1577,6 +1638,11 @@ static void bgp_pbr_policyroute_add_to_zebra_unit(struct bgp *bgp, 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); @@ -1699,9 +1765,13 @@ static void bgp_pbr_policyroute_add_to_zebra_recursive(struct bgp *bgp, orig_list = bpof->dscp; target_val = &bpf->dscp; } else if (type_entry == FLOWSPEC_PKT_LEN && bpof->pkt_len) { - next_type_entry = 0; + next_type_entry = FLOWSPEC_FRAGMENT; orig_list = bpof->pkt_len; target_val = &bpf->pkt_len_val; + } else if (type_entry == FLOWSPEC_FRAGMENT && bpof->fragment) { + next_type_entry = 0; + orig_list = bpof->fragment; + target_val = &bpf->fragment; } else { return bgp_pbr_policyroute_add_to_zebra_recursive(bgp, binfo, bpf, bpof, nh, rate, 0); @@ -1740,6 +1810,11 @@ static void bgp_pbr_policyroute_add_to_zebra(struct bgp *bgp, 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 bgp_pbr_policyroute_add_to_zebra_unit(bgp, binfo, bpf, nh, rate); @@ -1750,6 +1825,8 @@ static void bgp_pbr_policyroute_add_to_zebra(struct bgp *bgp, 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 const struct message icmp_code_unreach_str[] = { @@ -2018,6 +2095,14 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, 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; diff --git a/bgpd/bgp_pbr.h b/bgpd/bgp_pbr.h index d63d3c89c8f7..307a34e34f4c 100644 --- a/bgpd/bgp_pbr.h +++ b/bgpd/bgp_pbr.h @@ -185,6 +185,7 @@ struct bgp_pbr_match { uint16_t tcp_flags; uint16_t tcp_mask_flags; uint8_t dscp_value; + uint8_t fragment; vrf_id_t vrf_id; diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index 9036bfbab622..87c903614767 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -2238,6 +2238,7 @@ static void bgp_encode_pbr_iptable_match(struct stream *s, 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/lib/pbr.h b/lib/pbr.h index 3da7aba23e2c..0c447e605be5 100644 --- a/lib/pbr.h +++ b/lib/pbr.h @@ -109,6 +109,7 @@ struct pbr_rule { #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) extern int zapi_pbr_rule_encode(uint8_t cmd, struct stream *s, struct pbr_rule *zrule); From b0f0c45cc104a9602b3d3d82c8e1b607f8424595 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Wed, 20 Jun 2018 14:06:31 +0200 Subject: [PATCH 24/28] zebra: handle policy routing fragment handling incoming iptable entries with fragment parameter is handled. An iptable context is created for each fragment value received from BGP. Signed-off-by: Philippe Guibert --- zebra/zapi_msg.c | 1 + zebra/zebra_pbr.c | 20 ++++++++++++++++++++ zebra/zebra_pbr.h | 1 + 3 files changed, 22 insertions(+) diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index 65626b79cde8..5a80072e546b 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -2911,6 +2911,7 @@ static inline void zread_iptable(ZAPI_HANDLER_ARGS) 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_pbr.c b/zebra/zebra_pbr.c index ea103f9b7b0b..86e054795bb5 100644 --- a/zebra/zebra_pbr.c +++ b/zebra/zebra_pbr.c @@ -92,6 +92,14 @@ static const struct message tcp_value_str[] = { {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, @@ -375,6 +383,7 @@ uint32_t zebra_pbr_iptable_hash_key(void *arg) 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); } @@ -409,6 +418,8 @@ int zebra_pbr_iptable_hash_equal(const void *arg1, const void *arg2) return 0; if (r1->dscp_value != r2->dscp_value) return 0; + if (r1->fragment != r2->fragment) + return 0; return 1; } @@ -1086,6 +1097,15 @@ static int zebra_pbr_show_iptable_walkcb(struct hash_backet *backet, void *arg) 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 a1509091a592..3d26281d0dad 100644 --- a/zebra/zebra_pbr.h +++ b/zebra/zebra_pbr.h @@ -138,6 +138,7 @@ struct zebra_pbr_iptable { uint16_t tcp_flags; uint16_t tcp_mask_flags; uint8_t dscp_value; + uint8_t fragment; uint32_t nb_interface; From 67f51e93c54f3de33acb606316bde77ff8083d6e Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Wed, 20 Jun 2018 15:30:40 +0200 Subject: [PATCH 25/28] bgpd: fix recursive call combination The recursive algorithm was taking into account the fact that all the bpof structures were filled in. Because the dscp value was not given, the pkt_len parsing could not be achieved. Now the iteration takes into account each type according to the previous one, thus guaranting all parameters to be parsed. Signed-off-by: Philippe Guibert --- bgpd/bgp_pbr.c | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/bgpd/bgp_pbr.c b/bgpd/bgp_pbr.c index 6ab155573c18..c99202b9401d 100644 --- a/bgpd/bgp_pbr.c +++ b/bgpd/bgp_pbr.c @@ -1378,6 +1378,17 @@ static void bgp_pbr_policyroute_remove_from_zebra_unit(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; + return 0; +} + static void bgp_pbr_policyroute_remove_from_zebra_recursive(struct bgp *bgp, struct bgp_info *binfo, struct bgp_pbr_filter *bpf, @@ -1393,25 +1404,24 @@ static void bgp_pbr_policyroute_remove_from_zebra_recursive(struct bgp *bgp, 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) { - next_type_entry = FLOWSPEC_DSCP; orig_list = bpof->tcpflags; target_val = &bpf->tcp_flags; } else if (type_entry == FLOWSPEC_DSCP && bpof->dscp) { - next_type_entry = FLOWSPEC_PKT_LEN; orig_list = bpof->dscp; target_val = &bpf->dscp; } else if (type_entry == FLOWSPEC_PKT_LEN && bpof->pkt_len) { - next_type_entry = FLOWSPEC_FRAGMENT; orig_list = bpof->pkt_len; target_val = &bpf->pkt_len_val; } else if (type_entry == FLOWSPEC_FRAGMENT && bpof->fragment) { - next_type_entry = 0; orig_list = bpof->fragment; target_val = &bpf->fragment; } else { - return bgp_pbr_policyroute_remove_from_zebra_recursive(bgp, binfo, - bpf, bpof, 0); + 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; @@ -1756,25 +1766,23 @@ static void bgp_pbr_policyroute_add_to_zebra_recursive(struct bgp *bgp, 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) { - next_type_entry = FLOWSPEC_DSCP; orig_list = bpof->tcpflags; target_val = &bpf->tcp_flags; } else if (type_entry == FLOWSPEC_DSCP && bpof->dscp) { - next_type_entry = FLOWSPEC_PKT_LEN; orig_list = bpof->dscp; target_val = &bpf->dscp; } else if (type_entry == FLOWSPEC_PKT_LEN && bpof->pkt_len) { - next_type_entry = FLOWSPEC_FRAGMENT; orig_list = bpof->pkt_len; target_val = &bpf->pkt_len_val; } else if (type_entry == FLOWSPEC_FRAGMENT && bpof->fragment) { - next_type_entry = 0; orig_list = bpof->fragment; target_val = &bpf->fragment; } else { return bgp_pbr_policyroute_add_to_zebra_recursive(bgp, binfo, - bpf, bpof, nh, rate, 0); + bpf, bpof, nh, rate, + next_type_entry); } for (ALL_LIST_ELEMENTS(orig_list, node, nnode, valmask)) { *target_val = valmask; From 377f827d916d71f0201269d3dda89c5a93ae6db8 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Wed, 20 Jun 2018 16:59:17 +0200 Subject: [PATCH 26/28] bgpd: rework icmp enumerate list As the other enumerate list, icmp type and code are handled as the other combinations. The icmp type and code options are the last options to be injected into PBR. If icmp type is present only, all the filtering will apply to this icmp type. if icmp code is present only, then all the combination will be done with icmp type ranging from 0 to 255 values. Signed-off-by: Philippe Guibert --- bgpd/bgp_pbr.c | 338 ++++++++++++++++++++----------------------------- 1 file changed, 135 insertions(+), 203 deletions(-) diff --git a/bgpd/bgp_pbr.c b/bgpd/bgp_pbr.c index c99202b9401d..615b5723c42b 100644 --- a/bgpd/bgp_pbr.c +++ b/bgpd/bgp_pbr.c @@ -221,8 +221,15 @@ struct bgp_pbr_or_filter { 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); /* 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 @@ -283,7 +290,9 @@ static bool bgp_pbr_extract_enumerate_unary(struct bgp_pbr_match_val list[], 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; continue; } return false; @@ -300,6 +309,8 @@ static bool bgp_pbr_extract_enumerate_unary(struct bgp_pbr_match_val list[], 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; @@ -1386,9 +1397,80 @@ static uint8_t bgp_pbr_next_type_entry(uint8_t type_entry) 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, @@ -1417,6 +1499,11 @@ static void bgp_pbr_policyroute_remove_from_zebra_recursive(struct bgp *bgp, } 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, @@ -1456,6 +1543,10 @@ static void bgp_pbr_policyroute_remove_from_zebra(struct bgp *bgp, 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 */ @@ -1779,6 +1870,11 @@ static void bgp_pbr_policyroute_add_to_zebra_recursive(struct bgp *bgp, } 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, @@ -1823,6 +1919,10 @@ static void bgp_pbr_policyroute_add_to_zebra(struct bgp *bgp, 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); @@ -1835,149 +1935,10 @@ static void bgp_pbr_policyroute_add_to_zebra(struct bgp *bgp, list_delete_all_node(bpof->pkt_len); if (bpof->fragment) list_delete_all_node(bpof->fragment); -} - -static const struct message icmp_code_unreach_str[] = { - { 9, "communication-prohibited-by-filtering"}, - { 10, "destination-host-prohibited"}, - { 7, "destination-host-unknown"}, - { 6, "destination-network-unknown"}, - { 4, "fragmentation-needed"}, - { 14, "host-precedence-violation"}, - { 0, "network-unreachable"}, - { 12, "network-unreachable-for-tos"}, - { 3, "port-unreachable"}, - { 8, "source-host-isolated"}, - { 5, "source-route-failed"}, - {0} -}; - -static const struct message icmp_code_redirect_str[] = { - { 1, "redirect-for-host"}, - { 0, "redirect-for-network"}, - { 3, "redirect-for-tos-and-host"}, - { 2, "redirect-for-tos-and-net"}, - {0} -}; - -static const struct message icmp_code_exceed_str[] = { - { 1, "ttl-eq-zero-during-reassembly"}, - { 0, "ttl-eq-zero-during-transit"}, - {0} -}; - -static const struct message icmp_code_problem_str[] = { - { 1, "required-option-missing"}, - { 2, "ip-header-bad"}, - {0} -}; -static void bgp_pbr_enumerate_action_src_dst(struct bgp_pbr_match_val src[], - int src_num, - struct bgp_pbr_match_val dst[], - int dst_num, - struct bgp_pbr_filter *bpf, - struct bgp *bgp, - struct bgp_info *binfo, - bool add, - struct nexthop *nh, - float *rate) -{ - int i = 0, j; - struct bgp_pbr_range_port srcp, dstp; - - if (!bpf) - return; - if (bpf->protocol != IPPROTO_ICMP) - return; - bpf->src_port = &srcp; - bpf->dst_port = &dstp; - /* combinatory forced. ignore icmp type / code combinatory */ - if (src_num == 1 && dst_num == 1) { - srcp.max_port = 0; - dstp.max_port = 0; - srcp.min_port = src[0].value; - dstp.min_port = dst[0].value; - if (add) - bgp_pbr_policyroute_add_to_zebra(bgp, binfo, - bpf, NULL, nh, rate); - else - bgp_pbr_policyroute_remove_from_zebra(bgp, - binfo, bpf, NULL); - return; - } - /* 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 - */ - for (i = 0; i < src_num; i++) { - const struct message *pnt; - const struct message *pnt_code = NULL; - static struct message nt = {0}; - bool icmp_typecode_configured = false; - - srcp.min_port = src[i].value; - srcp.max_port = 0; - dstp.max_port = 0; - if (src[i].value == 3) - pnt_code = icmp_code_unreach_str; - else if (src[i].value == 5) - pnt_code = icmp_code_redirect_str; - else if (src[i].value == 11) - pnt_code = icmp_code_exceed_str; - else if (src[i].value == 12) - pnt_code = icmp_code_problem_str; - switch (src[i].value) { - case 3: - case 5: - case 11: - case 12: - for (j = 0; j < dst_num; j++) { - for (pnt = pnt_code; - pnt && memcmp(pnt, &nt, sizeof(struct message)); - pnt++) { - if (dst[i].value != pnt->key) - continue; - dstp.min_port = dst[i].value; - icmp_typecode_configured = true; - if (add) - bgp_pbr_policyroute_add_to_zebra(bgp, binfo, - bpf, NULL, - nh, rate); - else - bgp_pbr_policyroute_remove_from_zebra( - bgp, binfo, bpf, NULL); - } - } - /* create a list of ICMP type/code combinatories */ - if (!icmp_typecode_configured) { - for (pnt = pnt_code; - pnt && memcmp(pnt, &nt, sizeof(struct message)); - pnt++) { - dstp.min_port = pnt->key; - if (add) - bgp_pbr_policyroute_add_to_zebra(bgp, binfo, - bpf, NULL, nh, rate); - else - bgp_pbr_policyroute_remove_from_zebra(bgp, - binfo, bpf, NULL); - } - - } - break; - default: - /* icmp type is not one of the above - * forge an entry only based on the icmp type - */ - dstp.min_port = 0; - if (add) - bgp_pbr_policyroute_add_to_zebra(bgp, binfo, - bpf, NULL, nh, rate); - else - bgp_pbr_policyroute_remove_from_zebra(bgp, - binfo, bpf, NULL); - break; - } - } + 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, @@ -1994,7 +1955,6 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, struct bgp_pbr_range_port *srcp = NULL, *dstp = NULL; struct bgp_pbr_range_port range, range_icmp_code; struct bgp_pbr_range_port pkt_len; - bool enum_icmp = false; struct bgp_pbr_filter bpf; uint8_t kind_enum; struct bgp_pbr_or_filter bpof; @@ -2034,30 +1994,32 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, } if (api->match_icmp_type_num >= 1) { proto = IPPROTO_ICMP; - if (bgp_pbr_extract_enumerate(api->icmp_type, - api->match_icmp_type_num, - OPERATOR_UNARY_OR, NULL, - FLOWSPEC_ICMP_TYPE)) - enum_icmp = true; - else { - bgp_pbr_extract(api->icmp_type, - api->match_icmp_type_num, - &range); + 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_enumerate(api->icmp_code, - api->match_icmp_code_num, - OPERATOR_UNARY_OR, NULL, - FLOWSPEC_ICMP_CODE)) - enum_icmp = true; - else { - bgp_pbr_extract(api->icmp_code, - api->match_icmp_code_num, - &range_icmp_code); + 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); } } @@ -2117,19 +2079,10 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, bpf.protocol = proto; bpf.src_port = srcp; bpf.dst_port = dstp; - if (!add) { - if (enum_icmp) { - return bgp_pbr_enumerate_action_src_dst(api->icmp_type, - api->match_icmp_type_num, - api->icmp_code, - api->match_icmp_code_num, - &bpf, bgp, binfo, add, - NULL, NULL); - } + if (!add) 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) { @@ -2138,16 +2091,9 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, if (api->actions[i].u.r.rate == 0) { nh.vrf_id = api->vrf_id; nh.type = NEXTHOP_TYPE_BLACKHOLE; - if (enum_icmp) - bgp_pbr_enumerate_action_src_dst(api->icmp_type, - api->match_icmp_type_num, - api->icmp_code, - api->match_icmp_code_num, - &bpf, bgp, binfo, add, - &nh, &rate); - else - bgp_pbr_policyroute_add_to_zebra(bgp, binfo, &bpf, - &bpof, &nh, &rate); + bgp_pbr_policyroute_add_to_zebra(bgp, binfo, + &bpf, &bpof, + &nh, &rate); } else { /* update rate. can be reentrant */ rate = api->actions[i].u.r.rate; @@ -2187,16 +2133,9 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, nh.gate.ipv4.s_addr = api->actions[i].u.zr.redirect_ip_v4.s_addr; nh.vrf_id = api->vrf_id; - if (enum_icmp) - bgp_pbr_enumerate_action_src_dst(api->icmp_type, - api->match_icmp_type_num, - api->icmp_code, - api->match_icmp_code_num, - &bpf, bgp, binfo, add, - &nh, &rate); - else - bgp_pbr_policyroute_add_to_zebra(bgp, binfo, &bpf, &bpof, - &nh, &rate); + bgp_pbr_policyroute_add_to_zebra(bgp, binfo, + &bpf, &bpof, + &nh, &rate); /* XXX combination with REDIRECT_VRF * + REDIRECT_NH_IP not done */ @@ -2205,16 +2144,9 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, case ACTION_REDIRECT: nh.vrf_id = api->actions[i].u.redirect_vrf; nh.type = NEXTHOP_TYPE_IPV4; - if (enum_icmp) - bgp_pbr_enumerate_action_src_dst(api->icmp_type, - api->match_icmp_type_num, - api->icmp_code, - api->match_icmp_code_num, - &bpf, bgp, binfo, add, - &nh, &rate); - else - bgp_pbr_policyroute_add_to_zebra(bgp, binfo, - &bpf, &bpof, &nh, &rate); + bgp_pbr_policyroute_add_to_zebra(bgp, binfo, + &bpf, &bpof, + &nh, &rate); continue_loop = 0; break; case ACTION_MARKING: From 53dd0c19c283b2f871fd03ec3479bf1201359a92 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Thu, 21 Jun 2018 12:29:18 +0200 Subject: [PATCH 27/28] bgpd: add an icmp flag for flowspec icmp entries Some values for icmp type/code can not be encoded like port source or port destination. This is the case of 0 value that is authorized for icmp. Signed-off-by: Philippe Guibert --- bgpd/bgp_pbr.c | 43 ++++++++++++++++++++++++++----------------- lib/pbr.h | 1 + 2 files changed, 27 insertions(+), 17 deletions(-) diff --git a/bgpd/bgp_pbr.c b/bgpd/bgp_pbr.c index 615b5723c42b..43ff78d38e29 100644 --- a/bgpd/bgp_pbr.c +++ b/bgpd/bgp_pbr.c @@ -1310,7 +1310,9 @@ static void bgp_pbr_policyroute_remove_from_zebra_unit(struct bgp *bgp, prefix_copy(&temp2.dst, bpf->dst); } else temp2.dst.family = AF_INET; - 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; temp2.src_port_min = src_port->min_port; if (src_port->max_port) { @@ -1318,7 +1320,9 @@ static void bgp_pbr_policyroute_remove_from_zebra_unit(struct bgp *bgp, temp2.src_port_max = src_port->max_port; } } - 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; temp2.dst_port_min = dst_port->min_port; if (dst_port->max_port) { @@ -1692,33 +1696,38 @@ static void bgp_pbr_policyroute_add_to_zebra_unit(struct bgp *bgp, /* then look for bpm */ memset(&temp, 0, sizeof(temp)); - if (bpf->src == NULL || bpf->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 = bpf->vrf_id; if (bpf->src) temp.flags |= MATCH_IP_SRC_SET; 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) diff --git a/lib/pbr.h b/lib/pbr.h index 0c447e605be5..76b91e6205bb 100644 --- a/lib/pbr.h +++ b/lib/pbr.h @@ -110,6 +110,7 @@ struct pbr_rule { #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); From 936f6d185d10930b1f7cec1ff1e7131f1806cd32 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Fri, 22 Jun 2018 09:17:04 +0200 Subject: [PATCH 28/28] bgpd: rework enumerate function, handle not values The handling of reverse values is in a separate function. Signed-off-by: Philippe Guibert --- bgpd/bgp_pbr.c | 75 +++++++++++++++++++++++++++++++------------------- 1 file changed, 46 insertions(+), 29 deletions(-) diff --git a/bgpd/bgp_pbr.c b/bgpd/bgp_pbr.c index 43ff78d38e29..45ec21631c0f 100644 --- a/bgpd/bgp_pbr.c +++ b/bgpd/bgp_pbr.c @@ -230,6 +230,45 @@ static void bgp_pbr_policyroute_add_to_zebra_unit(struct bgp *bgp, 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 @@ -243,6 +282,7 @@ static bool bgp_pbr_extract_enumerate_unary(struct bgp_pbr_match_val list[], 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) { @@ -264,35 +304,12 @@ static bool bgp_pbr_extract_enumerate_unary(struct bgp_pbr_match_val list[], OPERATOR_COMPARE_LESS_THAN) && (list[i].compare_operator & OPERATOR_COMPARE_GREATER_THAN)) { - 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 (type_entry == FLOWSPEC_DSCP || - type_entry == FLOWSPEC_PKT_LEN || - type_entry == FLOWSPEC_FRAGMENT) { - and_valmask->val = list[i].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 & - ~(list[i].value); - } else if (type_entry == FLOWSPEC_DSCP || - type_entry == FLOWSPEC_FRAGMENT || - type_entry == FLOWSPEC_PKT_LEN) { - and_valmask->val = list[i].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; + 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;