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>
This commit is contained in:
Philippe Guibert 2018-03-30 12:25:03 +02:00
parent f730e5667d
commit 1de7dffff7
2 changed files with 234 additions and 28 deletions

View File

@ -169,7 +169,60 @@ static int sprintf_bgp_pbr_match_val(char *str, struct bgp_pbr_match_val *mval,
_cnt++; \ _cnt++; \
} while (0) } 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) static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api)
{ {
/* because bgp pbr entry may contain unsupported /* 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 nexthop [ + rate]
* - combination src/dst => redirect VRF [ + rate] * - combination src/dst => redirect VRF [ + rate]
* - combination src/dst => drop * - combination src/dst => drop
* - combination srcport + @IP
*/ */
if (api->match_src_port_num || api->match_dst_port_num if (api->match_icmp_type_num || api->match_icmp_type_num
|| api->match_port_num || api->match_protocol_num
|| api->match_icmp_type_num || api->match_icmp_type_num
|| api->match_packet_length_num || api->match_dscp_num || api->match_packet_length_num || api->match_dscp_num
|| api->match_tcpflags_num) { || api->match_tcpflags_num) {
if (BGP_DEBUG(pbr, PBR)) { if (BGP_DEBUG(pbr, PBR)) {
bgp_pbr_print_policy_route(api); bgp_pbr_print_policy_route(api);
zlog_debug("BGP: some SET actions not supported by Zebra. ignoring."); 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; 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) && if (!(api->match_bitmask & PREFIX_SRC_PRESENT) &&
!(api->match_bitmask & PREFIX_DST_PRESENT)) { !(api->match_bitmask & PREFIX_DST_PRESENT)) {
if (BGP_DEBUG(pbr, PBR)) { 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; pbme = (struct bgp_pbr_match_entry *)arg;
key = prefix_hash_key(&pbme->src); key = prefix_hash_key(&pbme->src);
key = jhash_1word(prefix_hash_key(&pbme->dst), key); 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; 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)) if (!prefix_same(&r1->dst, &r2->dst))
return 0; 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; return 1;
} }
@ -816,7 +934,10 @@ static void bgp_pbr_policyroute_remove_from_zebra(struct bgp *bgp,
struct bgp_info *binfo, struct bgp_info *binfo,
vrf_id_t vrf_id, vrf_id_t vrf_id,
struct prefix *src, struct prefix *src,
struct prefix *dst) 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 temp;
struct bgp_pbr_match_entry temp2; 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); prefix_copy(&temp2.dst, dst);
} else } else
temp2.dst.family = AF_INET; temp2.dst.family = AF_INET;
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 (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; temp.type = IPSET_NET;
} else {
if (temp.flags & (MATCH_PORT_DST_SET | MATCH_PORT_SRC_SET))
temp.type = IPSET_NET_PORT_NET;
else else
temp.type = IPSET_NET_NET; temp.type = IPSET_NET_NET;
}
if (vrf_id == VRF_UNKNOWN) /* XXX case BGP destroy */ if (vrf_id == VRF_UNKNOWN) /* XXX case BGP destroy */
temp.vrf_id = 0; temp.vrf_id = 0;
else else
@ -875,7 +1020,10 @@ static void bgp_pbr_policyroute_add_to_zebra(struct bgp *bgp,
struct prefix *src, struct prefix *src,
struct prefix *dst, struct prefix *dst,
struct nexthop *nh, struct nexthop *nh,
float *rate) 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 temp;
struct bgp_pbr_match_entry temp2; 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 */ /* then look for bpm */
memset(&temp, 0, sizeof(temp)); memset(&temp, 0, sizeof(temp));
if (src == NULL || dst == NULL) 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; 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 else
temp.type = IPSET_NET_NET; temp.type = IPSET_NET_NET;
}
temp.vrf_id = vrf_id; temp.vrf_id = vrf_id;
if (src) if (src)
temp.flags |= MATCH_IP_SRC_SET; temp.flags |= MATCH_IP_SRC_SET;
if (dst) if (dst)
temp.flags |= MATCH_IP_DST_SET; 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; temp.action = bpa;
bpm = hash_get(bgp->pbr_match_hash, &temp, bpm = hash_get(bgp->pbr_match_hash, &temp,
bgp_pbr_match_alloc_intern); bgp_pbr_match_alloc_intern);
@ -953,6 +1119,11 @@ static void bgp_pbr_policyroute_add_to_zebra(struct bgp *bgp,
prefix_copy(&temp2.dst, dst); prefix_copy(&temp2.dst, dst);
else else
temp2.dst.family = AF_INET; 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) if (bpm)
bpme = hash_get(bpm->entry_hash, &temp2, bpme = hash_get(bpm->entry_hash, &temp2,
bgp_pbr_match_entry_alloc_intern); bgp_pbr_match_entry_alloc_intern);
@ -1021,6 +1192,9 @@ static void bgp_pbr_handle_entry(struct bgp *bgp,
int continue_loop = 1; int continue_loop = 1;
float rate = 0; float rate = 0;
struct prefix *src = NULL, *dst = NULL; 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) if (api->match_bitmask & PREFIX_SRC_PRESENT)
src = &api->src_prefix; src = &api->src_prefix;
@ -1028,10 +1202,33 @@ static void bgp_pbr_handle_entry(struct bgp *bgp,
dst = &api->dst_prefix; dst = &api->dst_prefix;
memset(&nh, 0, sizeof(struct nexthop)); memset(&nh, 0, sizeof(struct nexthop));
nh.vrf_id = VRF_UNKNOWN; 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 = &range;
} else if (api->match_src_port_num >= 1) {
bgp_pbr_extract(api->src_port,
api->match_src_port_num,
&range);
srcp = &range;
dstp = NULL;
} else if (api->match_dst_port_num >= 1) {
bgp_pbr_extract(api->dst_port,
api->match_dst_port_num,
&range);
dstp = &range;
srcp = NULL;
}
if (!add) if (!add)
return bgp_pbr_policyroute_remove_from_zebra(bgp, binfo, 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 */ /* no action for add = true */
for (i = 0; i < api->action_num; i++) { for (i = 0; i < api->action_num; i++) {
switch (api->actions[i].action) { switch (api->actions[i].action) {
@ -1042,7 +1239,8 @@ static void bgp_pbr_handle_entry(struct bgp *bgp,
nh.type = NEXTHOP_TYPE_BLACKHOLE; nh.type = NEXTHOP_TYPE_BLACKHOLE;
bgp_pbr_policyroute_add_to_zebra(bgp, binfo, bgp_pbr_policyroute_add_to_zebra(bgp, binfo,
api->vrf_id, src, dst, api->vrf_id, src, dst,
&nh, &rate); &nh, &rate, proto,
srcp, dstp);
} else { } else {
/* update rate. can be reentrant */ /* update rate. can be reentrant */
rate = api->actions[i].u.r.rate; 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, bgp_pbr_policyroute_add_to_zebra(bgp, binfo,
api->vrf_id, api->vrf_id,
src, dst, src, dst,
&nh, &rate); &nh, &rate, proto,
srcp, dstp);
/* XXX combination with REDIRECT_VRF /* XXX combination with REDIRECT_VRF
* + REDIRECT_NH_IP not done * + 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, bgp_pbr_policyroute_add_to_zebra(bgp, binfo,
api->vrf_id, api->vrf_id,
src, dst, src, dst,
&nh, &rate); &nh, &rate, proto,
srcp, dstp);
continue_loop = 0; continue_loop = 0;
break; break;
case ACTION_MARKING: case ACTION_MARKING:

View File

@ -123,6 +123,8 @@ struct bgp_pbr_entry_main {
struct prefix src_prefix; struct prefix src_prefix;
struct prefix dst_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 protocol[BGP_PBR_MATCH_VAL_MAX];
struct bgp_pbr_match_val src_port[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]; 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_SRC_SET (1 << 0)
#define MATCH_IP_DST_SET (1 << 1) #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; uint32_t flags;
vrf_id_t vrf_id; vrf_id_t vrf_id;