diff options
author | Philippe Guibert <philippe.guibert@6wind.com> | 2018-03-30 12:25:03 +0200 |
---|---|---|
committer | Philippe Guibert <philippe.guibert@6wind.com> | 2018-05-25 15:49:38 +0200 |
commit | 1de7dffff707058b6633a664920bdca3def27860 (patch) | |
tree | 794e77f5fff9a113f3dd1737fdbda5009d3673c3 /bgpd | |
parent | bgpd: add 3 fields to ipset_entry : src,dst port, and proto (diff) | |
download | frr-1de7dffff707058b6633a664920bdca3def27860.tar.xz frr-1de7dffff707058b6633a664920bdca3def27860.zip |
bgpd: pbr support for port redirecting
Ability for BGP FS to convert some rules containining at least one
address and one port information into a pbr_match_entry rule.
Signed-off-by: Philippe Guibert <philippe.guibert@6wind.com>
Diffstat (limited to 'bgpd')
-rw-r--r-- | bgpd/bgp_pbr.c | 258 | ||||
-rw-r--r-- | bgpd/bgp_pbr.h | 6 |
2 files changed, 235 insertions, 29 deletions
diff --git a/bgpd/bgp_pbr.c b/bgpd/bgp_pbr.c index 04d6314fd..de94c0e03 100644 --- a/bgpd/bgp_pbr.c +++ b/bgpd/bgp_pbr.c @@ -169,7 +169,60 @@ static int sprintf_bgp_pbr_match_val(char *str, struct bgp_pbr_match_val *mval, _cnt++; \ } while (0) -/* return 1 if OK, 0 if validation should stop) */ +struct bgp_pbr_range_port { + uint16_t min_port; + uint16_t max_port; +}; + +/* return true if extraction ok + */ +static bool bgp_pbr_extract(struct bgp_pbr_match_val list[], + int num, + struct bgp_pbr_range_port *range) +{ + int i = 0; + bool exact_match = false; + + if (range) + memset(range, 0, sizeof(struct bgp_pbr_range_port)); + + if (num > 2) + return false; + for (i = 0; i < num; i++) { + if (i != 0 && (list[i].compare_operator == + OPERATOR_COMPARE_EQUAL_TO)) + return false; + if (i == 0 && (list[i].compare_operator == + OPERATOR_COMPARE_EQUAL_TO)) { + if (range) + range->min_port = list[i].value; + exact_match = true; + } + if (exact_match == true && i > 0) + return false; + if (list[i].compare_operator == + (OPERATOR_COMPARE_GREATER_THAN + + OPERATOR_COMPARE_EQUAL_TO)) { + if (range) + range->min_port = list[i].value; + } else if (list[i].compare_operator == + (OPERATOR_COMPARE_LESS_THAN + + OPERATOR_COMPARE_EQUAL_TO)) { + if (range) + range->max_port = list[i].value; + } else if (list[i].compare_operator == + OPERATOR_COMPARE_LESS_THAN) { + if (range) + range->max_port = list[i].value - 1; + } else if (list[i].compare_operator == + OPERATOR_COMPARE_GREATER_THAN) { + if (range) + range->min_port = list[i].value + 1; + } + } + return true; +} + static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api) { /* because bgp pbr entry may contain unsupported @@ -179,18 +232,63 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api) * - combination src/dst => redirect nexthop [ + rate] * - combination src/dst => redirect VRF [ + rate] * - combination src/dst => drop + * - combination srcport + @IP */ - if (api->match_src_port_num || api->match_dst_port_num - || api->match_port_num || api->match_protocol_num - || api->match_icmp_type_num || api->match_icmp_type_num + 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 (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:" + "multiple protocols ( %d). ignoring.", + api->match_protocol_num); + return 0; + } + if (api->match_protocol_num == 1 && + api->protocol[0].value != PROTOCOL_UDP && + api->protocol[0].value != PROTOCOL_TCP) { + if (BGP_DEBUG(pbr, PBR)) + zlog_debug("BGP: match protocol operations:" + "protocol (%d) not supported. ignoring", + api->match_protocol_num); + return 0; + } + if (!bgp_pbr_extract(api->src_port, api->match_src_port_num, NULL)) { + if (BGP_DEBUG(pbr, PBR)) + zlog_debug("BGP: match src port operations:" + "too complex. ignoring."); + return 0; + } + if (!bgp_pbr_extract(api->dst_port, api->match_dst_port_num, NULL)) { + if (BGP_DEBUG(pbr, PBR)) + zlog_debug("BGP: match dst port operations:" + "too complex. ignoring."); + return 0; + } + if (!bgp_pbr_extract(api->port, api->match_port_num, NULL)) { + if (BGP_DEBUG(pbr, PBR)) + zlog_debug("BGP: match port operations:" + "too complex. ignoring."); + return 0; + } + /* no combinations with both src_port and dst_port + * or port with src_port and dst_port + */ + if (api->match_src_port_num + api->match_dst_port_num + + api->match_port_num > 3) { + if (BGP_DEBUG(pbr, PBR)) + zlog_debug("BGP: match multiple port operations:" + " too complex. ignoring."); + return 0; + } if (!(api->match_bitmask & PREFIX_SRC_PRESENT) && !(api->match_bitmask & PREFIX_DST_PRESENT)) { if (BGP_DEBUG(pbr, PBR)) { @@ -447,6 +545,11 @@ uint32_t bgp_pbr_match_entry_hash_key(void *arg) pbme = (struct bgp_pbr_match_entry *)arg; key = prefix_hash_key(&pbme->src); key = jhash_1word(prefix_hash_key(&pbme->dst), key); + key = jhash(&pbme->dst_port_min, 2, key); + key = jhash(&pbme->src_port_min, 2, key); + key = jhash(&pbme->dst_port_max, 2, key); + key = jhash(&pbme->src_port_max, 2, key); + key = jhash(&pbme->proto, 1, key); return key; } @@ -474,6 +577,21 @@ int bgp_pbr_match_entry_hash_equal(const void *arg1, const void *arg2) if (!prefix_same(&r1->dst, &r2->dst)) return 0; + if (r1->src_port_min != r2->src_port_min) + return 0; + + if (r1->dst_port_min != r2->dst_port_min) + return 0; + + if (r1->src_port_max != r2->src_port_max) + return 0; + + if (r1->dst_port_max != r2->dst_port_max) + return 0; + + if (r1->proto != r2->proto) + return 0; + return 1; } @@ -813,10 +931,13 @@ 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) + struct bgp_info *binfo, + vrf_id_t vrf_id, + struct prefix *src, + struct prefix *dst, + uint8_t protocol, + struct bgp_pbr_range_port *src_port, + struct bgp_pbr_range_port *dst_port) { struct bgp_pbr_match temp; struct bgp_pbr_match_entry temp2; @@ -840,11 +961,35 @@ static void bgp_pbr_policyroute_remove_from_zebra(struct bgp *bgp, prefix_copy(&temp2.dst, dst); } else temp2.dst.family = AF_INET; - - if (src == NULL || dst == NULL) - temp.type = IPSET_NET; - else - temp.type = IPSET_NET_NET; + if (src_port) { + temp.flags |= MATCH_PORT_SRC_SET; + temp2.src_port_min = src_port->min_port; + if (src_port->max_port) { + temp.flags |= MATCH_PORT_SRC_RANGE_SET; + temp2.src_port_max = src_port->max_port; + } + } + if (dst_port) { + temp.flags |= MATCH_PORT_DST_SET; + temp2.dst_port_min = dst_port->min_port; + if (dst_port->max_port) { + temp.flags |= MATCH_PORT_DST_RANGE_SET; + temp2.dst_port_max = dst_port->max_port; + } + } + temp2.proto = protocol; + + if (src == NULL || 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 (vrf_id == VRF_UNKNOWN) /* XXX case BGP destroy */ temp.vrf_id = 0; else @@ -870,12 +1015,15 @@ 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 nexthop *nh, - float *rate) + struct bgp_info *binfo, + vrf_id_t vrf_id, + struct prefix *src, + struct prefix *dst, + struct nexthop *nh, + float *rate, + uint8_t protocol, + struct bgp_pbr_range_port *src_port, + struct bgp_pbr_range_port *dst_port) { struct bgp_pbr_match temp; struct bgp_pbr_match_entry temp2; @@ -913,15 +1061,33 @@ 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) - temp.type = IPSET_NET; - else - temp.type = IPSET_NET_NET; + if (src == NULL || dst == NULL) { + if ((src_port && src_port->min_port) || + (dst_port && dst_port->min_port)) + temp.type = IPSET_NET_PORT; + else + temp.type = IPSET_NET; + } else { + if ((src_port && src_port->min_port) || + (dst_port && dst_port->min_port)) + temp.type = IPSET_NET_PORT_NET; + else + temp.type = IPSET_NET_NET; + } temp.vrf_id = vrf_id; if (src) temp.flags |= MATCH_IP_SRC_SET; if (dst) temp.flags |= MATCH_IP_DST_SET; + + if (src_port && src_port->min_port) + temp.flags |= MATCH_PORT_SRC_SET; + if (dst_port && dst_port->min_port) + 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; temp.action = bpa; bpm = hash_get(bgp->pbr_match_hash, &temp, bgp_pbr_match_alloc_intern); @@ -953,9 +1119,14 @@ static void bgp_pbr_policyroute_add_to_zebra(struct bgp *bgp, prefix_copy(&temp2.dst, 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; if (bpm) bpme = hash_get(bpm->entry_hash, &temp2, - bgp_pbr_match_entry_alloc_intern); + bgp_pbr_match_entry_alloc_intern); if (bpme && bpme->unique == 0) { bpme->unique = ++bgp_pbr_match_entry_counter_unique; /* 0 value is forbidden */ @@ -1021,6 +1192,9 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, int continue_loop = 1; float rate = 0; 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; if (api->match_bitmask & PREFIX_SRC_PRESENT) src = &api->src_prefix; @@ -1028,10 +1202,33 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, dst = &api->dst_prefix; memset(&nh, 0, sizeof(struct nexthop)); nh.vrf_id = VRF_UNKNOWN; - + if (api->match_protocol_num) + proto = (uint8_t)api->protocol[0].value; + /* if match_port is selected, then either src or dst port will be parsed + * but not both at the same time + */ + if (api->match_port_num >= 1) { + bgp_pbr_extract(api->port, + api->match_port_num, + &range); + srcp = dstp = ⦥ + } else if (api->match_src_port_num >= 1) { + bgp_pbr_extract(api->src_port, + api->match_src_port_num, + &range); + srcp = ⦥ + dstp = NULL; + } else if (api->match_dst_port_num >= 1) { + bgp_pbr_extract(api->dst_port, + api->match_dst_port_num, + &range); + dstp = ⦥ + srcp = NULL; + } if (!add) return bgp_pbr_policyroute_remove_from_zebra(bgp, binfo, - api->vrf_id, src, dst); + 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) { @@ -1042,7 +1239,8 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, nh.type = NEXTHOP_TYPE_BLACKHOLE; bgp_pbr_policyroute_add_to_zebra(bgp, binfo, api->vrf_id, src, dst, - &nh, &rate); + &nh, &rate, proto, + srcp, dstp); } else { /* update rate. can be reentrant */ rate = api->actions[i].u.r.rate; @@ -1085,7 +1283,8 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, bgp_pbr_policyroute_add_to_zebra(bgp, binfo, api->vrf_id, src, dst, - &nh, &rate); + &nh, &rate, proto, + srcp, dstp); /* XXX combination with REDIRECT_VRF * + REDIRECT_NH_IP not done */ @@ -1097,7 +1296,8 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, bgp_pbr_policyroute_add_to_zebra(bgp, binfo, api->vrf_id, src, dst, - &nh, &rate); + &nh, &rate, proto, + srcp, dstp); continue_loop = 0; break; case ACTION_MARKING: diff --git a/bgpd/bgp_pbr.h b/bgpd/bgp_pbr.h index fd47ad6fb..212249295 100644 --- a/bgpd/bgp_pbr.h +++ b/bgpd/bgp_pbr.h @@ -123,6 +123,8 @@ struct bgp_pbr_entry_main { struct prefix src_prefix; struct prefix dst_prefix; +#define PROTOCOL_UDP 17 +#define PROTOCOL_TCP 6 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]; @@ -157,6 +159,10 @@ struct bgp_pbr_match { #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; vrf_id_t vrf_id; |