From 932404b7b09e42096fde0972fbe2976ff1de1b2b Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Mon, 4 Jun 2018 18:11:04 +0200 Subject: [PATCH] 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 | 329 ++++++++++++++++++++++++++++++++++++++++++++++--- bgpd/bgp_pbr.h | 1 + 2 files changed, 312 insertions(+), 18 deletions(-) diff --git a/bgpd/bgp_pbr.c b/bgpd/bgp_pbr.c index 5e36f91750..0a54183db7 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,7 +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_icmp_type_num || api->match_packet_length_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); @@ -259,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:" @@ -278,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:" @@ -294,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)) { @@ -984,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) { @@ -992,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) { @@ -1207,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, @@ -1219,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) @@ -1251,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) { @@ -1263,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; @@ -1306,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 */ @@ -1319,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 20edaf30b8..e869d0106d 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]; -- 2.39.5