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++; \
} 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;
}
@ -816,7 +934,10 @@ 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 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_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;
} 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
@ -875,7 +1020,10 @@ static void bgp_pbr_policyroute_add_to_zebra(struct bgp *bgp,
struct prefix *src,
struct prefix *dst,
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_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)
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,6 +1119,11 @@ 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);
@ -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 = &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)
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:

View File

@ -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;