mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-08-15 13:27:53 +00:00
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:
parent
f730e5667d
commit
1de7dffff7
226
bgpd/bgp_pbr.c
226
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;
|
||||
}
|
||||
|
||||
@ -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 = ⦥
|
||||
} 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:
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user