Merge pull request #2073 from pguibert6WIND/bgp_fs_pbr

Bgp Flowspec Policy Based Routing
This commit is contained in:
Russ White 2018-05-02 18:54:11 -04:00 committed by GitHub
commit d437ae815d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 2593 additions and 305 deletions

View File

@ -87,7 +87,7 @@ libbgp_a_SOURCES = \
bgp_encap_tlv.c $(BGP_VNC_RFAPI_SRC) bgp_attr_evpn.c \
bgp_evpn.c bgp_evpn_vty.c bgp_vpn.c bgp_label.c bgp_rd.c \
bgp_keepalives.c bgp_io.c bgp_flowspec.c bgp_flowspec_util.c \
bgp_flowspec_vty.c bgp_labelpool.c
bgp_flowspec_vty.c bgp_labelpool.c bgp_pbr.c
noinst_HEADERS = \
bgp_memory.h \
@ -101,7 +101,7 @@ noinst_HEADERS = \
$(BGP_VNC_RFAPI_HD) bgp_attr_evpn.h bgp_evpn.h bgp_evpn_vty.h \
bgp_vpn.h bgp_label.h bgp_rd.h bgp_evpn_private.h bgp_keepalives.h \
bgp_io.h bgp_flowspec.h bgp_flowspec_private.h bgp_flowspec_util.h \
bgp_labelpool.h
bgp_labelpool.h bgp_pbr.h
bgpd_SOURCES = bgp_main.c
bgpd_LDADD = libbgp.a $(BGP_VNC_RFP_LIB) ../lib/libfrr.la @LIBCAP@ @LIBM@

View File

@ -59,6 +59,7 @@ unsigned long conf_bgp_debug_update_groups;
unsigned long conf_bgp_debug_vpn;
unsigned long conf_bgp_debug_flowspec;
unsigned long conf_bgp_debug_labelpool;
unsigned long conf_bgp_debug_pbr;
unsigned long term_bgp_debug_as4;
unsigned long term_bgp_debug_neighbor_events;
@ -75,6 +76,7 @@ unsigned long term_bgp_debug_update_groups;
unsigned long term_bgp_debug_vpn;
unsigned long term_bgp_debug_flowspec;
unsigned long term_bgp_debug_labelpool;
unsigned long term_bgp_debug_pbr;
struct list *bgp_debug_neighbor_events_peers = NULL;
struct list *bgp_debug_keepalive_peers = NULL;
@ -1653,7 +1655,40 @@ DEFUN (no_debug_bgp_vpn,
if (vty->node != CONFIG_NODE)
vty_out(vty, "disabled debug bgp vpn %s\n", argv[idx]->text);
return CMD_SUCCESS;
}
/* debug bgp pbr */
DEFUN (debug_bgp_pbr,
debug_bgp_pbr_cmd,
"debug bgp pbr",
DEBUG_STR
BGP_STR
"BGP policy based routing\n")
{
if (vty->node == CONFIG_NODE)
DEBUG_ON(pbr, PBR);
else {
TERM_DEBUG_ON(pbr, PBR);
vty_out(vty, "BGP policy based routing is on\n");
}
return CMD_SUCCESS;
}
DEFUN (no_debug_bgp_pbr,
no_debug_bgp_pbr_cmd,
"no debug bgp pbr",
NO_STR
DEBUG_STR
BGP_STR
"BGP policy based routing\n")
{
if (vty->node == CONFIG_NODE)
DEBUG_OFF(pbr, PBR);
else {
TERM_DEBUG_OFF(pbr, PBR);
vty_out(vty, "BGP policy based routing is off\n");
}
return CMD_SUCCESS;
}
@ -1733,6 +1768,7 @@ DEFUN (no_debug_bgp,
TERM_DEBUG_OFF(vpn, VPN_LEAK_LABEL);
TERM_DEBUG_OFF(flowspec, FLOWSPEC);
TERM_DEBUG_OFF(labelpool, LABELPOOL);
TERM_DEBUG_OFF(pbr, PBR);
vty_out(vty, "All possible debugging has been turned off\n");
return CMD_SUCCESS;
@ -1808,6 +1844,9 @@ DEFUN_NOSH (show_debugging_bgp,
if (BGP_DEBUG(labelpool, LABELPOOL))
vty_out(vty, " BGP labelpool debugging is on\n");
if (BGP_DEBUG(pbr, PBR))
vty_out(vty, " BGP policy based routing debugging is on\n");
vty_out(vty, "\n");
return CMD_SUCCESS;
}
@ -1865,6 +1904,9 @@ int bgp_debug_count(void)
if (BGP_DEBUG(labelpool, LABELPOOL))
ret++;
if (BGP_DEBUG(pbr, PBR))
ret++;
return ret;
}
@ -1966,6 +2008,10 @@ static int bgp_config_write_debug(struct vty *vty)
write++;
}
if (CONF_BGP_DEBUG(pbr, PBR)) {
vty_out(vty, "debug bgp pbr\n");
write++;
}
return write;
}
@ -2069,6 +2115,13 @@ void bgp_debug_init(void)
install_element(CONFIG_NODE, &debug_bgp_labelpool_cmd);
install_element(ENABLE_NODE, &no_debug_bgp_labelpool_cmd);
install_element(CONFIG_NODE, &no_debug_bgp_labelpool_cmd);
/* debug bgp pbr */
install_element(ENABLE_NODE, &debug_bgp_pbr_cmd);
install_element(CONFIG_NODE, &debug_bgp_pbr_cmd);
install_element(ENABLE_NODE, &no_debug_bgp_pbr_cmd);
install_element(CONFIG_NODE, &no_debug_bgp_pbr_cmd);
}
/* Return true if this prefix is on the per_prefix_list of prefixes to debug

View File

@ -75,6 +75,7 @@ extern unsigned long conf_bgp_debug_update_groups;
extern unsigned long conf_bgp_debug_vpn;
extern unsigned long conf_bgp_debug_flowspec;
extern unsigned long conf_bgp_debug_labelpool;
extern unsigned long conf_bgp_debug_pbr;
extern unsigned long term_bgp_debug_as4;
extern unsigned long term_bgp_debug_neighbor_events;
@ -89,6 +90,7 @@ extern unsigned long term_bgp_debug_update_groups;
extern unsigned long term_bgp_debug_vpn;
extern unsigned long term_bgp_debug_flowspec;
extern unsigned long term_bgp_debug_labelpool;
extern unsigned long term_bgp_debug_pbr;
extern struct list *bgp_debug_neighbor_events_peers;
extern struct list *bgp_debug_keepalive_peers;
@ -123,6 +125,8 @@ struct bgp_debug_filter {
#define BGP_DEBUG_VPN_LEAK_LABEL 0x08
#define BGP_DEBUG_FLOWSPEC 0x01
#define BGP_DEBUG_LABELPOOL 0x01
#define BGP_DEBUG_PBR 0x01
#define BGP_DEBUG_PBR_ERROR 0x02
#define BGP_DEBUG_PACKET_SEND 0x01
#define BGP_DEBUG_PACKET_SEND_DETAIL 0x02

View File

@ -34,6 +34,7 @@
#include "bgpd/bgp_lcommunity.h"
#include "bgpd/bgp_aspath.h"
#include "bgpd/bgp_flowspec_private.h"
#include "bgpd/bgp_pbr.h"
/* struct used to dump the rate contained in FS set traffic-rate EC */
union traffic_rate {
@ -931,3 +932,52 @@ int ecommunity_del_val(struct ecommunity *ecom, struct ecommunity_val *eval)
ecom->val = p;
return 1;
}
int ecommunity_fill_pbr_action(struct ecommunity_val *ecom_eval,
struct bgp_pbr_entry_action *api)
{
if (ecom_eval->val[1] == ECOMMUNITY_TRAFFIC_RATE) {
api->action = ACTION_TRAFFICRATE;
api->u.r.rate_info[3] = ecom_eval->val[4];
api->u.r.rate_info[2] = ecom_eval->val[5];
api->u.r.rate_info[1] = ecom_eval->val[6];
api->u.r.rate_info[0] = ecom_eval->val[7];
} else if (ecom_eval->val[1] == ECOMMUNITY_TRAFFIC_ACTION) {
api->action = ACTION_TRAFFIC_ACTION;
/* else distribute code is set by default */
if (ecom_eval->val[5] & (1 << FLOWSPEC_TRAFFIC_ACTION_TERMINAL))
api->u.za.filter |= TRAFFIC_ACTION_TERMINATE;
else
api->u.za.filter |= TRAFFIC_ACTION_DISTRIBUTE;
if (ecom_eval->val[5] == 1 << FLOWSPEC_TRAFFIC_ACTION_SAMPLE)
api->u.za.filter |= TRAFFIC_ACTION_SAMPLE;
} else if (ecom_eval->val[1] == ECOMMUNITY_TRAFFIC_MARKING) {
api->action = ACTION_MARKING;
api->u.marking_dscp = ecom_eval->val[7];
} else if (ecom_eval->val[1] == ECOMMUNITY_REDIRECT_VRF) {
/* must use external function */
return 0;
} else if (ecom_eval->val[1] == ECOMMUNITY_REDIRECT_IP_NH) {
/* see draft-ietf-idr-flowspec-redirect-ip-02
* Q1: how come a ext. community can host ipv6 address
* Q2 : from cisco documentation:
* Announces the reachability of one or more flowspec NLRI.
* When a BGP speaker receives an UPDATE message with the
* redirect-to-IP extended community, it is expected to
* create a traffic filtering rule for every flow-spec
* NLRI in the message that has this path as its best
* path. The filter entry matches the IP packets
* described in the NLRI field and redirects them or
* copies them towards the IPv4 or IPv6 address specified
* in the 'Network Address of Next- Hop'
* field of the associated MP_REACH_NLRI.
*/
struct ecommunity_ip *ip_ecom = (struct ecommunity_ip *)
ecom_eval + 2;
api->u.zr.redirect_ip_v4 = ip_ecom->ip;
} else
return -1;
return 0;
}

View File

@ -172,4 +172,8 @@ extern int ecommunity_strip(struct ecommunity *ecom, uint8_t type,
extern struct ecommunity *ecommunity_new(void);
extern int ecommunity_del_val(struct ecommunity *ecom,
struct ecommunity_val *eval);
struct bgp_pbr_entry_action;
extern int ecommunity_fill_pbr_action(struct ecommunity_val *ecom_eval,
struct bgp_pbr_entry_action *api);
#endif /* _QUAGGA_BGP_ECOMMUNITY_H */

View File

@ -25,6 +25,7 @@
#include "bgp_table.h"
#include "bgp_flowspec_util.h"
#include "bgp_flowspec_private.h"
#include "bgp_pbr.h"
static void hex2bin(uint8_t *hex, int *bin)
{
@ -50,311 +51,28 @@ static int hexstr2num(uint8_t *hexstr, int len)
return num;
}
/*
* handle the flowspec address src/dst or generic address NLRI
* return number of bytes analysed ( >= 0).
/* call bgp_flowspec_op_decode
* returns offset
*/
int bgp_flowspec_ip_address(enum bgp_flowspec_util_nlri_t type,
uint8_t *nlri_ptr,
uint32_t max_len,
void *result, int *error)
static int bgp_flowspec_call_non_opaque_decode(uint8_t *nlri_content, int len,
struct bgp_pbr_match_val *mval,
uint8_t *match_num, int *error)
{
char *display = (char *)result; /* for return_string */
struct prefix *prefix = (struct prefix *)result;
uint32_t offset = 0;
struct prefix prefix_local;
int psize;
int ret;
*error = 0;
memset(&prefix_local, 0, sizeof(struct prefix));
/* read the prefix length */
prefix_local.prefixlen = nlri_ptr[offset];
psize = PSIZE(prefix_local.prefixlen);
offset++;
/* TODO Flowspec IPv6 Support */
prefix_local.family = AF_INET;
/* Prefix length check. */
if (prefix_local.prefixlen > prefix_blen(&prefix_local) * 8)
*error = -1;
/* When packet overflow occur return immediately. */
if (psize + offset > max_len)
*error = -1;
/* Defensive coding, double-check
* the psize fits in a struct prefix
*/
if (psize > (ssize_t)sizeof(prefix_local.u))
*error = -1;
memcpy(&prefix_local.u.prefix, &nlri_ptr[offset], psize);
offset += psize;
switch (type) {
case BGP_FLOWSPEC_RETURN_STRING:
prefix2str(&prefix_local, display,
BGP_FLOWSPEC_STRING_DISPLAY_MAX);
break;
case BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE:
PREFIX_COPY_IPV4(prefix, &prefix_local)
break;
case BGP_FLOWSPEC_VALIDATE_ONLY:
default:
break;
}
return offset;
ret = bgp_flowspec_op_decode(
BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE,
nlri_content,
len,
mval, error);
if (*error < 0)
zlog_err("%s: flowspec_op_decode error %d",
__func__, *error);
else
*match_num = *error;
return ret;
}
/*
* handle the flowspec operator NLRI
* return number of bytes analysed
* if there is an error, the passed error param is used to give error:
* -1 if decoding error,
* if result is a string, its assumed length
* is BGP_FLOWSPEC_STRING_DISPLAY_MAX
*/
int bgp_flowspec_op_decode(enum bgp_flowspec_util_nlri_t type,
uint8_t *nlri_ptr,
uint32_t max_len,
void *result, int *error)
{
int op[8];
int len, value, value_size;
int loop = 0;
char *ptr = (char *)result; /* for return_string */
uint32_t offset = 0;
int len_string = BGP_FLOWSPEC_STRING_DISPLAY_MAX;
int len_written;
*error = 0;
do {
hex2bin(&nlri_ptr[offset], op);
offset++;
len = 2*op[2]+op[3];
value_size = 1 << len;
value = hexstr2num(&nlri_ptr[offset], value_size);
/* can not be < and > at the same time */
if (op[5] == 1 && op[6] == 1)
*error = -1;
/* if first element, AND bit can not be set */
if (op[1] == 1 && loop == 0)
*error = -1;
switch (type) {
case BGP_FLOWSPEC_RETURN_STRING:
if (loop) {
len_written = snprintf(ptr, len_string,
", ");
len_string -= len_written;
ptr += len_written;
}
if (op[5] == 1) {
len_written = snprintf(ptr, len_string,
"<");
len_string -= len_written;
ptr += len_written;
}
if (op[6] == 1) {
len_written = snprintf(ptr, len_string,
">");
len_string -= len_written;
ptr += len_written;
}
if (op[7] == 1) {
len_written = snprintf(ptr, len_string,
"=");
len_string -= len_written;
ptr += len_written;
}
len_written = snprintf(ptr, len_string,
" %d ", value);
len_string -= len_written;
ptr += len_written;
break;
case BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE:
/* TODO : FS OPAQUE */
break;
case BGP_FLOWSPEC_VALIDATE_ONLY:
default:
/* no action */
break;
}
offset += value_size;
loop++;
} while (op[0] == 0 && offset < max_len - 1);
if (offset > max_len)
*error = -1;
/* use error parameter to count the number of entries */
if (*error == 0)
*error = loop;
return offset;
}
/*
* handle the flowspec tcpflags field
* return number of bytes analysed
* if there is an error, the passed error param is used to give error:
* -1 if decoding error,
* if result is a string, its assumed length
* is BGP_FLOWSPEC_STRING_DISPLAY_MAX
*/
int bgp_flowspec_tcpflags_decode(enum bgp_flowspec_util_nlri_t type,
uint8_t *nlri_ptr,
uint32_t max_len,
void *result, int *error)
{
int op[8];
int len, value_size, loop = 0, value;
char *ptr = (char *)result; /* for return_string */
uint32_t offset = 0;
int len_string = BGP_FLOWSPEC_STRING_DISPLAY_MAX;
int len_written;
*error = 0;
do {
hex2bin(&nlri_ptr[offset], op);
/* if first element, AND bit can not be set */
if (op[1] == 1 && loop == 0)
*error = -1;
offset++;
len = 2 * op[2] + op[3];
value_size = 1 << len;
value = hexstr2num(&nlri_ptr[offset], value_size);
switch (type) {
case BGP_FLOWSPEC_RETURN_STRING:
if (op[1] == 1 && loop != 0) {
len_written = snprintf(ptr, len_string,
", and ");
len_string -= len_written;
ptr += len_written;
} else if (op[1] == 0 && loop != 0) {
len_written = snprintf(ptr, len_string,
", or ");
len_string -= len_written;
ptr += len_written;
}
len_written = snprintf(ptr, len_string,
"tcp flags is ");
len_string -= len_written;
ptr += len_written;
if (op[6] == 1) {
ptr += snprintf(ptr, len_string,
"not ");
len_string -= len_written;
ptr += len_written;
}
if (op[7] == 1) {
ptr += snprintf(ptr, len_string,
"exactly match ");
len_string -= len_written;
ptr += len_written;
}
ptr += snprintf(ptr, len_string,
"%d", value);
len_string -= len_written;
ptr += len_written;
break;
case BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE:
/* TODO : FS OPAQUE */
break;
case BGP_FLOWSPEC_VALIDATE_ONLY:
default:
/* no action */
break;
}
offset += value_size;
loop++;
} while (op[0] == 0 && offset < max_len - 1);
if (offset > max_len)
*error = -1;
/* use error parameter to count the number of entries */
if (*error == 0)
*error = loop;
return offset;
}
/*
* handle the flowspec fragment type field
* return error (returned values are invalid) or number of bytes analysed
* -1 if error in decoding
* >= 0 : number of bytes analysed (ok).
*/
int bgp_flowspec_fragment_type_decode(enum bgp_flowspec_util_nlri_t type,
uint8_t *nlri_ptr,
uint32_t max_len,
void *result, int *error)
{
int op[8];
int len, value, value_size, loop = 0;
char *ptr = (char *)result; /* for return_string */
uint32_t offset = 0;
int len_string = BGP_FLOWSPEC_STRING_DISPLAY_MAX;
int len_written;
*error = 0;
do {
hex2bin(&nlri_ptr[offset], op);
offset++;
len = 2 * op[2] + op[3];
value_size = 1 << len;
value = hexstr2num(&nlri_ptr[offset], value_size);
if (value != 1 && value != 2 && value != 4 && value != 8)
*error = -1;
offset += value_size;
/* TODO : as per RFC5574 : first Fragment bits are Reserved
* does that mean that it is not possible
* to handle multiple occurences ?
* as of today, we only grab the first TCP fragment
*/
if (loop) {
*error = -2;
loop++;
continue;
}
switch (type) {
case BGP_FLOWSPEC_RETURN_STRING:
switch (value) {
case 1:
len_written = snprintf(ptr, len_string,
"dont-fragment");
len_string -= len_written;
ptr += len_written;
break;
case 2:
len_written = snprintf(ptr, len_string,
"is-fragment");
len_string -= len_written;
ptr += len_written;
break;
case 4:
len_written = snprintf(ptr, len_string,
"first-fragment");
len_string -= len_written;
ptr += len_written;
break;
case 8:
len_written = snprintf(ptr, len_string,
"last-fragment");
len_string -= len_written;
ptr += len_written;
break;
default:
{}
}
break;
case BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE:
/* TODO : FS OPAQUE */
break;
case BGP_FLOWSPEC_VALIDATE_ONLY:
default:
/* no action */
break;
}
loop++;
} while (op[0] == 0 && offset < max_len - 1);
if (offset > max_len)
*error = -1;
return offset;
}
static bool bgp_flowspec_contains_prefix(struct prefix *pfs,
struct prefix *input,
int prefix_check)
@ -437,6 +155,508 @@ static bool bgp_flowspec_contains_prefix(struct prefix *pfs,
return false;
}
/*
* handle the flowspec address src/dst or generic address NLRI
* return number of bytes analysed ( >= 0).
*/
int bgp_flowspec_ip_address(enum bgp_flowspec_util_nlri_t type,
uint8_t *nlri_ptr,
uint32_t max_len,
void *result, int *error)
{
char *display = (char *)result; /* for return_string */
struct prefix *prefix = (struct prefix *)result;
uint32_t offset = 0;
struct prefix prefix_local;
int psize;
*error = 0;
memset(&prefix_local, 0, sizeof(struct prefix));
/* read the prefix length */
prefix_local.prefixlen = nlri_ptr[offset];
psize = PSIZE(prefix_local.prefixlen);
offset++;
/* TODO Flowspec IPv6 Support */
prefix_local.family = AF_INET;
/* Prefix length check. */
if (prefix_local.prefixlen > prefix_blen(&prefix_local) * 8)
*error = -1;
/* When packet overflow occur return immediately. */
if (psize + offset > max_len)
*error = -1;
/* Defensive coding, double-check
* the psize fits in a struct prefix
*/
if (psize > (ssize_t)sizeof(prefix_local.u))
*error = -1;
memcpy(&prefix_local.u.prefix, &nlri_ptr[offset], psize);
offset += psize;
switch (type) {
case BGP_FLOWSPEC_RETURN_STRING:
prefix2str(&prefix_local, display,
BGP_FLOWSPEC_STRING_DISPLAY_MAX);
break;
case BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE:
PREFIX_COPY_IPV4(prefix, &prefix_local)
break;
case BGP_FLOWSPEC_VALIDATE_ONLY:
default:
break;
}
return offset;
}
/*
* handle the flowspec operator NLRI
* return number of bytes analysed
* if there is an error, the passed error param is used to give error:
* -1 if decoding error,
* if result is a string, its assumed length
* is BGP_FLOWSPEC_STRING_DISPLAY_MAX
*/
int bgp_flowspec_op_decode(enum bgp_flowspec_util_nlri_t type,
uint8_t *nlri_ptr,
uint32_t max_len,
void *result, int *error)
{
int op[8];
int len, value, value_size;
int loop = 0;
char *ptr = (char *)result; /* for return_string */
uint32_t offset = 0;
int len_string = BGP_FLOWSPEC_STRING_DISPLAY_MAX;
int len_written;
struct bgp_pbr_match_val *mval = (struct bgp_pbr_match_val *)result;
*error = 0;
do {
if (loop > BGP_PBR_MATCH_VAL_MAX)
*error = -2;
hex2bin(&nlri_ptr[offset], op);
offset++;
len = 2*op[2]+op[3];
value_size = 1 << len;
value = hexstr2num(&nlri_ptr[offset], value_size);
/* can not be < and > at the same time */
if (op[5] == 1 && op[6] == 1)
*error = -1;
/* if first element, AND bit can not be set */
if (op[1] == 1 && loop == 0)
*error = -1;
switch (type) {
case BGP_FLOWSPEC_RETURN_STRING:
if (loop) {
len_written = snprintf(ptr, len_string,
", ");
len_string -= len_written;
ptr += len_written;
}
if (op[5] == 1) {
len_written = snprintf(ptr, len_string,
"<");
len_string -= len_written;
ptr += len_written;
}
if (op[6] == 1) {
len_written = snprintf(ptr, len_string,
">");
len_string -= len_written;
ptr += len_written;
}
if (op[7] == 1) {
len_written = snprintf(ptr, len_string,
"=");
len_string -= len_written;
ptr += len_written;
}
len_written = snprintf(ptr, len_string,
" %d ", value);
len_string -= len_written;
ptr += len_written;
break;
case BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE:
/* limitation: stop converting */
if (*error == -2)
break;
mval->value = value;
if (op[5] == 1)
mval->compare_operator |=
OPERATOR_COMPARE_LESS_THAN;
if (op[6] == 1)
mval->compare_operator |=
OPERATOR_COMPARE_GREATER_THAN;
if (op[7] == 1)
mval->compare_operator |=
OPERATOR_COMPARE_EQUAL_TO;
if (op[1] == 1)
mval->unary_operator = OPERATOR_UNARY_AND;
else
mval->unary_operator = OPERATOR_UNARY_OR;
mval++;
break;
case BGP_FLOWSPEC_VALIDATE_ONLY:
default:
/* no action */
break;
}
offset += value_size;
loop++;
} while (op[0] == 0 && offset < max_len - 1);
if (offset > max_len)
*error = -1;
/* use error parameter to count the number of entries */
if (*error == 0)
*error = loop;
return offset;
}
/*
* handle the flowspec tcpflags field
* return number of bytes analysed
* if there is an error, the passed error param is used to give error:
* -1 if decoding error,
* if result is a string, its assumed length
* is BGP_FLOWSPEC_STRING_DISPLAY_MAX
*/
int bgp_flowspec_tcpflags_decode(enum bgp_flowspec_util_nlri_t type,
uint8_t *nlri_ptr,
uint32_t max_len,
void *result, int *error)
{
int op[8];
int len, value_size, loop = 0, value;
char *ptr = (char *)result; /* for return_string */
struct bgp_pbr_match_val *mval = (struct bgp_pbr_match_val *)result;
uint32_t offset = 0;
int len_string = BGP_FLOWSPEC_STRING_DISPLAY_MAX;
int len_written;
*error = 0;
do {
if (loop > BGP_PBR_MATCH_VAL_MAX)
*error = -2;
hex2bin(&nlri_ptr[offset], op);
/* if first element, AND bit can not be set */
if (op[1] == 1 && loop == 0)
*error = -1;
offset++;
len = 2 * op[2] + op[3];
value_size = 1 << len;
value = hexstr2num(&nlri_ptr[offset], value_size);
switch (type) {
case BGP_FLOWSPEC_RETURN_STRING:
if (op[1] == 1 && loop != 0) {
len_written = snprintf(ptr, len_string,
", and ");
len_string -= len_written;
ptr += len_written;
} else if (op[1] == 0 && loop != 0) {
len_written = snprintf(ptr, len_string,
", or ");
len_string -= len_written;
ptr += len_written;
}
len_written = snprintf(ptr, len_string,
"tcp flags is ");
len_string -= len_written;
ptr += len_written;
if (op[6] == 1) {
ptr += snprintf(ptr, len_string,
"not ");
len_string -= len_written;
ptr += len_written;
}
if (op[7] == 1) {
ptr += snprintf(ptr, len_string,
"exactly match ");
len_string -= len_written;
ptr += len_written;
}
ptr += snprintf(ptr, len_string,
"%d", value);
len_string -= len_written;
ptr += len_written;
break;
case BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE:
/* limitation: stop converting */
if (*error == -2)
break;
mval->value = value;
if (op[6] == 1) {
/* different from */
mval->compare_operator |=
OPERATOR_COMPARE_LESS_THAN;
mval->compare_operator |=
OPERATOR_COMPARE_GREATER_THAN;
} else
mval->compare_operator |=
OPERATOR_COMPARE_EQUAL_TO;
if (op[7] == 1)
mval->compare_operator |=
OPERATOR_COMPARE_EXACT_MATCH;
if (op[1] == 1)
mval->unary_operator =
OPERATOR_UNARY_AND;
else
mval->unary_operator =
OPERATOR_UNARY_OR;
mval++;
break;
case BGP_FLOWSPEC_VALIDATE_ONLY:
default:
/* no action */
break;
}
offset += value_size;
loop++;
} while (op[0] == 0 && offset < max_len - 1);
if (offset > max_len)
*error = -1;
/* use error parameter to count the number of entries */
if (*error == 0)
*error = loop;
return offset;
}
/*
* handle the flowspec fragment type field
* return error (returned values are invalid) or number of bytes analysed
* -1 if error in decoding
* >= 0 : number of bytes analysed (ok).
*/
int bgp_flowspec_fragment_type_decode(enum bgp_flowspec_util_nlri_t type,
uint8_t *nlri_ptr,
uint32_t max_len,
void *result, int *error)
{
int op[8];
int len, value, value_size, loop = 0;
char *ptr = (char *)result; /* for return_string */
struct bgp_pbr_fragment_val *mval =
(struct bgp_pbr_fragment_val *)result;
uint32_t offset = 0;
int len_string = BGP_FLOWSPEC_STRING_DISPLAY_MAX;
int len_written;
*error = 0;
do {
hex2bin(&nlri_ptr[offset], op);
offset++;
len = 2 * op[2] + op[3];
value_size = 1 << len;
value = hexstr2num(&nlri_ptr[offset], value_size);
if (value != 1 && value != 2 && value != 4 && value != 8)
*error = -1;
offset += value_size;
/* TODO : as per RFC5574 : first Fragment bits are Reserved
* does that mean that it is not possible
* to handle multiple occurences ?
* as of today, we only grab the first TCP fragment
*/
if (loop) {
*error = -2;
loop++;
continue;
}
switch (type) {
case BGP_FLOWSPEC_RETURN_STRING:
switch (value) {
case 1:
len_written = snprintf(ptr, len_string,
"dont-fragment");
len_string -= len_written;
ptr += len_written;
break;
case 2:
len_written = snprintf(ptr, len_string,
"is-fragment");
len_string -= len_written;
ptr += len_written;
break;
case 4:
len_written = snprintf(ptr, len_string,
"first-fragment");
len_string -= len_written;
ptr += len_written;
break;
case 8:
len_written = snprintf(ptr, len_string,
"last-fragment");
len_string -= len_written;
ptr += len_written;
break;
default:
{}
}
break;
case BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE:
mval->bitmask = (uint8_t)value;
break;
case BGP_FLOWSPEC_VALIDATE_ONLY:
default:
/* no action */
break;
}
loop++;
} while (op[0] == 0 && offset < max_len - 1);
if (offset > max_len)
*error = -1;
return offset;
}
int bgp_flowspec_match_rules_fill(uint8_t *nlri_content, int len,
struct bgp_pbr_entry_main *bpem)
{
int offset = 0, error = 0;
struct prefix *prefix;
struct bgp_pbr_match_val *mval;
uint8_t *match_num;
uint8_t bitmask = 0;
int ret = 0, type;
while (offset < len - 1 && error >= 0) {
type = nlri_content[offset];
offset++;
switch (type) {
case FLOWSPEC_DEST_PREFIX:
case FLOWSPEC_SRC_PREFIX:
bitmask = 0;
if (type == FLOWSPEC_DEST_PREFIX) {
bitmask |= PREFIX_DST_PRESENT;
prefix = &bpem->dst_prefix;
} else {
bitmask |= PREFIX_SRC_PRESENT;
prefix = &bpem->src_prefix;
}
ret = bgp_flowspec_ip_address(
BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE,
nlri_content + offset,
len - offset,
prefix, &error);
if (error < 0)
zlog_err("%s: flowspec_ip_address error %d",
__func__, error);
else
bpem->match_bitmask |= bitmask;
offset += ret;
break;
case FLOWSPEC_IP_PROTOCOL:
match_num = &(bpem->match_protocol_num);
mval = (struct bgp_pbr_match_val *)
&(bpem->protocol);
offset += bgp_flowspec_call_non_opaque_decode(
nlri_content + offset,
len - offset,
mval, match_num,
&error);
break;
case FLOWSPEC_PORT:
match_num = &(bpem->match_port_num);
mval = (struct bgp_pbr_match_val *)
&(bpem->port);
offset += bgp_flowspec_call_non_opaque_decode(
nlri_content + offset,
len - offset,
mval, match_num,
&error);
break;
case FLOWSPEC_DEST_PORT:
match_num = &(bpem->match_dst_port_num);
mval = (struct bgp_pbr_match_val *)
&(bpem->dst_port);
offset += bgp_flowspec_call_non_opaque_decode(
nlri_content + offset,
len - offset,
mval, match_num,
&error);
break;
case FLOWSPEC_SRC_PORT:
match_num = &(bpem->match_src_port_num);
mval = (struct bgp_pbr_match_val *)
&(bpem->src_port);
offset += bgp_flowspec_call_non_opaque_decode(
nlri_content + offset,
len - offset,
mval, match_num,
&error);
break;
case FLOWSPEC_ICMP_TYPE:
match_num = &(bpem->match_icmp_type_num);
mval = (struct bgp_pbr_match_val *)
&(bpem->icmp_type);
offset += bgp_flowspec_call_non_opaque_decode(
nlri_content + offset,
len - offset,
mval, match_num,
&error);
break;
case FLOWSPEC_ICMP_CODE:
match_num = &(bpem->match_icmp_code_num);
mval = (struct bgp_pbr_match_val *)
&(bpem->icmp_code);
offset += bgp_flowspec_call_non_opaque_decode(
nlri_content + offset,
len - offset,
mval, match_num,
&error);
break;
case FLOWSPEC_PKT_LEN:
match_num =
&(bpem->match_packet_length_num);
mval = (struct bgp_pbr_match_val *)
&(bpem->packet_length);
offset += bgp_flowspec_call_non_opaque_decode(
nlri_content + offset,
len - offset,
mval, match_num,
&error);
break;
case FLOWSPEC_DSCP:
match_num = &(bpem->match_dscp_num);
mval = (struct bgp_pbr_match_val *)
&(bpem->dscp);
offset += bgp_flowspec_call_non_opaque_decode(
nlri_content + offset,
len - offset,
mval, match_num,
&error);
break;
case FLOWSPEC_TCP_FLAGS:
ret = bgp_flowspec_tcpflags_decode(
BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE,
nlri_content + offset,
len - offset,
&bpem->tcpflags, &error);
if (error < 0)
zlog_err("%s: flowspec_tcpflags_decode error %d",
__func__, error);
else
bpem->match_tcpflags_num = error;
/* contains the number of slots used */
offset += ret;
break;
case FLOWSPEC_FRAGMENT:
ret = bgp_flowspec_fragment_type_decode(
BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE,
nlri_content + offset,
len - offset, &bpem->fragment,
&error);
if (error < 0)
zlog_err("%s: flowspec_fragment_type_decode error %d",
__func__, error);
else
bpem->match_bitmask |= FRAGMENT_PRESENT;
offset += ret;
break;
default:
zlog_err("%s: unknown type %d\n", __func__, type);
}
}
return error;
}
struct bgp_node *bgp_flowspec_get_match_per_ip(afi_t afi,
struct bgp_table *rib,
struct prefix *match,

View File

@ -50,6 +50,9 @@ extern int bgp_flowspec_fragment_type_decode(enum bgp_flowspec_util_nlri_t type,
uint8_t *nlri_ptr,
uint32_t max_len,
void *result, int *error);
struct bgp_pbr_entry_main;
extern int bgp_flowspec_match_rules_fill(uint8_t *nlri_content, int len,
struct bgp_pbr_entry_main *bpem);
extern struct bgp_node *bgp_flowspec_get_match_per_ip(afi_t afi,
struct bgp_table *rib,

1140
bgpd/bgp_pbr.c Normal file

File diff suppressed because it is too large Load Diff

256
bgpd/bgp_pbr.h Normal file
View File

@ -0,0 +1,256 @@
/*
* BGP pbr
* Copyright (C) 6WIND
*
* FRR is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2, or (at your option) any
* later version.
*
* FRR is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; see the file COPYING; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __BGP_PBR_H__
#define __BGP_PBR_H__
#include "nexthop.h"
#include "zclient.h"
/* flowspec case: 0 to 3 actions maximum:
* 1 redirect
* 1 set dscp
* 1 set traffic rate
*/
#define ACTIONS_MAX_NUM 4
enum bgp_pbr_action_enum {
ACTION_TRAFFICRATE = 1,
ACTION_TRAFFIC_ACTION = 2,
ACTION_REDIRECT = 3,
ACTION_MARKING = 4,
ACTION_REDIRECT_IP = 5
};
#define TRAFFIC_ACTION_SAMPLE (1 << 0)
#define TRAFFIC_ACTION_TERMINATE (1 << 1)
#define TRAFFIC_ACTION_DISTRIBUTE (1 << 2)
#define OPERATOR_COMPARE_LESS_THAN (1<<1)
#define OPERATOR_COMPARE_GREATER_THAN (1<<2)
#define OPERATOR_COMPARE_EQUAL_TO (1<<3)
#define OPERATOR_COMPARE_EXACT_MATCH (1<<4)
#define OPERATOR_UNARY_OR (1<<1)
#define OPERATOR_UNARY_AND (1<<2)
/* struct used to store values [0;65535]
* this can be used for port number of protocol
*/
#define BGP_PBR_MATCH_VAL_MAX 5
struct bgp_pbr_match_val {
uint16_t value;
uint8_t compare_operator;
uint8_t unary_operator;
} bgp_pbr_value_t;
#define FRAGMENT_DONT 1
#define FRAGMENT_IS 2
#define FRAGMENT_FIRST 4
#define FRAGMENT_LAST 8
struct bgp_pbr_fragment_val {
uint8_t bitmask;
};
struct bgp_pbr_entry_action {
/* used to store enum bgp_pbr_action_enum enumerate */
uint8_t action;
union {
union {
uint8_t rate_info[4]; /* IEEE.754.1985 */
float rate;
} r __attribute__((aligned(8)));
struct _pbr_action {
uint8_t do_sample;
uint8_t filter;
} za;
vrf_id_t redirect_vrf;
struct _pbr_redirect_ip {
struct in_addr redirect_ip_v4;
uint8_t duplicate;
} zr;
uint8_t marking_dscp;
} u __attribute__((aligned(8)));
};
/* BGP Policy Route structure */
struct bgp_pbr_entry_main {
uint8_t type;
uint16_t instance;
uint32_t flags;
uint8_t message;
/*
* This is an enum but we are going to treat it as a uint8_t
* for purpose of encoding/decoding
*/
afi_t afi;
safi_t safi;
#define PREFIX_SRC_PRESENT (1 << 0)
#define PREFIX_DST_PRESENT (1 << 1)
#define FRAGMENT_PRESENT (1 << 2)
uint8_t match_bitmask;
uint8_t match_src_port_num;
uint8_t match_dst_port_num;
uint8_t match_port_num;
uint8_t match_protocol_num;
uint8_t match_icmp_type_num;
uint8_t match_icmp_code_num;
uint8_t match_packet_length_num;
uint8_t match_dscp_num;
uint8_t match_tcpflags_num;
struct prefix src_prefix;
struct prefix dst_prefix;
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];
struct bgp_pbr_match_val port[BGP_PBR_MATCH_VAL_MAX];
struct bgp_pbr_match_val icmp_type[BGP_PBR_MATCH_VAL_MAX];
struct bgp_pbr_match_val icmp_code[BGP_PBR_MATCH_VAL_MAX];
struct bgp_pbr_match_val packet_length[BGP_PBR_MATCH_VAL_MAX];
struct bgp_pbr_match_val dscp[BGP_PBR_MATCH_VAL_MAX];
struct bgp_pbr_match_val tcpflags[BGP_PBR_MATCH_VAL_MAX];
struct bgp_pbr_fragment_val fragment;
uint16_t action_num;
struct bgp_pbr_entry_action actions[ACTIONS_MAX_NUM];
uint8_t distance;
uint32_t metric;
route_tag_t tag;
uint32_t mtu;
vrf_id_t vrf_id;
};
struct bgp_pbr_match {
char ipset_name[ZEBRA_IPSET_NAME_SIZE];
/* mapped on enum ipset_type
*/
uint32_t type;
#define MATCH_IP_SRC_SET (1 << 0)
#define MATCH_IP_DST_SET (1 << 1)
uint32_t flags;
vrf_id_t vrf_id;
/* unique identifier for ipset create transaction
*/
uint32_t unique;
/* unique identifier for iptable add transaction
*/
uint32_t unique2;
bool installed;
bool install_in_progress;
bool installed_in_iptable;
bool install_iptable_in_progress;
struct hash *entry_hash;
struct bgp_pbr_action *action;
};
struct bgp_pbr_match_entry {
struct bgp_pbr_match *backpointer;
uint32_t unique;
struct prefix src;
struct prefix dst;
bool installed;
bool install_in_progress;
};
struct bgp_pbr_action {
/*
* The Unique identifier of this specific pbrms
*/
uint32_t unique;
uint32_t fwmark;
uint32_t table_id;
float rate;
/*
* nexthop information, or drop information
* contains src vrf_id and nh contains dest vrf_id
*/
vrf_id_t vrf_id;
struct nexthop nh;
bool installed;
bool install_in_progress;
uint32_t refcnt;
struct bgp *bgp;
};
extern struct bgp_pbr_action *bgp_pbr_action_rule_lookup(vrf_id_t vrf_id,
uint32_t unique);
extern struct bgp_pbr_match *bgp_pbr_match_ipset_lookup(vrf_id_t vrf_id,
uint32_t unique);
extern struct bgp_pbr_match_entry *bgp_pbr_match_ipset_entry_lookup(
vrf_id_t vrf_id, char *name,
uint32_t unique);
extern struct bgp_pbr_match *bgp_pbr_match_iptable_lookup(vrf_id_t vrf_id,
uint32_t unique);
extern void bgp_pbr_cleanup(struct bgp *bgp);
extern void bgp_pbr_init(struct bgp *bgp);
extern uint32_t bgp_pbr_action_hash_key(void *arg);
extern int bgp_pbr_action_hash_equal(const void *arg1,
const void *arg2);
extern uint32_t bgp_pbr_match_entry_hash_key(void *arg);
extern int bgp_pbr_match_entry_hash_equal(const void *arg1,
const void *arg2);
extern uint32_t bgp_pbr_match_hash_key(void *arg);
extern int bgp_pbr_match_hash_equal(const void *arg1,
const void *arg2);
void bgp_pbr_print_policy_route(struct bgp_pbr_entry_main *api);
struct bgp_node;
struct bgp_info;
extern void bgp_pbr_update_entry(struct bgp *bgp, struct prefix *p,
struct bgp_info *new_select,
afi_t afi, safi_t safi,
bool nlri_update);
#endif /* __BGP_PBR_H__ */

View File

@ -75,6 +75,7 @@
#include "bgpd/bgp_evpn_vty.h"
#include "bgpd/bgp_flowspec.h"
#include "bgpd/bgp_flowspec_util.h"
#include "bgpd/bgp_pbr.h"
#ifndef VTYSH_EXTRACT_PL
#include "bgpd/bgp_route_clippy.c"
@ -2231,7 +2232,6 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_node *rn,
/* If best route remains the same and this is not due to user-initiated
* clear, see exactly what needs to be done.
*/
if (old_select && old_select == new_select
&& !CHECK_FLAG(rn->flags, BGP_NODE_USER_CLEAR)
&& !CHECK_FLAG(old_select->flags, BGP_INFO_ATTR_CHANGED)

View File

@ -320,7 +320,8 @@ static inline void bgp_bump_version(struct bgp_node *node)
static inline int bgp_fibupd_safi(safi_t safi)
{
if (safi == SAFI_UNICAST || safi == SAFI_MULTICAST
|| safi == SAFI_LABELED_UNICAST)
|| safi == SAFI_LABELED_UNICAST
|| safi == SAFI_FLOWSPEC)
return 1;
return 0;
}

View File

@ -56,6 +56,7 @@
#include "bgpd/bgp_evpn.h"
#include "bgpd/bgp_mplsvpn.h"
#include "bgpd/bgp_labelpool.h"
#include "bgpd/bgp_pbr.h"
/* All information about zebra. */
struct zclient *zclient = NULL;
@ -997,6 +998,9 @@ static int bgp_table_map_apply(struct route_map *map, struct prefix *p,
static struct thread *bgp_tm_thread_connect;
static bool bgp_tm_status_connected;
static bool bgp_tm_chunk_obtained;
#define BGP_FLOWSPEC_TABLE_CHUNK 100000
static uint32_t bgp_tm_min, bgp_tm_max, bgp_tm_chunk_size;
static int bgp_zebra_tm_connect(struct thread *t)
{
@ -1017,12 +1021,27 @@ static int bgp_zebra_tm_connect(struct thread *t)
if (!bgp_tm_status_connected)
zlog_debug("Connecting to table manager. Success");
bgp_tm_status_connected = true;
if (!bgp_tm_chunk_obtained) {
if (bgp_zebra_get_table_range(bgp_tm_chunk_size,
&bgp_tm_min,
&bgp_tm_max) >= 0)
bgp_tm_chunk_obtained = true;
}
}
thread_add_timer(bm->master, bgp_zebra_tm_connect, zclient, delay,
&bgp_tm_thread_connect);
return 0;
}
uint32_t bgp_zebra_tm_get_id(void)
{
static int table_id;
if (!bgp_tm_chunk_obtained)
return ++table_id;
return bgp_tm_min++;
}
void bgp_zebra_init_tm_connect(void)
{
int delay = 1;
@ -1032,6 +1051,9 @@ void bgp_zebra_init_tm_connect(void)
if (bgp_tm_thread_connect != NULL)
return;
bgp_tm_status_connected = false;
bgp_tm_chunk_obtained = false;
bgp_tm_min = bgp_tm_max = 0;
bgp_tm_chunk_size = BGP_FLOWSPEC_TABLE_CHUNK;
thread_add_timer(bm->master, bgp_zebra_tm_connect, zclient, delay,
&bgp_tm_thread_connect);
}
@ -1173,6 +1195,10 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p,
if (bgp_debug_zebra(p))
prefix2str(&api.prefix, buf_prefix, sizeof(buf_prefix));
if (safi == SAFI_FLOWSPEC)
return bgp_pbr_update_entry(bgp, &rn->p,
info, afi, safi, true);
/*
* vrf leaking support (will have only one nexthop)
*/
@ -1459,6 +1485,7 @@ void bgp_zebra_withdraw(struct prefix *p, struct bgp_info *info,
struct bgp *bgp, safi_t safi)
{
struct zapi_route api;
struct peer *peer;
/* Don't try to install if we're not connected to Zebra or Zebra doesn't
* know of this instance.
@ -1466,6 +1493,12 @@ void bgp_zebra_withdraw(struct prefix *p, struct bgp_info *info,
if (!bgp_install_info_to_zebra(bgp))
return;
if (safi == SAFI_FLOWSPEC) {
peer = info->peer;
return bgp_pbr_update_entry(peer->bgp, p,
info, AFI_IP, safi, false);
}
memset(&api, 0, sizeof(api));
memcpy(&api.rmac, &(info->attr->rmac), sizeof(struct ethaddr));
api.vrf_id = bgp->vrf_id;
@ -1908,6 +1941,271 @@ int bgp_zebra_advertise_all_vni(struct bgp *bgp, int advertise)
return zclient_send_message(zclient);
}
static int rule_notify_owner(int command, struct zclient *zclient,
zebra_size_t length, vrf_id_t vrf_id)
{
uint32_t seqno, priority, unique;
enum zapi_rule_notify_owner note;
struct bgp_pbr_action *bgp_pbra;
ifindex_t ifi;
if (!zapi_rule_notify_decode(zclient->ibuf, &seqno, &priority, &unique,
&ifi, &note))
return -1;
bgp_pbra = bgp_pbr_action_rule_lookup(vrf_id, unique);
if (!bgp_pbra) {
if (BGP_DEBUG(zebra, ZEBRA))
zlog_debug("%s: Fail to look BGP rule (%u)",
__PRETTY_FUNCTION__, unique);
return 0;
}
switch (note) {
case ZAPI_RULE_FAIL_INSTALL:
if (BGP_DEBUG(zebra, ZEBRA))
zlog_debug("%s: Received RULE_FAIL_INSTALL",
__PRETTY_FUNCTION__);
bgp_pbra->installed = false;
bgp_pbra->install_in_progress = false;
break;
case ZAPI_RULE_INSTALLED:
bgp_pbra->installed = true;
bgp_pbra->install_in_progress = false;
if (BGP_DEBUG(zebra, ZEBRA))
zlog_debug("%s: Received RULE_INSTALLED",
__PRETTY_FUNCTION__);
break;
case ZAPI_RULE_REMOVED:
if (BGP_DEBUG(zebra, ZEBRA))
zlog_debug("%s: Received RULE REMOVED",
__PRETTY_FUNCTION__);
break;
}
return 0;
}
static int ipset_notify_owner(int command, struct zclient *zclient,
zebra_size_t length, vrf_id_t vrf_id)
{
uint32_t unique;
enum zapi_ipset_notify_owner note;
struct bgp_pbr_match *bgp_pbim;
if (!zapi_ipset_notify_decode(zclient->ibuf,
&unique,
&note))
return -1;
bgp_pbim = bgp_pbr_match_ipset_lookup(vrf_id, unique);
if (!bgp_pbim) {
if (BGP_DEBUG(zebra, ZEBRA))
zlog_debug("%s: Fail to look BGP match (%u)",
__PRETTY_FUNCTION__, unique);
return 0;
}
switch (note) {
case ZAPI_IPSET_FAIL_INSTALL:
if (BGP_DEBUG(zebra, ZEBRA))
zlog_debug("%s: Received IPSET_FAIL_INSTALL",
__PRETTY_FUNCTION__);
bgp_pbim->installed = false;
bgp_pbim->install_in_progress = false;
break;
case ZAPI_IPSET_INSTALLED:
bgp_pbim->installed = true;
bgp_pbim->install_in_progress = false;
if (BGP_DEBUG(zebra, ZEBRA))
zlog_debug("%s: Received IPSET_INSTALLED",
__PRETTY_FUNCTION__);
break;
case ZAPI_IPSET_REMOVED:
if (BGP_DEBUG(zebra, ZEBRA))
zlog_debug("%s: Received IPSET REMOVED",
__PRETTY_FUNCTION__);
break;
}
return 0;
}
static int ipset_entry_notify_owner(int command, struct zclient *zclient,
zebra_size_t length, vrf_id_t vrf_id)
{
uint32_t unique;
char ipset_name[ZEBRA_IPSET_NAME_SIZE];
enum zapi_ipset_entry_notify_owner note;
struct bgp_pbr_match_entry *bgp_pbime;
if (!zapi_ipset_entry_notify_decode(
zclient->ibuf,
&unique,
ipset_name,
&note))
return -1;
bgp_pbime = bgp_pbr_match_ipset_entry_lookup(vrf_id,
ipset_name,
unique);
if (!bgp_pbime) {
if (BGP_DEBUG(zebra, ZEBRA))
zlog_debug("%s: Fail to look BGP match entry (%u)",
__PRETTY_FUNCTION__, unique);
return 0;
}
switch (note) {
case ZAPI_IPSET_ENTRY_FAIL_INSTALL:
if (BGP_DEBUG(zebra, ZEBRA))
zlog_debug("%s: Received IPSET_ENTRY_FAIL_INSTALL",
__PRETTY_FUNCTION__);
bgp_pbime->installed = false;
bgp_pbime->install_in_progress = false;
break;
case ZAPI_IPSET_ENTRY_INSTALLED:
bgp_pbime->installed = true;
bgp_pbime->install_in_progress = false;
if (BGP_DEBUG(zebra, ZEBRA))
zlog_debug("%s: Received IPSET_ENTRY_INSTALLED",
__PRETTY_FUNCTION__);
break;
case ZAPI_IPSET_ENTRY_REMOVED:
if (BGP_DEBUG(zebra, ZEBRA))
zlog_debug("%s: Received IPSET_ENTRY_REMOVED",
__PRETTY_FUNCTION__);
break;
}
return 0;
}
static int iptable_notify_owner(int command, struct zclient *zclient,
zebra_size_t length, vrf_id_t vrf_id)
{
uint32_t unique;
enum zapi_iptable_notify_owner note;
struct bgp_pbr_match *bgpm;
if (!zapi_iptable_notify_decode(
zclient->ibuf,
&unique,
&note))
return -1;
bgpm = bgp_pbr_match_iptable_lookup(vrf_id, unique);
if (!bgpm) {
if (BGP_DEBUG(zebra, ZEBRA))
zlog_debug("%s: Fail to look BGP iptable (%u)",
__PRETTY_FUNCTION__, unique);
return 0;
}
switch (note) {
case ZAPI_IPTABLE_FAIL_INSTALL:
if (BGP_DEBUG(zebra, ZEBRA))
zlog_debug("%s: Received IPTABLE_FAIL_INSTALL",
__PRETTY_FUNCTION__);
bgpm->installed_in_iptable = false;
bgpm->install_iptable_in_progress = false;
break;
case ZAPI_IPTABLE_INSTALLED:
bgpm->installed_in_iptable = true;
bgpm->install_iptable_in_progress = false;
if (BGP_DEBUG(zebra, ZEBRA))
zlog_debug("%s: Received IPTABLE_INSTALLED",
__PRETTY_FUNCTION__);
bgpm->action->refcnt++;
break;
case ZAPI_IPTABLE_REMOVED:
if (BGP_DEBUG(zebra, ZEBRA))
zlog_debug("%s: Received IPTABLE REMOVED",
__PRETTY_FUNCTION__);
break;
}
return 0;
}
static void bgp_encode_pbr_rule_action(struct stream *s,
struct bgp_pbr_action *pbra)
{
struct prefix any;
stream_putl(s, 0); /* seqno unused */
stream_putl(s, 0); /* ruleno unused */
stream_putl(s, pbra->unique);
memset(&any, 0, sizeof(any));
any.family = AF_INET;
stream_putc(s, any.family);
stream_putc(s, any.prefixlen);
stream_put(s, &any.u.prefix, prefix_blen(&any));
stream_putw(s, 0); /* src port */
stream_putc(s, any.family);
stream_putc(s, any.prefixlen);
stream_put(s, &any.u.prefix, prefix_blen(&any));
stream_putw(s, 0); /* dst port */
stream_putl(s, pbra->fwmark); /* fwmark */
stream_putl(s, pbra->table_id);
stream_putl(s, 0); /* ifindex unused */
}
static void bgp_encode_pbr_ipset_match(struct stream *s,
struct bgp_pbr_match *pbim)
{
stream_putl(s, pbim->unique);
stream_putl(s, pbim->type);
stream_put(s, pbim->ipset_name,
ZEBRA_IPSET_NAME_SIZE);
}
static void bgp_encode_pbr_ipset_entry_match(struct stream *s,
struct bgp_pbr_match_entry *pbime)
{
stream_putl(s, pbime->unique);
/* check that back pointer is not null */
stream_put(s, pbime->backpointer->ipset_name,
ZEBRA_IPSET_NAME_SIZE);
stream_putc(s, pbime->src.family);
stream_putc(s, pbime->src.prefixlen);
stream_put(s, &pbime->src.u.prefix, prefix_blen(&pbime->src));
stream_putc(s, pbime->dst.family);
stream_putc(s, pbime->dst.prefixlen);
stream_put(s, &pbime->dst.u.prefix, prefix_blen(&pbime->dst));
}
static void bgp_encode_pbr_iptable_match(struct stream *s,
struct bgp_pbr_action *bpa,
struct bgp_pbr_match *pbm)
{
stream_putl(s, pbm->unique2);
stream_putl(s, pbm->type);
stream_putl(s, pbm->flags);
/* TODO: correlate with what is contained
* into bgp_pbr_action.
* currently only forward supported
*/
if (bpa->nh.type == NEXTHOP_TYPE_BLACKHOLE)
stream_putl(s, ZEBRA_IPTABLES_DROP);
else
stream_putl(s, ZEBRA_IPTABLES_FORWARD);
stream_putl(s, bpa->fwmark);
stream_put(s, pbm->ipset_name,
ZEBRA_IPSET_NAME_SIZE);
}
/* BGP has established connection with Zebra. */
static void bgp_zebra_connected(struct zclient *zclient)
{
@ -2167,6 +2465,10 @@ void bgp_zebra_init(struct thread_master *master)
zclient->local_ip_prefix_add = bgp_zebra_process_local_ip_prefix;
zclient->local_ip_prefix_del = bgp_zebra_process_local_ip_prefix;
zclient->label_chunk = bgp_zebra_process_label_chunk;
zclient->rule_notify_owner = rule_notify_owner;
zclient->ipset_notify_owner = ipset_notify_owner;
zclient->ipset_entry_notify_owner = ipset_entry_notify_owner;
zclient->iptable_notify_owner = iptable_notify_owner;
}
void bgp_zebra_destroy(void)
@ -2182,3 +2484,176 @@ int bgp_zebra_num_connects(void)
{
return zclient_num_connects;
}
void bgp_send_pbr_rule_action(struct bgp_pbr_action *pbra, bool install)
{
struct stream *s;
if (pbra->install_in_progress)
return;
zlog_debug("%s: table %d fwmark %d %d", __PRETTY_FUNCTION__,
pbra->table_id, pbra->fwmark, install);
s = zclient->obuf;
stream_reset(s);
zclient_create_header(s,
install ? ZEBRA_RULE_ADD : ZEBRA_RULE_DELETE,
VRF_DEFAULT);
stream_putl(s, 1); /* send one pbr action */
bgp_encode_pbr_rule_action(s, pbra);
stream_putw_at(s, 0, stream_get_endp(s));
if (!zclient_send_message(zclient) && install)
pbra->install_in_progress = true;
}
void bgp_send_pbr_ipset_match(struct bgp_pbr_match *pbrim, bool install)
{
struct stream *s;
if (pbrim->install_in_progress)
return;
zlog_debug("%s: name %s type %d %d", __PRETTY_FUNCTION__,
pbrim->ipset_name, pbrim->type, install);
s = zclient->obuf;
stream_reset(s);
zclient_create_header(s,
install ? ZEBRA_IPSET_CREATE :
ZEBRA_IPSET_DESTROY,
VRF_DEFAULT);
stream_putl(s, 1); /* send one pbr action */
bgp_encode_pbr_ipset_match(s, pbrim);
stream_putw_at(s, 0, stream_get_endp(s));
if (!zclient_send_message(zclient) && install)
pbrim->install_in_progress = true;
}
void bgp_send_pbr_ipset_entry_match(struct bgp_pbr_match_entry *pbrime,
bool install)
{
struct stream *s;
if (pbrime->install_in_progress)
return;
zlog_debug("%s: name %s %d %d", __PRETTY_FUNCTION__,
pbrime->backpointer->ipset_name,
pbrime->unique, install);
s = zclient->obuf;
stream_reset(s);
zclient_create_header(s,
install ? ZEBRA_IPSET_ENTRY_ADD :
ZEBRA_IPSET_ENTRY_DELETE,
VRF_DEFAULT);
stream_putl(s, 1); /* send one pbr action */
bgp_encode_pbr_ipset_entry_match(s, pbrime);
stream_putw_at(s, 0, stream_get_endp(s));
if (!zclient_send_message(zclient) && install)
pbrime->install_in_progress = true;
}
void bgp_send_pbr_iptable(struct bgp_pbr_action *pba,
struct bgp_pbr_match *pbm,
bool install)
{
struct stream *s;
if (pbm->install_iptable_in_progress)
return;
zlog_debug("%s: name %s type %d mark %d %d", __PRETTY_FUNCTION__,
pbm->ipset_name, pbm->type, pba->fwmark, install);
s = zclient->obuf;
stream_reset(s);
zclient_create_header(s,
install ? ZEBRA_IPTABLE_ADD :
ZEBRA_IPTABLE_DELETE,
VRF_DEFAULT);
bgp_encode_pbr_iptable_match(s, pba, pbm);
stream_putw_at(s, 0, stream_get_endp(s));
if (!zclient_send_message(zclient) && install) {
pbm->install_iptable_in_progress = true;
pba->refcnt++;
}
}
/* inject in table <table_id> a default route to:
* - if nexthop IP is present : to this nexthop
* - if vrf is different from local : to the matching VRF
*/
void bgp_zebra_announce_default(struct bgp *bgp, struct nexthop *nh,
afi_t afi, uint32_t table_id, bool announce)
{
struct zapi_nexthop *api_nh;
struct zapi_route api;
struct prefix p;
if (!nh || nh->type != NEXTHOP_TYPE_IPV4
|| nh->vrf_id == VRF_UNKNOWN)
return;
memset(&p, 0, sizeof(struct prefix));
/* default route */
if (afi != AFI_IP)
return;
p.family = AF_INET;
memset(&api, 0, sizeof(api));
api.vrf_id = bgp->vrf_id;
api.type = ZEBRA_ROUTE_BGP;
api.safi = SAFI_UNICAST;
api.prefix = p;
api.tableid = table_id;
api.nexthop_num = 1;
SET_FLAG(api.message, ZAPI_MESSAGE_TABLEID);
SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP);
api_nh = &api.nexthops[0];
/* redirect IP */
if (nh->gate.ipv4.s_addr) {
char buff[PREFIX_STRLEN];
api_nh->vrf_id = nh->vrf_id;
api_nh->gate.ipv4 = nh->gate.ipv4;
api_nh->type = NEXTHOP_TYPE_IPV4;
inet_ntop(AF_INET, &(nh->gate.ipv4), buff, INET_ADDRSTRLEN);
if (BGP_DEBUG(zebra, ZEBRA))
zlog_info("BGP: sending default route to %s table %d (redirect IP)",
buff, table_id);
zclient_route_send(announce ? ZEBRA_ROUTE_ADD
: ZEBRA_ROUTE_DELETE,
zclient, &api);
} else if (nh->vrf_id != bgp->vrf_id) {
struct vrf *vrf;
struct interface *ifp;
vrf = vrf_lookup_by_id(nh->vrf_id);
if (!vrf)
return;
/* create default route with interface <VRF>
* with nexthop-vrf <VRF>
*/
ifp = if_lookup_by_name_all_vrf(vrf->name);
if (!ifp)
return;
api_nh->vrf_id = nh->vrf_id;
api_nh->type = NEXTHOP_TYPE_IFINDEX;
api_nh->ifindex = ifp->ifindex;
if (BGP_DEBUG(zebra, ZEBRA))
zlog_info("BGP: sending default route to %s table %d (redirect VRF)",
vrf->name, table_id);
zclient_route_send(announce ? ZEBRA_ROUTE_ADD
: ZEBRA_ROUTE_DELETE,
zclient, &api);
return;
}
}

View File

@ -25,6 +25,7 @@
extern void bgp_zebra_init(struct thread_master *master);
extern void bgp_zebra_init_tm_connect(void);
extern uint32_t bgp_zebra_tm_get_id(void);
extern void bgp_zebra_destroy(void);
extern int bgp_zebra_get_table_range(uint32_t chunk_size,
uint32_t *start, uint32_t *end);
@ -70,4 +71,20 @@ extern int bgp_zebra_advertise_all_vni(struct bgp *, int);
extern int bgp_zebra_num_connects(void);
struct bgp_pbr_action;
struct bgp_pbr_match;
struct bgp_pbr_match_entry;
extern void bgp_send_pbr_rule_action(struct bgp_pbr_action *pbra,
bool install);
extern void bgp_send_pbr_ipset_match(struct bgp_pbr_match *pbrim,
bool install);
extern void bgp_send_pbr_ipset_entry_match(struct bgp_pbr_match_entry *pbrime,
bool install);
extern void bgp_send_pbr_iptable(struct bgp_pbr_action *pba,
struct bgp_pbr_match *pbm,
bool install);
extern void bgp_zebra_announce_default(struct bgp *bgp, struct nexthop *nh,
afi_t afi, uint32_t table_id, bool announce);
#endif /* _QUAGGA_BGP_ZEBRA_H */

View File

@ -83,6 +83,7 @@
#include "bgpd/bgp_ecommunity.h"
#include "bgpd/bgp_flowspec.h"
#include "bgpd/bgp_labelpool.h"
#include "bgpd/bgp_pbr.h"
DEFINE_MTYPE_STATIC(BGPD, PEER_TX_SHUTDOWN_MSG, "Peer shutdown message (TX)");
DEFINE_QOBJ_TYPE(bgp_master)
@ -3006,6 +3007,7 @@ static struct bgp *bgp_create(as_t *as, const char *name,
bf_assign_index(bm->rd_idspace, bgp->vrf_rd_id);
bgp_evpn_init(bgp);
bgp_pbr_init(bgp);
return bgp;
}
@ -3401,7 +3403,7 @@ void bgp_free(struct bgp *bgp)
bf_release_index(bm->rd_idspace, bgp->vrf_rd_id);
bgp_evpn_cleanup(bgp);
bgp_pbr_cleanup(bgp);
if (bgp->name)
XFREE(MTYPE_BGP, bgp->name);
if (bgp->name_pretty)

View File

@ -400,6 +400,25 @@ struct bgp {
/* Allocate MPLS labels */
uint8_t allocate_mpls_labels[AFI_MAX][SAFI_MAX];
/* Allocate hash entries to store policy routing information
* The hash are used to host pbr rules somewhere.
* Actually, pbr will only be used by flowspec
* those hash elements will have relationship together as
* illustrated in below diagram:
*
* pbr_action a <----- pbr_match i <--- pbr_match_entry 1..n
* <----- pbr_match j <--- pbr_match_entry 1..m
*
* - here in BGP structure, the list of match and actions will
* stand for the list of ipset sets, and table_ids in the kernel
* - the arrow above between pbr_match and pbr_action indicate
* that a backpointer permits match to find the action
* - the arrow betwen match_entry and match is a hash list
* contained in match, that lists the whole set of entries
*/
struct hash *pbr_match_hash;
struct hash *pbr_action_hash;
/* timer to re-evaluate neighbor default-originate route-maps */
struct thread *t_rmap_def_originate_eval;
#define RMAP_DEFAULT_ORIGINATE_EVAL_TIMER 5

View File

@ -1374,6 +1374,26 @@ stream_failure:
return false;
}
bool zapi_iptable_notify_decode(struct stream *s,
uint32_t *unique,
enum zapi_iptable_notify_owner *note)
{
uint32_t uni;
STREAM_GET(note, s, sizeof(*note));
STREAM_GETL(s, uni);
if (zclient_debug)
zlog_debug("%s: %u", __PRETTY_FUNCTION__, uni);
*unique = uni;
return true;
stream_failure:
return false;
}
struct nexthop *nexthop_from_zapi_nexthop(struct zapi_nexthop *znh)
{
struct nexthop *n = nexthop_new();
@ -2765,6 +2785,22 @@ static int zclient_read(struct thread *thread)
(*zclient->label_chunk)(command, zclient, length,
vrf_id);
break;
case ZEBRA_IPSET_NOTIFY_OWNER:
if (zclient->ipset_notify_owner)
(*zclient->ipset_notify_owner)(command, zclient, length,
vrf_id);
break;
case ZEBRA_IPSET_ENTRY_NOTIFY_OWNER:
if (zclient->ipset_entry_notify_owner)
(*zclient->ipset_entry_notify_owner)(command,
zclient, length,
vrf_id);
break;
case ZEBRA_IPTABLE_NOTIFY_OWNER:
if (zclient->iptable_notify_owner)
(*zclient->iptable_notify_owner)(command,
zclient, length,
vrf_id);
default:
break;
}

View File

@ -258,6 +258,10 @@ struct zclient {
struct zclient *zclient,
uint16_t length,
vrf_id_t vrf_id);
int (*iptable_notify_owner)(int command,
struct zclient *zclient,
uint16_t length,
vrf_id_t vrf_id);
};
/* Zebra API message flag. */
@ -680,6 +684,9 @@ bool zapi_ipset_entry_notify_decode(struct stream *s,
uint32_t *unique,
char *ipset_name,
enum zapi_ipset_entry_notify_owner *note);
bool zapi_iptable_notify_decode(struct stream *s,
uint32_t *unique,
enum zapi_iptable_notify_owner *note);
extern struct nexthop *nexthop_from_zapi_nexthop(struct zapi_nexthop *znh);
extern bool zapi_nexthop_update_decode(struct stream *s,

View File

@ -54,6 +54,7 @@ vtysh_scan += $(top_srcdir)/bgpd/bgp_nexthop.c
vtysh_scan += $(top_srcdir)/bgpd/bgp_route.c
vtysh_scan += $(top_srcdir)/bgpd/bgp_routemap.c
vtysh_scan += $(top_srcdir)/bgpd/bgp_vty.c
vtysh_scan += $(top_srcdir)/bgpd/bgp_flowspec_vty.c
endif
if RPKI