mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-08-14 08:50:26 +00:00
Merge pull request #5799 from pguibert6WIND/flowspec_ipv6
Flowspec ipv6
This commit is contained in:
commit
c6d41e93e0
@ -84,6 +84,7 @@ static const struct message attr_str[] = {
|
||||
#endif
|
||||
{BGP_ATTR_LARGE_COMMUNITIES, "LARGE_COMMUNITY"},
|
||||
{BGP_ATTR_PREFIX_SID, "PREFIX_SID"},
|
||||
{BGP_ATTR_IPV6_EXT_COMMUNITIES, "IPV6_EXT_COMMUNITIES"},
|
||||
{0}};
|
||||
|
||||
static const struct message attr_flag_str[] = {
|
||||
@ -662,6 +663,8 @@ unsigned int attrhash_key_make(const void *p)
|
||||
MIX(lcommunity_hash_make(attr->lcommunity));
|
||||
if (attr->ecommunity)
|
||||
MIX(ecommunity_hash_make(attr->ecommunity));
|
||||
if (attr->ipv6_ecommunity)
|
||||
MIX(ecommunity_hash_make(attr->ipv6_ecommunity));
|
||||
if (attr->cluster)
|
||||
MIX(cluster_hash_key_make(attr->cluster));
|
||||
if (attr->transit)
|
||||
@ -700,6 +703,7 @@ bool attrhash_cmp(const void *p1, const void *p2)
|
||||
&& attr1->label_index == attr2->label_index
|
||||
&& attr1->mp_nexthop_len == attr2->mp_nexthop_len
|
||||
&& attr1->ecommunity == attr2->ecommunity
|
||||
&& attr1->ipv6_ecommunity == attr2->ipv6_ecommunity
|
||||
&& attr1->lcommunity == attr2->lcommunity
|
||||
&& attr1->cluster == attr2->cluster
|
||||
&& attr1->transit == attr2->transit
|
||||
@ -829,6 +833,15 @@ struct attr *bgp_attr_intern(struct attr *attr)
|
||||
else
|
||||
attr->ecommunity->refcnt++;
|
||||
}
|
||||
|
||||
if (attr->ipv6_ecommunity) {
|
||||
if (!attr->ipv6_ecommunity->refcnt)
|
||||
attr->ipv6_ecommunity =
|
||||
ecommunity_intern(attr->ipv6_ecommunity);
|
||||
else
|
||||
attr->ipv6_ecommunity->refcnt++;
|
||||
}
|
||||
|
||||
if (attr->lcommunity) {
|
||||
if (!attr->lcommunity->refcnt)
|
||||
attr->lcommunity = lcommunity_intern(attr->lcommunity);
|
||||
@ -1034,6 +1047,10 @@ void bgp_attr_unintern_sub(struct attr *attr)
|
||||
ecommunity_unintern(&attr->ecommunity);
|
||||
UNSET_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES));
|
||||
|
||||
if (attr->ipv6_ecommunity)
|
||||
ecommunity_unintern(&attr->ipv6_ecommunity);
|
||||
UNSET_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_IPV6_EXT_COMMUNITIES));
|
||||
|
||||
if (attr->lcommunity)
|
||||
lcommunity_unintern(&attr->lcommunity);
|
||||
UNSET_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_LARGE_COMMUNITIES));
|
||||
@ -1118,6 +1135,8 @@ void bgp_attr_flush(struct attr *attr)
|
||||
community_free(&attr->community);
|
||||
if (attr->ecommunity && !attr->ecommunity->refcnt)
|
||||
ecommunity_free(&attr->ecommunity);
|
||||
if (attr->ipv6_ecommunity && !attr->ipv6_ecommunity->refcnt)
|
||||
ecommunity_free(&attr->ipv6_ecommunity);
|
||||
if (attr->lcommunity && !attr->lcommunity->refcnt)
|
||||
lcommunity_free(&attr->lcommunity);
|
||||
if (attr->cluster && !attr->cluster->refcnt) {
|
||||
@ -1201,6 +1220,7 @@ bgp_attr_malformed(struct bgp_attr_parser_args *args, uint8_t subcode,
|
||||
case BGP_ATTR_LOCAL_PREF:
|
||||
case BGP_ATTR_COMMUNITIES:
|
||||
case BGP_ATTR_EXT_COMMUNITIES:
|
||||
case BGP_ATTR_IPV6_EXT_COMMUNITIES:
|
||||
case BGP_ATTR_LARGE_COMMUNITIES:
|
||||
case BGP_ATTR_ORIGINATOR_ID:
|
||||
case BGP_ATTR_CLUSTER_LIST:
|
||||
@ -1286,6 +1306,8 @@ const uint8_t attr_flags_values[] = {
|
||||
[BGP_ATTR_LARGE_COMMUNITIES] =
|
||||
BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS,
|
||||
[BGP_ATTR_PREFIX_SID] = BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS,
|
||||
[BGP_ATTR_IPV6_EXT_COMMUNITIES] =
|
||||
BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS,
|
||||
};
|
||||
static const size_t attr_flags_values_max = array_size(attr_flags_values) - 1;
|
||||
|
||||
@ -2264,6 +2286,34 @@ bgp_attr_ext_communities(struct bgp_attr_parser_args *args)
|
||||
return BGP_ATTR_PARSE_PROCEED;
|
||||
}
|
||||
|
||||
/* IPv6 Extended Community attribute. */
|
||||
static bgp_attr_parse_ret_t
|
||||
bgp_attr_ipv6_ext_communities(struct bgp_attr_parser_args *args)
|
||||
{
|
||||
struct peer *const peer = args->peer;
|
||||
struct attr *const attr = args->attr;
|
||||
const bgp_size_t length = args->length;
|
||||
|
||||
if (length == 0) {
|
||||
attr->ipv6_ecommunity = NULL;
|
||||
return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_OPT_ATTR_ERR,
|
||||
args->total);
|
||||
}
|
||||
|
||||
attr->ipv6_ecommunity =
|
||||
ecommunity_parse_ipv6(stream_pnt(peer->curr), length);
|
||||
/* XXX: fix ecommunity_parse to use stream API */
|
||||
stream_forward_getp(peer->curr, length);
|
||||
|
||||
if (!attr->ipv6_ecommunity)
|
||||
return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_OPT_ATTR_ERR,
|
||||
args->total);
|
||||
|
||||
attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_IPV6_EXT_COMMUNITIES);
|
||||
|
||||
return BGP_ATTR_PARSE_PROCEED;
|
||||
}
|
||||
|
||||
/* Parse Tunnel Encap attribute in an UPDATE */
|
||||
static int bgp_attr_encap(uint8_t type, struct peer *peer, /* IN */
|
||||
bgp_size_t length, /* IN: attr's length field */
|
||||
@ -3082,6 +3132,9 @@ bgp_attr_parse_ret_t bgp_attr_parse(struct peer *peer, struct attr *attr,
|
||||
case BGP_ATTR_PMSI_TUNNEL:
|
||||
ret = bgp_attr_pmsi_tunnel(&attr_args);
|
||||
break;
|
||||
case BGP_ATTR_IPV6_EXT_COMMUNITIES:
|
||||
ret = bgp_attr_ipv6_ext_communities(&attr_args);
|
||||
break;
|
||||
default:
|
||||
ret = bgp_attr_unknown(&attr_args);
|
||||
break;
|
||||
|
@ -177,6 +177,9 @@ struct attr {
|
||||
/* Extended Communities attribute. */
|
||||
struct ecommunity *ecommunity;
|
||||
|
||||
/* Extended Communities attribute. */
|
||||
struct ecommunity *ipv6_ecommunity;
|
||||
|
||||
/* Large Communities attribute. */
|
||||
struct lcommunity *lcommunity;
|
||||
|
||||
|
@ -2692,7 +2692,8 @@ const char *bgp_debug_rdpfxpath2str(afi_t afi, safi_t safi,
|
||||
bgp_fs_nlri_get_string((unsigned char *)fs->prefix.ptr,
|
||||
fs->prefix.prefixlen,
|
||||
return_string,
|
||||
NLRI_STRING_FORMAT_DEBUG, NULL);
|
||||
NLRI_STRING_FORMAT_DEBUG, NULL,
|
||||
family2afi(fs->prefix.family));
|
||||
snprintf(str, size, "FS %s Match{%s}", afi2str(afi),
|
||||
return_string);
|
||||
} else
|
||||
|
@ -48,7 +48,12 @@ static struct hash *ecomhash;
|
||||
/* Allocate a new ecommunities. */
|
||||
struct ecommunity *ecommunity_new(void)
|
||||
{
|
||||
return XCALLOC(MTYPE_ECOMMUNITY, sizeof(struct ecommunity));
|
||||
struct ecommunity *ecom;
|
||||
|
||||
ecom = (struct ecommunity *)XCALLOC(MTYPE_ECOMMUNITY,
|
||||
sizeof(struct ecommunity));
|
||||
ecom->unit_size = ECOMMUNITY_SIZE;
|
||||
return ecom;
|
||||
}
|
||||
|
||||
void ecommunity_strfree(char **s)
|
||||
@ -83,36 +88,56 @@ static void ecommunity_hash_free(struct ecommunity *ecom)
|
||||
once and whether the new value should replace what is existing or
|
||||
not.
|
||||
*/
|
||||
bool ecommunity_add_val(struct ecommunity *ecom, struct ecommunity_val *eval,
|
||||
bool unique, bool overwrite)
|
||||
static bool ecommunity_add_val_internal(struct ecommunity *ecom,
|
||||
const void *eval,
|
||||
bool unique, bool overwrite,
|
||||
uint8_t ecom_size)
|
||||
{
|
||||
int c, ins_idx;
|
||||
const struct ecommunity_val *eval4 = (struct ecommunity_val *)eval;
|
||||
const struct ecommunity_val_ipv6 *eval6 =
|
||||
(struct ecommunity_val_ipv6 *)eval;
|
||||
|
||||
/* When this is fist value, just add it. */
|
||||
if (ecom->val == NULL) {
|
||||
ecom->size = 1;
|
||||
ecom->val = XCALLOC(MTYPE_ECOMMUNITY_VAL, ECOMMUNITY_SIZE);
|
||||
memcpy(ecom->val, eval->val, ECOMMUNITY_SIZE);
|
||||
ecom->val = XMALLOC(MTYPE_ECOMMUNITY_VAL,
|
||||
ecom_length_size(ecom, ecom_size));
|
||||
memcpy(ecom->val, eval, ecom_size);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* If the value already exists in the structure return 0. */
|
||||
/* check also if the extended community itself exists. */
|
||||
c = 0;
|
||||
|
||||
ins_idx = -1;
|
||||
for (uint8_t *p = ecom->val; c < ecom->size;
|
||||
p += ECOMMUNITY_SIZE, c++) {
|
||||
p += ecom_size, c++) {
|
||||
if (unique) {
|
||||
if (p[0] == eval->val[0] &&
|
||||
p[1] == eval->val[1]) {
|
||||
if (overwrite) {
|
||||
memcpy(p, eval->val, ECOMMUNITY_SIZE);
|
||||
return true;
|
||||
if (ecom_size == ECOMMUNITY_SIZE) {
|
||||
if (p[0] == eval4->val[0] &&
|
||||
p[1] == eval4->val[1]) {
|
||||
if (overwrite) {
|
||||
memcpy(p, eval4->val,
|
||||
ecom_size);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (p[0] == eval6->val[0] &&
|
||||
p[1] == eval6->val[1]) {
|
||||
if (overwrite) {
|
||||
memcpy(p, eval6->val,
|
||||
ecom_size);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
int ret = memcmp(p, eval->val, ECOMMUNITY_SIZE);
|
||||
int ret = memcmp(p, eval, ecom_size);
|
||||
if (ret == 0)
|
||||
return false;
|
||||
if (ret > 0) {
|
||||
@ -129,61 +154,106 @@ bool ecommunity_add_val(struct ecommunity *ecom, struct ecommunity_val *eval,
|
||||
/* Add the value to the structure with numerical sorting. */
|
||||
ecom->size++;
|
||||
ecom->val = XREALLOC(MTYPE_ECOMMUNITY_VAL, ecom->val,
|
||||
ecom->size * ECOMMUNITY_SIZE);
|
||||
ecom_length_size(ecom, ecom_size));
|
||||
|
||||
memmove(ecom->val + ((ins_idx + 1) * ECOMMUNITY_SIZE),
|
||||
ecom->val + (ins_idx * ECOMMUNITY_SIZE),
|
||||
(ecom->size - 1 - ins_idx) * ECOMMUNITY_SIZE);
|
||||
memcpy(ecom->val + (ins_idx * ECOMMUNITY_SIZE),
|
||||
eval->val, ECOMMUNITY_SIZE);
|
||||
|
||||
memmove(ecom->val + ((ins_idx + 1) * ecom_size),
|
||||
ecom->val + (ins_idx * ecom_size),
|
||||
(ecom->size - 1 - ins_idx) * ecom_size);
|
||||
memcpy(ecom->val + (ins_idx * ecom_size),
|
||||
eval, ecom_size);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* This function takes pointer to Extended Communites strucutre then
|
||||
create a new Extended Communities structure by uniq and sort each
|
||||
Extended Communities value. */
|
||||
struct ecommunity *ecommunity_uniq_sort(struct ecommunity *ecom)
|
||||
/* Add a new Extended Communities value to Extended Communities
|
||||
* Attribute structure. When the value is already exists in the
|
||||
* structure, we don't add the value. Newly added value is sorted by
|
||||
* numerical order. When the value is added to the structure return 1
|
||||
* else return 0.
|
||||
*/
|
||||
bool ecommunity_add_val(struct ecommunity *ecom, struct ecommunity_val *eval,
|
||||
bool unique, bool overwrite)
|
||||
{
|
||||
return ecommunity_add_val_internal(ecom, (const void *)eval, unique,
|
||||
overwrite, ECOMMUNITY_SIZE);
|
||||
}
|
||||
|
||||
bool ecommunity_add_val_ipv6(struct ecommunity *ecom,
|
||||
struct ecommunity_val_ipv6 *eval,
|
||||
bool unique, bool overwrite)
|
||||
{
|
||||
return ecommunity_add_val_internal(ecom, (const void *)eval, unique,
|
||||
overwrite, IPV6_ECOMMUNITY_SIZE);
|
||||
}
|
||||
|
||||
static struct ecommunity *
|
||||
ecommunity_uniq_sort_internal(struct ecommunity *ecom,
|
||||
unsigned short ecom_size)
|
||||
{
|
||||
int i;
|
||||
struct ecommunity *new;
|
||||
struct ecommunity_val *eval;
|
||||
const void *eval;
|
||||
|
||||
if (!ecom)
|
||||
return NULL;
|
||||
|
||||
new = ecommunity_new();
|
||||
new->unit_size = ecom_size;
|
||||
|
||||
for (i = 0; i < ecom->size; i++) {
|
||||
eval = (struct ecommunity_val *)(ecom->val
|
||||
+ (i * ECOMMUNITY_SIZE));
|
||||
ecommunity_add_val(new, eval, false, false);
|
||||
eval = (void *)(ecom->val + (i * ecom_size));
|
||||
ecommunity_add_val_internal(new, eval, false, false, ecom_size);
|
||||
}
|
||||
return new;
|
||||
}
|
||||
|
||||
/* This function takes pointer to Extended Communites strucutre then
|
||||
* create a new Extended Communities structure by uniq and sort each
|
||||
* Extended Communities value.
|
||||
*/
|
||||
struct ecommunity *ecommunity_uniq_sort(struct ecommunity *ecom)
|
||||
{
|
||||
return ecommunity_uniq_sort_internal(ecom, ECOMMUNITY_SIZE);
|
||||
}
|
||||
|
||||
/* Parse Extended Communites Attribute in BGP packet. */
|
||||
struct ecommunity *ecommunity_parse(uint8_t *pnt, unsigned short length)
|
||||
static struct ecommunity *ecommunity_parse_internal(uint8_t *pnt,
|
||||
unsigned short length,
|
||||
unsigned short size_ecom)
|
||||
{
|
||||
struct ecommunity tmp;
|
||||
struct ecommunity *new;
|
||||
|
||||
/* Length check. */
|
||||
if (length % ECOMMUNITY_SIZE)
|
||||
if (length % size_ecom)
|
||||
return NULL;
|
||||
|
||||
/* Prepare tmporary structure for making a new Extended Communities
|
||||
Attribute. */
|
||||
tmp.size = length / ECOMMUNITY_SIZE;
|
||||
tmp.size = length / size_ecom;
|
||||
tmp.val = pnt;
|
||||
|
||||
/* Create a new Extended Communities Attribute by uniq and sort each
|
||||
Extended Communities value */
|
||||
new = ecommunity_uniq_sort(&tmp);
|
||||
new = ecommunity_uniq_sort_internal(&tmp, size_ecom);
|
||||
|
||||
return ecommunity_intern(new);
|
||||
}
|
||||
|
||||
struct ecommunity *ecommunity_parse(uint8_t *pnt,
|
||||
unsigned short length)
|
||||
{
|
||||
return ecommunity_parse_internal(pnt, length, ECOMMUNITY_SIZE);
|
||||
}
|
||||
|
||||
struct ecommunity *ecommunity_parse_ipv6(uint8_t *pnt,
|
||||
unsigned short length)
|
||||
{
|
||||
return ecommunity_parse_internal(pnt, length,
|
||||
IPV6_ECOMMUNITY_SIZE);
|
||||
}
|
||||
|
||||
/* Duplicate the Extended Communities Attribute structure. */
|
||||
struct ecommunity *ecommunity_dup(struct ecommunity *ecom)
|
||||
{
|
||||
@ -191,10 +261,11 @@ struct ecommunity *ecommunity_dup(struct ecommunity *ecom)
|
||||
|
||||
new = XCALLOC(MTYPE_ECOMMUNITY, sizeof(struct ecommunity));
|
||||
new->size = ecom->size;
|
||||
new->unit_size = ecom->unit_size;
|
||||
if (new->size) {
|
||||
new->val = XMALLOC(MTYPE_ECOMMUNITY_VAL,
|
||||
ecom->size * ECOMMUNITY_SIZE);
|
||||
memcpy(new->val, ecom->val, ecom->size * ECOMMUNITY_SIZE);
|
||||
ecom->size * ecom->unit_size);
|
||||
memcpy(new->val, ecom->val, ecom->size * ecom->unit_size);
|
||||
} else
|
||||
new->val = NULL;
|
||||
return new;
|
||||
@ -216,14 +287,16 @@ struct ecommunity *ecommunity_merge(struct ecommunity *ecom1,
|
||||
if (ecom1->val)
|
||||
ecom1->val =
|
||||
XREALLOC(MTYPE_ECOMMUNITY_VAL, ecom1->val,
|
||||
(ecom1->size + ecom2->size) * ECOMMUNITY_SIZE);
|
||||
(ecom1->size + ecom2->size) *
|
||||
ecom1->unit_size);
|
||||
else
|
||||
ecom1->val =
|
||||
XMALLOC(MTYPE_ECOMMUNITY_VAL,
|
||||
(ecom1->size + ecom2->size) * ECOMMUNITY_SIZE);
|
||||
(ecom1->size + ecom2->size) *
|
||||
ecom1->unit_size);
|
||||
|
||||
memcpy(ecom1->val + (ecom1->size * ECOMMUNITY_SIZE), ecom2->val,
|
||||
ecom2->size * ECOMMUNITY_SIZE);
|
||||
memcpy(ecom1->val + (ecom1->size * ecom1->unit_size), ecom2->val,
|
||||
ecom2->size * ecom1->unit_size);
|
||||
ecom1->size += ecom2->size;
|
||||
|
||||
return ecom1;
|
||||
@ -235,9 +308,7 @@ struct ecommunity *ecommunity_intern(struct ecommunity *ecom)
|
||||
struct ecommunity *find;
|
||||
|
||||
assert(ecom->refcnt == 0);
|
||||
|
||||
find = (struct ecommunity *)hash_get(ecomhash, ecom, hash_alloc_intern);
|
||||
|
||||
if (find != ecom)
|
||||
ecommunity_free(&ecom);
|
||||
|
||||
@ -272,7 +343,7 @@ void ecommunity_unintern(struct ecommunity **ecom)
|
||||
unsigned int ecommunity_hash_make(const void *arg)
|
||||
{
|
||||
const struct ecommunity *ecom = arg;
|
||||
int size = ecom->size * ECOMMUNITY_SIZE;
|
||||
int size = ecom->size * ecom->unit_size;
|
||||
|
||||
return jhash(ecom->val, size, 0x564321ab);
|
||||
}
|
||||
@ -289,9 +360,12 @@ bool ecommunity_cmp(const void *arg1, const void *arg2)
|
||||
if (ecom1 == NULL || ecom2 == NULL)
|
||||
return false;
|
||||
|
||||
if (ecom1->unit_size != ecom2->unit_size)
|
||||
return false;
|
||||
|
||||
return (ecom1->size == ecom2->size
|
||||
&& memcmp(ecom1->val, ecom2->val, ecom1->size * ECOMMUNITY_SIZE)
|
||||
== 0);
|
||||
&& memcmp(ecom1->val, ecom2->val, ecom1->size *
|
||||
ecom1->unit_size) == 0);
|
||||
}
|
||||
|
||||
/* Initialize Extended Comminities related hash. */
|
||||
@ -314,16 +388,21 @@ enum ecommunity_token {
|
||||
ecommunity_token_rt,
|
||||
ecommunity_token_soo,
|
||||
ecommunity_token_val,
|
||||
ecommunity_token_rt6,
|
||||
ecommunity_token_val6,
|
||||
};
|
||||
|
||||
/*
|
||||
* Encode BGP extended community from passed values. Supports types
|
||||
* defined in RFC 4360 and well-known sub-types.
|
||||
*/
|
||||
static int ecommunity_encode(uint8_t type, uint8_t sub_type, int trans, as_t as,
|
||||
struct in_addr ip, uint32_t val,
|
||||
struct ecommunity_val *eval)
|
||||
static int ecommunity_encode_internal(uint8_t type, uint8_t sub_type,
|
||||
int trans, as_t as,
|
||||
struct in_addr *ip,
|
||||
struct in6_addr *ip6,
|
||||
uint32_t val,
|
||||
void *eval_ptr)
|
||||
{
|
||||
struct ecommunity_val *eval = (struct ecommunity_val *)eval_ptr;
|
||||
struct ecommunity_val_ipv6 *eval6 =
|
||||
(struct ecommunity_val_ipv6 *)eval_ptr;
|
||||
|
||||
assert(eval);
|
||||
if (type == ECOMMUNITY_ENCODE_AS) {
|
||||
if (as > BGP_AS_MAX)
|
||||
@ -332,6 +411,10 @@ static int ecommunity_encode(uint8_t type, uint8_t sub_type, int trans, as_t as,
|
||||
|| type == ECOMMUNITY_ENCODE_AS4) {
|
||||
if (val > UINT16_MAX)
|
||||
return -1;
|
||||
} else if (type == ECOMMUNITY_ENCODE_TRANS_EXP &&
|
||||
sub_type == ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6 &&
|
||||
(!ip6 || val > UINT16_MAX)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Fill in the values. */
|
||||
@ -347,9 +430,14 @@ static int ecommunity_encode(uint8_t type, uint8_t sub_type, int trans, as_t as,
|
||||
eval->val[6] = (val >> 8) & 0xff;
|
||||
eval->val[7] = val & 0xff;
|
||||
} else if (type == ECOMMUNITY_ENCODE_IP) {
|
||||
memcpy(&eval->val[2], &ip, sizeof(struct in_addr));
|
||||
memcpy(&eval->val[2], ip, sizeof(struct in_addr));
|
||||
eval->val[6] = (val >> 8) & 0xff;
|
||||
eval->val[7] = val & 0xff;
|
||||
} else if (type == ECOMMUNITY_ENCODE_TRANS_EXP &&
|
||||
sub_type == ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6) {
|
||||
memcpy(&eval6->val[2], ip6, sizeof(struct in6_addr));
|
||||
eval6->val[18] = (val >> 8) & 0xff;
|
||||
eval6->val[19] = val & 0xff;
|
||||
} else {
|
||||
eval->val[2] = (as >> 24) & 0xff;
|
||||
eval->val[3] = (as >> 16) & 0xff;
|
||||
@ -362,9 +450,21 @@ static int ecommunity_encode(uint8_t type, uint8_t sub_type, int trans, as_t as,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Encode BGP extended community from passed values. Supports types
|
||||
* defined in RFC 4360 and well-known sub-types.
|
||||
*/
|
||||
static int ecommunity_encode(uint8_t type, uint8_t sub_type, int trans, as_t as,
|
||||
struct in_addr ip, uint32_t val,
|
||||
struct ecommunity_val *eval)
|
||||
{
|
||||
return ecommunity_encode_internal(type, sub_type, trans, as,
|
||||
&ip, NULL, val, (void *)eval);
|
||||
}
|
||||
|
||||
/* Get next Extended Communities token from the string. */
|
||||
static const char *ecommunity_gettoken(const char *str,
|
||||
struct ecommunity_val *eval,
|
||||
void *eval_ptr,
|
||||
enum ecommunity_token *token)
|
||||
{
|
||||
int ret;
|
||||
@ -374,11 +474,12 @@ static const char *ecommunity_gettoken(const char *str,
|
||||
const char *p = str;
|
||||
char *endptr;
|
||||
struct in_addr ip;
|
||||
struct in6_addr ip6;
|
||||
as_t as = 0;
|
||||
uint32_t val = 0;
|
||||
uint8_t ecomm_type;
|
||||
char buf[INET_ADDRSTRLEN + 1];
|
||||
|
||||
struct ecommunity_val *eval = (struct ecommunity_val *)eval_ptr;
|
||||
/* Skip white space. */
|
||||
while (isspace((unsigned char)*p)) {
|
||||
p++;
|
||||
@ -396,7 +497,10 @@ static const char *ecommunity_gettoken(const char *str,
|
||||
p++;
|
||||
if (tolower((unsigned char)*p) == 't') {
|
||||
p++;
|
||||
*token = ecommunity_token_rt;
|
||||
if (*p != '\0' && tolower((int)*p) == '6')
|
||||
*token = ecommunity_token_rt6;
|
||||
else
|
||||
*token = ecommunity_token_rt;
|
||||
return p;
|
||||
}
|
||||
if (isspace((unsigned char)*p) || *p == '\0') {
|
||||
@ -435,6 +539,7 @@ static const char *ecommunity_gettoken(const char *str,
|
||||
* a) A.B.C.D:MN
|
||||
* b) EF:OPQR
|
||||
* c) GHJK:MN
|
||||
* d) <IPV6>:MN (only with rt6)
|
||||
*
|
||||
* A.B.C.D: Four Byte IP
|
||||
* EF: Two byte ASN
|
||||
@ -443,6 +548,38 @@ static const char *ecommunity_gettoken(const char *str,
|
||||
* OPQR: Four byte value
|
||||
*
|
||||
*/
|
||||
/* IPv6 case : look for last ':' */
|
||||
if (*token == ecommunity_token_rt6 ||
|
||||
*token == ecommunity_token_val6) {
|
||||
char *limit;
|
||||
|
||||
limit = endptr = strrchr(p, ':');
|
||||
if (!endptr)
|
||||
goto error;
|
||||
|
||||
endptr++;
|
||||
as = strtoul(endptr, &endptr, 10);
|
||||
if (*endptr != '\0' || as == BGP_AS4_MAX)
|
||||
goto error;
|
||||
|
||||
memcpy(buf, p, (limit - p));
|
||||
buf[limit - p] = '\0';
|
||||
ret = inet_pton(AF_INET6, buf, &ip6);
|
||||
if (ret == 0)
|
||||
goto error;
|
||||
|
||||
ecomm_type = ECOMMUNITY_ENCODE_TRANS_EXP;
|
||||
if (ecommunity_encode_internal(ecomm_type,
|
||||
ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6,
|
||||
1, 0, NULL, &ip6, as, eval_ptr))
|
||||
goto error;
|
||||
|
||||
*token = ecommunity_token_val6;
|
||||
while (isdigit((int)*p) || *p == ':' || *p == '.') {
|
||||
p++;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
while (isdigit((unsigned char)*p) || *p == ':' || *p == '.') {
|
||||
if (*p == ':') {
|
||||
if (separator)
|
||||
@ -508,41 +645,18 @@ error:
|
||||
return p;
|
||||
}
|
||||
|
||||
/* Convert string to extended community attribute.
|
||||
|
||||
When type is already known, please specify both str and type. str
|
||||
should not include keyword such as "rt" and "soo". Type is
|
||||
ECOMMUNITY_ROUTE_TARGET or ECOMMUNITY_SITE_ORIGIN.
|
||||
keyword_included should be zero.
|
||||
|
||||
For example route-map's "set extcommunity" command case:
|
||||
|
||||
"rt 100:1 100:2 100:3" -> str = "100:1 100:2 100:3"
|
||||
type = ECOMMUNITY_ROUTE_TARGET
|
||||
keyword_included = 0
|
||||
|
||||
"soo 100:1" -> str = "100:1"
|
||||
type = ECOMMUNITY_SITE_ORIGIN
|
||||
keyword_included = 0
|
||||
|
||||
When string includes keyword for each extended community value.
|
||||
Please specify keyword_included as non-zero value.
|
||||
|
||||
For example standard extcommunity-list case:
|
||||
|
||||
"rt 100:1 rt 100:2 soo 100:1" -> str = "rt 100:1 rt 100:2 soo 100:1"
|
||||
type = 0
|
||||
keyword_include = 1
|
||||
*/
|
||||
struct ecommunity *ecommunity_str2com(const char *str, int type,
|
||||
int keyword_included)
|
||||
static struct ecommunity *ecommunity_str2com_internal(const char *str, int type,
|
||||
int keyword_included,
|
||||
bool is_ipv6_extcomm)
|
||||
{
|
||||
struct ecommunity *ecom = NULL;
|
||||
enum ecommunity_token token = ecommunity_token_unknown;
|
||||
struct ecommunity_val eval;
|
||||
struct ecommunity_val_ipv6 eval;
|
||||
int keyword = 0;
|
||||
|
||||
while ((str = ecommunity_gettoken(str, &eval, &token))) {
|
||||
if (is_ipv6_extcomm)
|
||||
token = ecommunity_token_rt6;
|
||||
while ((str = ecommunity_gettoken(str, (void *)&eval, &token))) {
|
||||
switch (token) {
|
||||
case ecommunity_token_rt:
|
||||
case ecommunity_token_soo:
|
||||
@ -553,7 +667,8 @@ struct ecommunity *ecommunity_str2com(const char *str, int type,
|
||||
}
|
||||
keyword = 1;
|
||||
|
||||
if (token == ecommunity_token_rt) {
|
||||
if (token == ecommunity_token_rt ||
|
||||
token == ecommunity_token_rt6) {
|
||||
type = ECOMMUNITY_ROUTE_TARGET;
|
||||
}
|
||||
if (token == ecommunity_token_soo) {
|
||||
@ -563,8 +678,7 @@ struct ecommunity *ecommunity_str2com(const char *str, int type,
|
||||
case ecommunity_token_val:
|
||||
if (keyword_included) {
|
||||
if (!keyword) {
|
||||
if (ecom)
|
||||
ecommunity_free(&ecom);
|
||||
ecommunity_free(&ecom);
|
||||
return NULL;
|
||||
}
|
||||
keyword = 0;
|
||||
@ -572,7 +686,24 @@ struct ecommunity *ecommunity_str2com(const char *str, int type,
|
||||
if (ecom == NULL)
|
||||
ecom = ecommunity_new();
|
||||
eval.val[1] = type;
|
||||
ecommunity_add_val(ecom, &eval, false, false);
|
||||
ecommunity_add_val_internal(ecom, (void *)&eval,
|
||||
false, false,
|
||||
ecom->unit_size);
|
||||
break;
|
||||
case ecommunity_token_val6:
|
||||
if (keyword_included) {
|
||||
if (!keyword) {
|
||||
ecommunity_free(&ecom);
|
||||
return NULL;
|
||||
}
|
||||
keyword = 0;
|
||||
}
|
||||
if (ecom == NULL)
|
||||
ecom = ecommunity_new();
|
||||
ecom->unit_size = IPV6_ECOMMUNITY_SIZE;
|
||||
eval.val[1] = type;
|
||||
ecommunity_add_val_internal(ecom, (void *)&eval, false, false,
|
||||
ecom->unit_size);
|
||||
break;
|
||||
case ecommunity_token_unknown:
|
||||
default:
|
||||
@ -584,16 +715,59 @@ struct ecommunity *ecommunity_str2com(const char *str, int type,
|
||||
return ecom;
|
||||
}
|
||||
|
||||
static int ecommunity_rt_soo_str(char *buf, size_t bufsz, const uint8_t *pnt,
|
||||
int type, int sub_type, int format)
|
||||
/* Convert string to extended community attribute.
|
||||
*
|
||||
* When type is already known, please specify both str and type. str
|
||||
* should not include keyword such as "rt" and "soo". Type is
|
||||
* ECOMMUNITY_ROUTE_TARGET or ECOMMUNITY_SITE_ORIGIN.
|
||||
* keyword_included should be zero.
|
||||
*
|
||||
* For example route-map's "set extcommunity" command case:
|
||||
*
|
||||
* "rt 100:1 100:2 100:3" -> str = "100:1 100:2 100:3"
|
||||
* type = ECOMMUNITY_ROUTE_TARGET
|
||||
* keyword_included = 0
|
||||
*
|
||||
* "soo 100:1" -> str = "100:1"
|
||||
* type = ECOMMUNITY_SITE_ORIGIN
|
||||
* keyword_included = 0
|
||||
*
|
||||
* When string includes keyword for each extended community value.
|
||||
* Please specify keyword_included as non-zero value.
|
||||
*
|
||||
* For example standard extcommunity-list case:
|
||||
*
|
||||
* "rt 100:1 rt 100:2 soo 100:1" -> str = "rt 100:1 rt 100:2 soo 100:1"
|
||||
* type = 0
|
||||
* keyword_include = 1
|
||||
*/
|
||||
struct ecommunity *ecommunity_str2com(const char *str, int type,
|
||||
int keyword_included)
|
||||
{
|
||||
return ecommunity_str2com_internal(str, type,
|
||||
keyword_included, false);
|
||||
}
|
||||
|
||||
struct ecommunity *ecommunity_str2com_ipv6(const char *str, int type,
|
||||
int keyword_included)
|
||||
{
|
||||
return ecommunity_str2com_internal(str, type,
|
||||
keyword_included, true);
|
||||
}
|
||||
|
||||
static int ecommunity_rt_soo_str_internal(char *buf, size_t bufsz,
|
||||
const uint8_t *pnt, int type,
|
||||
int sub_type, int format,
|
||||
unsigned short ecom_size)
|
||||
{
|
||||
int len = 0;
|
||||
const char *prefix;
|
||||
char buf_local[INET6_ADDRSTRLEN];
|
||||
|
||||
/* For parse Extended Community attribute tupple. */
|
||||
struct ecommunity_as eas;
|
||||
struct ecommunity_ip eip;
|
||||
|
||||
struct ecommunity_ip6 eip6;
|
||||
|
||||
/* Determine prefix for string, if any. */
|
||||
switch (format) {
|
||||
@ -619,11 +793,27 @@ static int ecommunity_rt_soo_str(char *buf, size_t bufsz, const uint8_t *pnt,
|
||||
|
||||
len = snprintf(buf, bufsz, "%s%u:%u", prefix, eas.as, eas.val);
|
||||
} else if (type == ECOMMUNITY_ENCODE_AS) {
|
||||
eas.as = (*pnt++ << 8);
|
||||
eas.as |= (*pnt++);
|
||||
pnt = ptr_get_be32(pnt, &eas.val);
|
||||
if (ecom_size == ECOMMUNITY_SIZE) {
|
||||
eas.as = (*pnt++ << 8);
|
||||
eas.as |= (*pnt++);
|
||||
pnt = ptr_get_be32(pnt, &eas.val);
|
||||
|
||||
len = snprintf(buf, bufsz, "%s%u:%u", prefix, eas.as, eas.val);
|
||||
len = snprintf(buf, bufsz, "%s%u:%u", prefix, eas.as,
|
||||
eas.val);
|
||||
} else {
|
||||
/* this is an IPv6 ext community
|
||||
* first 16 bytes stands for IPv6 addres
|
||||
*/
|
||||
memcpy(&eip6.ip, pnt, 16);
|
||||
pnt += 16;
|
||||
eip6.val = (*pnt++ << 8);
|
||||
eip6.val |= (*pnt++);
|
||||
|
||||
inet_ntop(AF_INET6, &eip6.ip, buf_local,
|
||||
sizeof(buf_local));
|
||||
len = snprintf(buf, bufsz, "%s%s:%u", prefix,
|
||||
buf_local, eip6.val);
|
||||
}
|
||||
} else if (type == ECOMMUNITY_ENCODE_IP) {
|
||||
memcpy(&eip.ip, pnt, 4);
|
||||
pnt += 4;
|
||||
@ -640,6 +830,14 @@ static int ecommunity_rt_soo_str(char *buf, size_t bufsz, const uint8_t *pnt,
|
||||
return len;
|
||||
}
|
||||
|
||||
static int ecommunity_rt_soo_str(char *buf, size_t bufsz, const uint8_t *pnt,
|
||||
int type, int sub_type, int format)
|
||||
{
|
||||
return ecommunity_rt_soo_str_internal(buf, bufsz, pnt, type,
|
||||
sub_type, format,
|
||||
ECOMMUNITY_SIZE);
|
||||
}
|
||||
|
||||
static int ecommunity_lb_str(char *buf, size_t bufsz, const uint8_t *pnt)
|
||||
{
|
||||
int len = 0;
|
||||
@ -852,13 +1050,45 @@ char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter)
|
||||
type == ECOMMUNITY_EXTENDED_COMMUNITY_PART_2 ||
|
||||
type == ECOMMUNITY_EXTENDED_COMMUNITY_PART_3) {
|
||||
sub_type = *pnt++;
|
||||
if (sub_type == ECOMMUNITY_REDIRECT_VRF) {
|
||||
char buf[16] = {};
|
||||
ecommunity_rt_soo_str(
|
||||
buf, sizeof(buf), pnt,
|
||||
type & ~ECOMMUNITY_ENCODE_TRANS_EXP,
|
||||
ECOMMUNITY_ROUTE_TARGET,
|
||||
ECOMMUNITY_FORMAT_DISPLAY);
|
||||
|
||||
if (sub_type == ECOMMUNITY_ROUTE_TARGET) {
|
||||
char buf[ECOMMUNITY_STRLEN];
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
ecommunity_rt_soo_str_internal(buf, sizeof(buf),
|
||||
(const uint8_t *)pnt,
|
||||
type &
|
||||
~ECOMMUNITY_ENCODE_TRANS_EXP,
|
||||
ECOMMUNITY_ROUTE_TARGET,
|
||||
format,
|
||||
ecom->unit_size);
|
||||
snprintf(encbuf, sizeof(encbuf), "%s", buf);
|
||||
} else if (sub_type ==
|
||||
ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6) {
|
||||
char buf[64];
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
ecommunity_rt_soo_str_internal(buf, sizeof(buf),
|
||||
(const uint8_t *)pnt,
|
||||
type &
|
||||
~ECOMMUNITY_ENCODE_TRANS_EXP,
|
||||
ECOMMUNITY_ROUTE_TARGET,
|
||||
ECOMMUNITY_FORMAT_DISPLAY,
|
||||
ecom->unit_size);
|
||||
snprintf(encbuf, sizeof(encbuf),
|
||||
"FS:redirect VRF %s", buf);
|
||||
} else if (sub_type == ECOMMUNITY_REDIRECT_VRF) {
|
||||
char buf[16];
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
ecommunity_rt_soo_str(buf, sizeof(buf),
|
||||
(const uint8_t *)pnt,
|
||||
type &
|
||||
~ECOMMUNITY_ENCODE_TRANS_EXP,
|
||||
ECOMMUNITY_ROUTE_TARGET,
|
||||
ECOMMUNITY_FORMAT_DISPLAY);
|
||||
snprintf(encbuf, sizeof(encbuf),
|
||||
"FS:redirect VRF %s", buf);
|
||||
snprintf(encbuf, sizeof(encbuf),
|
||||
"FS:redirect VRF %s", buf);
|
||||
} else if (type != ECOMMUNITY_ENCODE_TRANS_EXP)
|
||||
@ -935,8 +1165,9 @@ bool ecommunity_match(const struct ecommunity *ecom1,
|
||||
|
||||
/* Every community on com2 needs to be on com1 for this to match */
|
||||
while (i < ecom1->size && j < ecom2->size) {
|
||||
if (memcmp(ecom1->val + i * ECOMMUNITY_SIZE,
|
||||
ecom2->val + j * ECOMMUNITY_SIZE, ECOMMUNITY_SIZE)
|
||||
if (memcmp(ecom1->val + i * ecom1->unit_size,
|
||||
ecom2->val + j * ecom2->unit_size,
|
||||
ecom2->unit_size)
|
||||
== 0)
|
||||
j++;
|
||||
i++;
|
||||
@ -957,7 +1188,7 @@ extern struct ecommunity_val *ecommunity_lookup(const struct ecommunity *ecom,
|
||||
|
||||
/* If the value already exists in the structure return 0. */
|
||||
c = 0;
|
||||
for (p = ecom->val; c < ecom->size; p += ECOMMUNITY_SIZE, c++) {
|
||||
for (p = ecom->val; c < ecom->size; p += ecom->unit_size, c++) {
|
||||
if (p == NULL) {
|
||||
continue;
|
||||
}
|
||||
@ -984,7 +1215,7 @@ bool ecommunity_strip(struct ecommunity *ecom, uint8_t type,
|
||||
* multiple times, handle that.
|
||||
*/
|
||||
c = 0;
|
||||
for (p = ecom->val; c < ecom->size; p += ECOMMUNITY_SIZE, c++) {
|
||||
for (p = ecom->val; c < ecom->size; p += ecom->unit_size, c++) {
|
||||
if (p[0] == type && p[1] == subtype)
|
||||
found++;
|
||||
}
|
||||
@ -1001,12 +1232,12 @@ bool ecommunity_strip(struct ecommunity *ecom, uint8_t type,
|
||||
|
||||
/* Strip matching ext community(ies). */
|
||||
new = XMALLOC(MTYPE_ECOMMUNITY_VAL,
|
||||
(ecom->size - found) * ECOMMUNITY_SIZE);
|
||||
(ecom->size - found) * ecom->unit_size);
|
||||
q = new;
|
||||
for (c = 0, p = ecom->val; c < ecom->size; c++, p += ECOMMUNITY_SIZE) {
|
||||
for (c = 0, p = ecom->val; c < ecom->size; c++, p += ecom->unit_size) {
|
||||
if (!(p[0] == type && p[1] == subtype)) {
|
||||
memcpy(q, p, ECOMMUNITY_SIZE);
|
||||
q += ECOMMUNITY_SIZE;
|
||||
memcpy(q, p, ecom->unit_size);
|
||||
q += ecom->unit_size;
|
||||
}
|
||||
}
|
||||
XFREE(MTYPE_ECOMMUNITY_VAL, ecom->val);
|
||||
@ -1028,8 +1259,8 @@ bool ecommunity_del_val(struct ecommunity *ecom, struct ecommunity_val *eval)
|
||||
if (ecom == NULL || ecom->val == NULL)
|
||||
return false;
|
||||
c = 0;
|
||||
for (p = ecom->val; c < ecom->size; p += ECOMMUNITY_SIZE, c++) {
|
||||
if (!memcmp(p, eval->val, ECOMMUNITY_SIZE)) {
|
||||
for (p = ecom->val; c < ecom->size; p += ecom->unit_size, c++) {
|
||||
if (!memcmp(p, eval->val, ecom->unit_size)) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
@ -1039,20 +1270,21 @@ bool ecommunity_del_val(struct ecommunity *ecom, struct ecommunity_val *eval)
|
||||
|
||||
/* Delete the selected value */
|
||||
ecom->size--;
|
||||
p = XMALLOC(MTYPE_ECOMMUNITY_VAL, ecom->size * ECOMMUNITY_SIZE);
|
||||
p = XMALLOC(MTYPE_ECOMMUNITY_VAL, ecom->size * ecom->unit_size);
|
||||
if (c != 0)
|
||||
memcpy(p, ecom->val, c * ECOMMUNITY_SIZE);
|
||||
memcpy(p, ecom->val, c * ecom->unit_size);
|
||||
if ((ecom->size - c) != 0)
|
||||
memcpy(p + (c)*ECOMMUNITY_SIZE,
|
||||
ecom->val + (c + 1) * ECOMMUNITY_SIZE,
|
||||
(ecom->size - c) * ECOMMUNITY_SIZE);
|
||||
memcpy(p + (c)*ecom->unit_size,
|
||||
ecom->val + (c + 1) * ecom->unit_size,
|
||||
(ecom->size - c) * ecom->unit_size);
|
||||
XFREE(MTYPE_ECOMMUNITY_VAL, ecom->val);
|
||||
ecom->val = p;
|
||||
return true;
|
||||
}
|
||||
|
||||
int ecommunity_fill_pbr_action(struct ecommunity_val *ecom_eval,
|
||||
struct bgp_pbr_entry_action *api)
|
||||
struct bgp_pbr_entry_action *api,
|
||||
afi_t afi)
|
||||
{
|
||||
if (ecom_eval->val[1] == ECOMMUNITY_TRAFFIC_RATE) {
|
||||
api->action = ACTION_TRAFFICRATE;
|
||||
@ -1076,7 +1308,8 @@ int ecommunity_fill_pbr_action(struct ecommunity_val *ecom_eval,
|
||||
} 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) {
|
||||
} else if (ecom_eval->val[1] == ECOMMUNITY_REDIRECT_IP_NH &&
|
||||
afi == AFI_IP) {
|
||||
/* see draft-ietf-idr-flowspec-redirect-ip-02
|
||||
* Q1: how come a ext. community can host ipv6 address
|
||||
* Q2 : from cisco documentation:
|
||||
|
@ -63,6 +63,10 @@
|
||||
* 0x0c Flow-spec Redirect to IPv4 - draft-ietf-idr-flowspec-redirect
|
||||
*/
|
||||
#define ECOMMUNITY_FLOWSPEC_REDIRECT_IPV4 0x0c
|
||||
/* from draft-ietf-idr-flow-spec-v6-09
|
||||
* 0x0b Flow-spec Redirect to IPv6
|
||||
*/
|
||||
#define ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6 0x0b
|
||||
|
||||
/* Low-order octet of the Extended Communities type field for EVPN types */
|
||||
#define ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY 0x00
|
||||
@ -90,6 +94,7 @@
|
||||
|
||||
/* Extended Communities value is eight octet long. */
|
||||
#define ECOMMUNITY_SIZE 8
|
||||
#define IPV6_ECOMMUNITY_SIZE 20
|
||||
|
||||
/* Extended Communities type flag. */
|
||||
#define ECOMMUNITY_FLAG_NON_TRANSITIVE 0x40
|
||||
@ -99,6 +104,11 @@ struct ecommunity {
|
||||
/* Reference counter. */
|
||||
unsigned long refcnt;
|
||||
|
||||
/* Size of Each Unit of Extended Communities attribute.
|
||||
* to differentiate between IPv6 ext comm and ext comm
|
||||
*/
|
||||
uint8_t unit_size;
|
||||
|
||||
/* Size of Extended Communities attribute. */
|
||||
int size;
|
||||
|
||||
@ -119,11 +129,23 @@ struct ecommunity_ip {
|
||||
uint16_t val;
|
||||
};
|
||||
|
||||
struct ecommunity_ip6 {
|
||||
struct in6_addr ip;
|
||||
uint16_t val;
|
||||
};
|
||||
|
||||
/* Extended community value is eight octet. */
|
||||
struct ecommunity_val {
|
||||
char val[ECOMMUNITY_SIZE];
|
||||
};
|
||||
|
||||
/* IPv6 Extended community value is eight octet. */
|
||||
struct ecommunity_val_ipv6 {
|
||||
char val[IPV6_ECOMMUNITY_SIZE];
|
||||
};
|
||||
|
||||
#define ecom_length_size(X, Y) ((X)->size * (Y))
|
||||
|
||||
/*
|
||||
* Encode BGP Route Target AS:nn.
|
||||
*/
|
||||
@ -193,6 +215,8 @@ extern void ecommunity_init(void);
|
||||
extern void ecommunity_finish(void);
|
||||
extern void ecommunity_free(struct ecommunity **);
|
||||
extern struct ecommunity *ecommunity_parse(uint8_t *, unsigned short);
|
||||
extern struct ecommunity *ecommunity_parse_ipv6(uint8_t *pnt,
|
||||
unsigned short length);
|
||||
extern struct ecommunity *ecommunity_dup(struct ecommunity *);
|
||||
extern struct ecommunity *ecommunity_merge(struct ecommunity *,
|
||||
struct ecommunity *);
|
||||
@ -202,6 +226,8 @@ extern bool ecommunity_cmp(const void *arg1, const void *arg2);
|
||||
extern void ecommunity_unintern(struct ecommunity **);
|
||||
extern unsigned int ecommunity_hash_make(const void *);
|
||||
extern struct ecommunity *ecommunity_str2com(const char *, int, int);
|
||||
extern struct ecommunity *ecommunity_str2com_ipv6(const char *str, int type,
|
||||
int keyword_included);
|
||||
extern char *ecommunity_ecom2str(struct ecommunity *, int, int);
|
||||
extern void ecommunity_strfree(char **s);
|
||||
extern bool ecommunity_match(const struct ecommunity *,
|
||||
@ -209,9 +235,13 @@ extern bool ecommunity_match(const struct ecommunity *,
|
||||
extern char *ecommunity_str(struct ecommunity *);
|
||||
extern struct ecommunity_val *ecommunity_lookup(const struct ecommunity *,
|
||||
uint8_t, uint8_t);
|
||||
|
||||
extern bool ecommunity_add_val(struct ecommunity *ecom,
|
||||
struct ecommunity_val *eval,
|
||||
bool unique, bool overwrite);
|
||||
extern bool ecommunity_add_val_ipv6(struct ecommunity *ecom,
|
||||
struct ecommunity_val_ipv6 *eval,
|
||||
bool unique, bool overwrite);
|
||||
|
||||
/* for vpn */
|
||||
extern struct ecommunity *ecommunity_new(void);
|
||||
@ -222,7 +252,8 @@ extern bool 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);
|
||||
struct bgp_pbr_entry_action *api,
|
||||
afi_t afi);
|
||||
|
||||
extern void bgp_compute_aggregate_ecommunity(
|
||||
struct bgp_aggregate *aggregate,
|
||||
|
@ -726,6 +726,7 @@ static void build_evpn_type5_route_extcomm(struct bgp *bgp_vrf,
|
||||
memset(&ecom_encap, 0, sizeof(ecom_encap));
|
||||
encode_encap_extcomm(tnl_type, &eval);
|
||||
ecom_encap.size = 1;
|
||||
ecom_encap.unit_size = ECOMMUNITY_SIZE;
|
||||
ecom_encap.val = (uint8_t *)eval.val;
|
||||
|
||||
/* Add Encap */
|
||||
@ -788,6 +789,7 @@ static void build_evpn_route_extcomm(struct bgpevpn *vpn, struct attr *attr,
|
||||
memset(&ecom_encap, 0, sizeof(ecom_encap));
|
||||
encode_encap_extcomm(tnl_type, &eval);
|
||||
ecom_encap.size = 1;
|
||||
ecom_encap.unit_size = ECOMMUNITY_SIZE;
|
||||
ecom_encap.val = (uint8_t *)eval.val;
|
||||
|
||||
/* Add Encap */
|
||||
@ -816,6 +818,7 @@ static void build_evpn_route_extcomm(struct bgpevpn *vpn, struct attr *attr,
|
||||
memset(&ecom_sticky, 0, sizeof(ecom_sticky));
|
||||
encode_mac_mobility_extcomm(1, seqnum, &eval_sticky);
|
||||
ecom_sticky.size = 1;
|
||||
ecom_sticky.unit_size = ECOMMUNITY_SIZE;
|
||||
ecom_sticky.val = (uint8_t *)eval_sticky.val;
|
||||
attr->ecommunity =
|
||||
ecommunity_merge(attr->ecommunity, &ecom_sticky);
|
||||
@ -832,6 +835,7 @@ static void build_evpn_route_extcomm(struct bgpevpn *vpn, struct attr *attr,
|
||||
memset(&ecom_default_gw, 0, sizeof(ecom_default_gw));
|
||||
encode_default_gw_extcomm(&eval_default_gw);
|
||||
ecom_default_gw.size = 1;
|
||||
ecom_default_gw.unit_size = ECOMMUNITY_SIZE;
|
||||
ecom_default_gw.val = (uint8_t *)eval_default_gw.val;
|
||||
attr->ecommunity =
|
||||
ecommunity_merge(attr->ecommunity, &ecom_default_gw);
|
||||
@ -842,6 +846,7 @@ static void build_evpn_route_extcomm(struct bgpevpn *vpn, struct attr *attr,
|
||||
memset(&ecom_na, 0, sizeof(ecom_na));
|
||||
encode_na_flag_extcomm(&eval_na, attr->router_flag, proxy);
|
||||
ecom_na.size = 1;
|
||||
ecom_na.unit_size = ECOMMUNITY_SIZE;
|
||||
ecom_na.val = (uint8_t *)eval_na.val;
|
||||
attr->ecommunity = ecommunity_merge(attr->ecommunity,
|
||||
&ecom_na);
|
||||
@ -871,7 +876,8 @@ static void add_mac_mobility_to_attr(uint32_t seq_num, struct attr *attr)
|
||||
|
||||
if (attr->ecommunity) {
|
||||
for (i = 0; i < attr->ecommunity->size; i++) {
|
||||
pnt = attr->ecommunity->val + (i * 8);
|
||||
pnt = attr->ecommunity->val +
|
||||
(i * attr->ecommunity->unit_size);
|
||||
type = *pnt++;
|
||||
sub_type = *pnt++;
|
||||
|
||||
@ -879,7 +885,8 @@ static void add_mac_mobility_to_attr(uint32_t seq_num, struct attr *attr)
|
||||
&& sub_type
|
||||
== ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY) {
|
||||
ecom_val_ptr =
|
||||
(attr->ecommunity->val + (i * 8));
|
||||
(attr->ecommunity->val +
|
||||
(i * attr->ecommunity->unit_size));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -887,12 +894,14 @@ static void add_mac_mobility_to_attr(uint32_t seq_num, struct attr *attr)
|
||||
|
||||
/* Update the existing MM ecommunity */
|
||||
if (ecom_val_ptr) {
|
||||
memcpy(ecom_val_ptr, eval.val, sizeof(char) * ECOMMUNITY_SIZE);
|
||||
memcpy(ecom_val_ptr, eval.val, sizeof(char)
|
||||
* attr->ecommunity->unit_size);
|
||||
}
|
||||
/* Add MM to existing */
|
||||
else {
|
||||
memset(&ecom_tmp, 0, sizeof(ecom_tmp));
|
||||
ecom_tmp.size = 1;
|
||||
ecom_tmp.unit_size = ECOMMUNITY_SIZE;
|
||||
ecom_tmp.val = (uint8_t *)eval.val;
|
||||
|
||||
if (attr->ecommunity)
|
||||
@ -2713,9 +2722,9 @@ static int is_route_matching_for_vrf(struct bgp *bgp_vrf,
|
||||
struct vrf_irt_node *irt;
|
||||
|
||||
/* Only deal with RTs */
|
||||
pnt = (ecom->val + (i * ECOMMUNITY_SIZE));
|
||||
pnt = (ecom->val + (i * ecom->unit_size));
|
||||
eval = (struct ecommunity_val *)(ecom->val
|
||||
+ (i * ECOMMUNITY_SIZE));
|
||||
+ (i * ecom->unit_size));
|
||||
type = *pnt++;
|
||||
sub_type = *pnt++;
|
||||
if (sub_type != ECOMMUNITY_ROUTE_TARGET)
|
||||
@ -2737,7 +2746,7 @@ static int is_route_matching_for_vrf(struct bgp *bgp_vrf,
|
||||
if (type == ECOMMUNITY_ENCODE_AS
|
||||
|| type == ECOMMUNITY_ENCODE_AS4
|
||||
|| type == ECOMMUNITY_ENCODE_IP) {
|
||||
memcpy(&eval_tmp, eval, ECOMMUNITY_SIZE);
|
||||
memcpy(&eval_tmp, eval, ecom->unit_size);
|
||||
mask_ecom_global_admin(&eval_tmp, eval);
|
||||
irt = lookup_vrf_import_rt(&eval_tmp);
|
||||
}
|
||||
@ -2780,9 +2789,9 @@ static int is_route_matching_for_vni(struct bgp *bgp, struct bgpevpn *vpn,
|
||||
struct irt_node *irt;
|
||||
|
||||
/* Only deal with RTs */
|
||||
pnt = (ecom->val + (i * ECOMMUNITY_SIZE));
|
||||
pnt = (ecom->val + (i * ecom->unit_size));
|
||||
eval = (struct ecommunity_val *)(ecom->val
|
||||
+ (i * ECOMMUNITY_SIZE));
|
||||
+ (i * ecom->unit_size));
|
||||
type = *pnt++;
|
||||
sub_type = *pnt++;
|
||||
if (sub_type != ECOMMUNITY_ROUTE_TARGET)
|
||||
@ -2804,7 +2813,7 @@ static int is_route_matching_for_vni(struct bgp *bgp, struct bgpevpn *vpn,
|
||||
if (type == ECOMMUNITY_ENCODE_AS
|
||||
|| type == ECOMMUNITY_ENCODE_AS4
|
||||
|| type == ECOMMUNITY_ENCODE_IP) {
|
||||
memcpy(&eval_tmp, eval, ECOMMUNITY_SIZE);
|
||||
memcpy(&eval_tmp, eval, ecom->unit_size);
|
||||
mask_ecom_global_admin(&eval_tmp, eval);
|
||||
irt = lookup_import_rt(bgp, &eval_tmp);
|
||||
}
|
||||
@ -3220,9 +3229,9 @@ static int install_uninstall_evpn_route(struct bgp *bgp, afi_t afi, safi_t safi,
|
||||
struct bgp_evpn_es *es;
|
||||
|
||||
/* Only deal with RTs */
|
||||
pnt = (ecom->val + (i * ECOMMUNITY_SIZE));
|
||||
pnt = (ecom->val + (i * ecom->unit_size));
|
||||
eval = (struct ecommunity_val *)(ecom->val
|
||||
+ (i * ECOMMUNITY_SIZE));
|
||||
+ (i * ecom->unit_size));
|
||||
type = *pnt++;
|
||||
sub_type = *pnt++;
|
||||
if (sub_type != ECOMMUNITY_ROUTE_TARGET)
|
||||
@ -3261,7 +3270,7 @@ static int install_uninstall_evpn_route(struct bgp *bgp, afi_t afi, safi_t safi,
|
||||
if (type == ECOMMUNITY_ENCODE_AS
|
||||
|| type == ECOMMUNITY_ENCODE_AS4
|
||||
|| type == ECOMMUNITY_ENCODE_IP) {
|
||||
memcpy(&eval_tmp, eval, ECOMMUNITY_SIZE);
|
||||
memcpy(&eval_tmp, eval, ecom->unit_size);
|
||||
mask_ecom_global_admin(&eval_tmp, eval);
|
||||
irt = lookup_import_rt(bgp, &eval_tmp);
|
||||
vrf_irt = lookup_vrf_import_rt(&eval_tmp);
|
||||
|
@ -523,6 +523,7 @@ static void bgp_evpn_type4_route_extcomm_build(struct bgp_evpn_es *es,
|
||||
memset(&ecom_encap, 0, sizeof(ecom_encap));
|
||||
encode_encap_extcomm(tnl_type, &eval);
|
||||
ecom_encap.size = 1;
|
||||
ecom_encap.unit_size = ECOMMUNITY_SIZE;
|
||||
ecom_encap.val = (uint8_t *)eval.val;
|
||||
attr->ecommunity = ecommunity_dup(&ecom_encap);
|
||||
|
||||
@ -532,6 +533,7 @@ static void bgp_evpn_type4_route_extcomm_build(struct bgp_evpn_es *es,
|
||||
es_get_system_mac(&es->esi, &mac);
|
||||
encode_es_rt_extcomm(&eval_es_rt, &mac);
|
||||
ecom_es_rt.size = 1;
|
||||
ecom_es_rt.unit_size = ECOMMUNITY_SIZE;
|
||||
ecom_es_rt.val = (uint8_t *)eval_es_rt.val;
|
||||
attr->ecommunity =
|
||||
ecommunity_merge(attr->ecommunity, &ecom_es_rt);
|
||||
@ -775,6 +777,7 @@ static void bgp_evpn_type1_es_route_extcomm_build(struct bgp_evpn_es *es,
|
||||
memset(&ecom_encap, 0, sizeof(ecom_encap));
|
||||
encode_encap_extcomm(tnl_type, &eval);
|
||||
ecom_encap.size = 1;
|
||||
ecom_encap.unit_size = ECOMMUNITY_SIZE;
|
||||
ecom_encap.val = (uint8_t *)eval.val;
|
||||
attr->ecommunity = ecommunity_dup(&ecom_encap);
|
||||
|
||||
@ -782,6 +785,7 @@ static void bgp_evpn_type1_es_route_extcomm_build(struct bgp_evpn_es *es,
|
||||
encode_esi_label_extcomm(&eval_esi_label,
|
||||
false /*single_active*/);
|
||||
ecom_esi_label.size = 1;
|
||||
ecom_esi_label.unit_size = ECOMMUNITY_SIZE;
|
||||
ecom_esi_label.val = (uint8_t *)eval_esi_label.val;
|
||||
attr->ecommunity =
|
||||
ecommunity_merge(attr->ecommunity, &ecom_esi_label);
|
||||
@ -818,6 +822,7 @@ static void bgp_evpn_type1_evi_route_extcomm_build(struct bgp_evpn_es *es,
|
||||
memset(&ecom_encap, 0, sizeof(ecom_encap));
|
||||
encode_encap_extcomm(tnl_type, &eval);
|
||||
ecom_encap.size = 1;
|
||||
ecom_encap.unit_size = ECOMMUNITY_SIZE;
|
||||
ecom_encap.val = (uint8_t *)eval.val;
|
||||
attr->ecommunity = ecommunity_dup(&ecom_encap);
|
||||
|
||||
|
@ -33,7 +33,8 @@
|
||||
#include "bgpd/bgp_debug.h"
|
||||
#include "bgpd/bgp_errors.h"
|
||||
|
||||
static int bgp_fs_nlri_validate(uint8_t *nlri_content, uint32_t len)
|
||||
static int bgp_fs_nlri_validate(uint8_t *nlri_content, uint32_t len,
|
||||
afi_t afi)
|
||||
{
|
||||
uint32_t offset = 0;
|
||||
int type;
|
||||
@ -48,7 +49,15 @@ static int bgp_fs_nlri_validate(uint8_t *nlri_content, uint32_t len)
|
||||
ret = bgp_flowspec_ip_address(
|
||||
BGP_FLOWSPEC_VALIDATE_ONLY,
|
||||
nlri_content + offset,
|
||||
len - offset, NULL, &error);
|
||||
len - offset, NULL, &error,
|
||||
afi, NULL);
|
||||
break;
|
||||
case FLOWSPEC_FLOW_LABEL:
|
||||
if (afi == AFI_IP)
|
||||
return -1;
|
||||
ret = bgp_flowspec_op_decode(BGP_FLOWSPEC_VALIDATE_ONLY,
|
||||
nlri_content + offset,
|
||||
len - offset, NULL, &error);
|
||||
break;
|
||||
case FLOWSPEC_IP_PROTOCOL:
|
||||
case FLOWSPEC_PORT:
|
||||
@ -103,11 +112,6 @@ int bgp_nlri_parse_flowspec(struct peer *peer, struct attr *attr,
|
||||
afi = packet->afi;
|
||||
safi = packet->safi;
|
||||
|
||||
if (afi == AFI_IP6) {
|
||||
flog_err(EC_LIB_DEVELOPMENT, "BGP flowspec IPv6 not supported");
|
||||
return BGP_NLRI_PARSE_ERROR_FLOWSPEC_IPV6_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
if (packet->length >= FLOWSPEC_NLRI_SIZELIMIT_EXTENDED) {
|
||||
flog_err(EC_BGP_FLOWSPEC_PACKET,
|
||||
"BGP flowspec nlri length maximum reached (%u)",
|
||||
@ -137,7 +141,7 @@ int bgp_nlri_parse_flowspec(struct peer *peer, struct attr *attr,
|
||||
psize);
|
||||
return BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW;
|
||||
}
|
||||
if (bgp_fs_nlri_validate(pnt, psize) < 0) {
|
||||
if (bgp_fs_nlri_validate(pnt, psize, afi) < 0) {
|
||||
flog_err(
|
||||
EC_BGP_FLOWSPEC_PACKET,
|
||||
"Bad flowspec format or NLRI options not supported");
|
||||
@ -147,6 +151,7 @@ int bgp_nlri_parse_flowspec(struct peer *peer, struct attr *attr,
|
||||
p.prefixlen = 0;
|
||||
/* Flowspec encoding is in bytes */
|
||||
p.u.prefix_flowspec.prefixlen = psize;
|
||||
p.u.prefix_flowspec.family = afi2family(afi);
|
||||
temp = XCALLOC(MTYPE_TMP, psize);
|
||||
memcpy(temp, pnt, psize);
|
||||
p.u.prefix_flowspec.ptr = (uintptr_t) temp;
|
||||
@ -161,7 +166,8 @@ int bgp_nlri_parse_flowspec(struct peer *peer, struct attr *attr,
|
||||
p.u.prefix_flowspec.ptr,
|
||||
p.u.prefix_flowspec.prefixlen,
|
||||
return_string,
|
||||
NLRI_STRING_FORMAT_MIN, NULL);
|
||||
NLRI_STRING_FORMAT_MIN, NULL,
|
||||
afi);
|
||||
snprintf(ec_string, sizeof(ec_string),
|
||||
"EC{none}");
|
||||
if (attr && attr->ecommunity) {
|
||||
|
@ -41,7 +41,8 @@ extern int bgp_show_table_flowspec(struct vty *vty, struct bgp *bgp, afi_t afi,
|
||||
|
||||
extern void bgp_fs_nlri_get_string(unsigned char *nlri_content, size_t len,
|
||||
char *return_string, int format,
|
||||
json_object *json_path);
|
||||
json_object *json_path,
|
||||
afi_t afi);
|
||||
|
||||
extern void route_vty_out_flowspec(struct vty *vty, const struct prefix *p,
|
||||
struct bgp_path_info *path, int display,
|
||||
|
@ -41,5 +41,6 @@
|
||||
#define FLOWSPEC_PKT_LEN 10
|
||||
#define FLOWSPEC_DSCP 11
|
||||
#define FLOWSPEC_FRAGMENT 12
|
||||
#define FLOWSPEC_FLOW_LABEL 13 /* For IPv6 only */
|
||||
|
||||
#endif /* _FRR_BGP_FLOWSPEC_PRIVATE_H */
|
||||
|
@ -76,6 +76,7 @@ static int bgp_flowspec_call_non_opaque_decode(uint8_t *nlri_content, int len,
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
bool bgp_flowspec_contains_prefix(const struct prefix *pfs,
|
||||
struct prefix *input, int prefix_check)
|
||||
{
|
||||
@ -84,6 +85,7 @@ bool bgp_flowspec_contains_prefix(const struct prefix *pfs,
|
||||
int ret = 0, error = 0;
|
||||
uint8_t *nlri_content = (uint8_t *)pfs->u.prefix_flowspec.ptr;
|
||||
size_t len = pfs->u.prefix_flowspec.prefixlen;
|
||||
afi_t afi = family2afi(pfs->u.prefix_flowspec.family);
|
||||
struct prefix compare;
|
||||
|
||||
error = 0;
|
||||
@ -98,7 +100,8 @@ bool bgp_flowspec_contains_prefix(const struct prefix *pfs,
|
||||
BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE,
|
||||
nlri_content+offset,
|
||||
len - offset,
|
||||
&compare, &error);
|
||||
&compare, &error,
|
||||
afi, NULL);
|
||||
if (ret <= 0)
|
||||
break;
|
||||
if (prefix_check &&
|
||||
@ -115,6 +118,16 @@ bool bgp_flowspec_contains_prefix(const struct prefix *pfs,
|
||||
&compare.u.prefix6.s6_addr))
|
||||
return true;
|
||||
break;
|
||||
case FLOWSPEC_FLOW_LABEL:
|
||||
if (afi == AFI_IP) {
|
||||
error = -1;
|
||||
continue;
|
||||
}
|
||||
ret = bgp_flowspec_op_decode(BGP_FLOWSPEC_VALIDATE_ONLY,
|
||||
nlri_content+offset,
|
||||
len - offset,
|
||||
NULL, &error);
|
||||
break;
|
||||
case FLOWSPEC_IP_PROTOCOL:
|
||||
case FLOWSPEC_PORT:
|
||||
case FLOWSPEC_DEST_PORT:
|
||||
@ -158,13 +171,16 @@ bool bgp_flowspec_contains_prefix(const struct prefix *pfs,
|
||||
int bgp_flowspec_ip_address(enum bgp_flowspec_util_nlri_t type,
|
||||
uint8_t *nlri_ptr,
|
||||
uint32_t max_len,
|
||||
void *result, int *error)
|
||||
void *result, int *error,
|
||||
afi_t afi,
|
||||
uint8_t *ipv6_offset)
|
||||
{
|
||||
char *display = (char *)result; /* for return_string */
|
||||
struct prefix *prefix = (struct prefix *)result;
|
||||
uint32_t offset = 0;
|
||||
struct prefix prefix_local;
|
||||
int psize;
|
||||
uint8_t prefix_offset = 0;
|
||||
|
||||
*error = 0;
|
||||
memset(&prefix_local, 0, sizeof(struct prefix));
|
||||
@ -172,8 +188,13 @@ int bgp_flowspec_ip_address(enum bgp_flowspec_util_nlri_t type,
|
||||
prefix_local.prefixlen = nlri_ptr[offset];
|
||||
psize = PSIZE(prefix_local.prefixlen);
|
||||
offset++;
|
||||
/* TODO Flowspec IPv6 Support */
|
||||
prefix_local.family = AF_INET;
|
||||
prefix_local.family = afi2family(afi);
|
||||
if (prefix_local.family == AF_INET6) {
|
||||
prefix_offset = nlri_ptr[offset];
|
||||
if (ipv6_offset)
|
||||
*ipv6_offset = prefix_offset;
|
||||
offset++;
|
||||
}
|
||||
/* Prefix length check. */
|
||||
if (prefix_local.prefixlen > prefix_blen(&prefix_local) * 8)
|
||||
*error = -1;
|
||||
@ -189,11 +210,30 @@ int bgp_flowspec_ip_address(enum bgp_flowspec_util_nlri_t type,
|
||||
offset += psize;
|
||||
switch (type) {
|
||||
case BGP_FLOWSPEC_RETURN_STRING:
|
||||
prefix2str(&prefix_local, display,
|
||||
BGP_FLOWSPEC_STRING_DISPLAY_MAX);
|
||||
if (prefix_local.family == AF_INET6) {
|
||||
char str[BGP_FLOWSPEC_STRING_DISPLAY_MAX];
|
||||
int ret;
|
||||
|
||||
prefix2str(&prefix_local, str,
|
||||
BGP_FLOWSPEC_STRING_DISPLAY_MAX);
|
||||
ret = snprintf(display, BGP_FLOWSPEC_STRING_DISPLAY_MAX,
|
||||
"%s/off %u",
|
||||
str, prefix_offset);
|
||||
if (ret < 0) {
|
||||
*error = -1;
|
||||
break;
|
||||
}
|
||||
} else
|
||||
prefix2str(&prefix_local, display,
|
||||
BGP_FLOWSPEC_STRING_DISPLAY_MAX);
|
||||
break;
|
||||
case BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE:
|
||||
PREFIX_COPY_IPV4(prefix, &prefix_local)
|
||||
if (prefix) {
|
||||
if (prefix_local.family == AF_INET)
|
||||
PREFIX_COPY_IPV4(prefix, &prefix_local)
|
||||
else
|
||||
PREFIX_COPY_IPV6(prefix, &prefix_local)
|
||||
}
|
||||
break;
|
||||
case BGP_FLOWSPEC_VALIDATE_ONLY:
|
||||
default:
|
||||
@ -417,7 +457,8 @@ int bgp_flowspec_bitmask_decode(enum bgp_flowspec_util_nlri_t type,
|
||||
}
|
||||
|
||||
int bgp_flowspec_match_rules_fill(uint8_t *nlri_content, int len,
|
||||
struct bgp_pbr_entry_main *bpem)
|
||||
struct bgp_pbr_entry_main *bpem,
|
||||
afi_t afi)
|
||||
{
|
||||
int offset = 0, error = 0;
|
||||
struct prefix *prefix;
|
||||
@ -425,6 +466,7 @@ int bgp_flowspec_match_rules_fill(uint8_t *nlri_content, int len,
|
||||
uint8_t *match_num;
|
||||
uint8_t bitmask = 0;
|
||||
int ret = 0, type;
|
||||
uint8_t *prefix_offset;
|
||||
|
||||
while (offset < len - 1 && error >= 0) {
|
||||
type = nlri_content[offset];
|
||||
@ -436,15 +478,18 @@ int bgp_flowspec_match_rules_fill(uint8_t *nlri_content, int len,
|
||||
if (type == FLOWSPEC_DEST_PREFIX) {
|
||||
bitmask |= PREFIX_DST_PRESENT;
|
||||
prefix = &bpem->dst_prefix;
|
||||
prefix_offset = &bpem->dst_prefix_offset;
|
||||
} else {
|
||||
bitmask |= PREFIX_SRC_PRESENT;
|
||||
prefix = &bpem->src_prefix;
|
||||
prefix_offset = &bpem->src_prefix_offset;
|
||||
}
|
||||
ret = bgp_flowspec_ip_address(
|
||||
BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE,
|
||||
nlri_content + offset,
|
||||
len - offset,
|
||||
prefix, &error);
|
||||
prefix, &error,
|
||||
afi, prefix_offset);
|
||||
if (error < 0)
|
||||
flog_err(EC_BGP_FLOWSPEC_PACKET,
|
||||
"%s: flowspec_ip_address error %d",
|
||||
@ -456,11 +501,30 @@ int bgp_flowspec_match_rules_fill(uint8_t *nlri_content, int len,
|
||||
if (prefix->family == AF_INET
|
||||
&& prefix->u.prefix4.s_addr == INADDR_ANY)
|
||||
bpem->match_bitmask_iprule |= bitmask;
|
||||
else if (prefix->family == AF_INET6
|
||||
&& !memcmp(&prefix->u.prefix6,
|
||||
&in6addr_any,
|
||||
sizeof(struct in6_addr)))
|
||||
bpem->match_bitmask_iprule |= bitmask;
|
||||
else
|
||||
bpem->match_bitmask |= bitmask;
|
||||
}
|
||||
offset += ret;
|
||||
break;
|
||||
case FLOWSPEC_FLOW_LABEL:
|
||||
if (afi == AFI_IP) {
|
||||
error = -1;
|
||||
continue;
|
||||
}
|
||||
match_num = &(bpem->match_flowlabel_num);
|
||||
mval = (struct bgp_pbr_match_val *)
|
||||
&(bpem->flow_label);
|
||||
offset += bgp_flowspec_call_non_opaque_decode(
|
||||
nlri_content + offset,
|
||||
len - offset,
|
||||
mval, match_num,
|
||||
&error);
|
||||
break;
|
||||
case FLOWSPEC_IP_PROTOCOL:
|
||||
match_num = &(bpem->match_protocol_num);
|
||||
mval = (struct bgp_pbr_match_val *)
|
||||
@ -583,7 +647,8 @@ int bgp_flowspec_match_rules_fill(uint8_t *nlri_content, int len,
|
||||
bpem->match_packet_length_num || bpem->match_icmp_code_num ||
|
||||
bpem->match_icmp_type_num || bpem->match_port_num ||
|
||||
bpem->match_src_port_num || bpem->match_dst_port_num ||
|
||||
bpem->match_protocol_num || bpem->match_bitmask)
|
||||
bpem->match_protocol_num || bpem->match_bitmask ||
|
||||
bpem->match_flowlabel_num)
|
||||
bpem->type = BGP_PBR_IPSET;
|
||||
else if ((bpem->match_bitmask_iprule & PREFIX_SRC_PRESENT) ||
|
||||
(bpem->match_bitmask_iprule & PREFIX_DST_PRESENT))
|
||||
@ -599,7 +664,7 @@ int bgp_flowspec_match_rules_fill(uint8_t *nlri_content, int len,
|
||||
|
||||
/* return 1 if FS entry invalid or no NH IP */
|
||||
bool bgp_flowspec_get_first_nh(struct bgp *bgp, struct bgp_path_info *pi,
|
||||
struct prefix *p)
|
||||
struct prefix *p, afi_t afi)
|
||||
{
|
||||
struct bgp_pbr_entry_main api;
|
||||
int i;
|
||||
@ -615,9 +680,15 @@ bool bgp_flowspec_get_first_nh(struct bgp *bgp, struct bgp_path_info *pi,
|
||||
api_action = &api.actions[i];
|
||||
if (api_action->action != ACTION_REDIRECT_IP)
|
||||
continue;
|
||||
p->family = AF_INET;
|
||||
p->prefixlen = IPV4_MAX_BITLEN;
|
||||
p->u.prefix4 = api_action->u.zr.redirect_ip_v4;
|
||||
p->family = afi2family(afi);
|
||||
if (afi == AFI_IP) {
|
||||
p->prefixlen = IPV4_MAX_BITLEN;
|
||||
p->u.prefix4 = api_action->u.zr.redirect_ip_v4;
|
||||
} else {
|
||||
p->prefixlen = IPV6_MAX_BITLEN;
|
||||
memcpy(&p->u.prefix6, &api_action->u.zr.redirect_ip_v6,
|
||||
sizeof(struct in6_addr));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -39,7 +39,8 @@ extern int bgp_flowspec_op_decode(enum bgp_flowspec_util_nlri_t type,
|
||||
extern int bgp_flowspec_ip_address(enum bgp_flowspec_util_nlri_t type,
|
||||
uint8_t *nlri_ptr,
|
||||
uint32_t max_len,
|
||||
void *result, int *error);
|
||||
void *result, int *error,
|
||||
afi_t afi, uint8_t *ipv6_offset);
|
||||
|
||||
extern int bgp_flowspec_bitmask_decode(enum bgp_flowspec_util_nlri_t type,
|
||||
uint8_t *nlri_ptr,
|
||||
@ -48,13 +49,14 @@ extern int bgp_flowspec_bitmask_decode(enum bgp_flowspec_util_nlri_t type,
|
||||
|
||||
struct bgp_pbr_entry_main;
|
||||
extern int bgp_flowspec_match_rules_fill(uint8_t *nlri_content, int len,
|
||||
struct bgp_pbr_entry_main *bpem);
|
||||
struct bgp_pbr_entry_main *bpem,
|
||||
afi_t afi);
|
||||
|
||||
extern bool bgp_flowspec_contains_prefix(const struct prefix *pfs,
|
||||
struct prefix *input,
|
||||
int prefix_check);
|
||||
|
||||
extern bool bgp_flowspec_get_first_nh(struct bgp *bgp, struct bgp_path_info *pi,
|
||||
struct prefix *nh);
|
||||
struct prefix *nh, afi_t afi);
|
||||
|
||||
#endif /* _FRR_BGP_FLOWSPEC_UTIL_H */
|
||||
|
@ -50,6 +50,7 @@ static const struct message bgp_flowspec_display_large[] = {
|
||||
{FLOWSPEC_PKT_LEN, "Packet Length"},
|
||||
{FLOWSPEC_DSCP, "DSCP field"},
|
||||
{FLOWSPEC_FRAGMENT, "Packet Fragment"},
|
||||
{FLOWSPEC_FLOW_LABEL, "Packet Flow Label"},
|
||||
{0}
|
||||
};
|
||||
|
||||
@ -66,6 +67,7 @@ static const struct message bgp_flowspec_display_min[] = {
|
||||
{FLOWSPEC_PKT_LEN, "pktlen"},
|
||||
{FLOWSPEC_DSCP, "dscp"},
|
||||
{FLOWSPEC_FRAGMENT, "pktfrag"},
|
||||
{FLOWSPEC_FLOW_LABEL, "flwlbl"},
|
||||
{0}
|
||||
};
|
||||
|
||||
@ -93,7 +95,8 @@ static const struct message bgp_flowspec_display_min[] = {
|
||||
*/
|
||||
void bgp_fs_nlri_get_string(unsigned char *nlri_content, size_t len,
|
||||
char *return_string, int format,
|
||||
json_object *json_path)
|
||||
json_object *json_path,
|
||||
afi_t afi)
|
||||
{
|
||||
uint32_t offset = 0;
|
||||
int type;
|
||||
@ -127,7 +130,8 @@ void bgp_fs_nlri_get_string(unsigned char *nlri_content, size_t len,
|
||||
type_util,
|
||||
nlri_content+offset,
|
||||
len - offset,
|
||||
local_string, &error);
|
||||
local_string, &error,
|
||||
afi, NULL);
|
||||
if (ret <= 0)
|
||||
break;
|
||||
if (json_path) {
|
||||
@ -145,6 +149,7 @@ void bgp_fs_nlri_get_string(unsigned char *nlri_content, size_t len,
|
||||
len_string -= len_written;
|
||||
ptr += len_written;
|
||||
break;
|
||||
case FLOWSPEC_FLOW_LABEL:
|
||||
case FLOWSPEC_IP_PROTOCOL:
|
||||
case FLOWSPEC_PORT:
|
||||
case FLOWSPEC_DEST_PORT:
|
||||
@ -258,66 +263,99 @@ void route_vty_out_flowspec(struct vty *vty, const struct prefix *p,
|
||||
{
|
||||
struct attr *attr;
|
||||
char return_string[BGP_FLOWSPEC_STRING_DISPLAY_MAX];
|
||||
char *s;
|
||||
char *s1 = NULL, *s2 = NULL;
|
||||
json_object *json_nlri_path = NULL;
|
||||
json_object *json_ecom_path = NULL;
|
||||
json_object *json_time_path = NULL;
|
||||
char timebuf[BGP_UPTIME_LEN];
|
||||
struct bgp_dest *dest = NULL;
|
||||
|
||||
/* Print prefix */
|
||||
if (p != NULL) {
|
||||
if (p->family != AF_FLOWSPEC)
|
||||
return;
|
||||
if (json_paths) {
|
||||
if (display == NLRI_STRING_FORMAT_JSON)
|
||||
json_nlri_path = json_object_new_object();
|
||||
else
|
||||
json_nlri_path = json_paths;
|
||||
}
|
||||
if (display == NLRI_STRING_FORMAT_LARGE && path)
|
||||
vty_out(vty, "BGP flowspec entry: (flags 0x%x)\n",
|
||||
path->flags);
|
||||
bgp_fs_nlri_get_string((unsigned char *)
|
||||
p->u.prefix_flowspec.ptr,
|
||||
p->u.prefix_flowspec.prefixlen,
|
||||
return_string,
|
||||
display,
|
||||
json_nlri_path);
|
||||
if (display == NLRI_STRING_FORMAT_LARGE)
|
||||
vty_out(vty, "%s", return_string);
|
||||
else if (display == NLRI_STRING_FORMAT_DEBUG)
|
||||
vty_out(vty, "%s", return_string);
|
||||
else if (display == NLRI_STRING_FORMAT_MIN)
|
||||
vty_out(vty, " %-30s", return_string);
|
||||
else if (json_paths && display == NLRI_STRING_FORMAT_JSON)
|
||||
json_object_array_add(json_paths, json_nlri_path);
|
||||
if (path)
|
||||
dest = path->net;
|
||||
if (dest)
|
||||
bgp_dest_get_bgp_table_info(dest);
|
||||
if (p == NULL || p->family != AF_FLOWSPEC)
|
||||
return;
|
||||
if (json_paths) {
|
||||
if (display == NLRI_STRING_FORMAT_JSON)
|
||||
json_nlri_path = json_object_new_object();
|
||||
else
|
||||
json_nlri_path = json_paths;
|
||||
}
|
||||
if (display == NLRI_STRING_FORMAT_LARGE && path)
|
||||
vty_out(vty, "BGP flowspec entry: (flags 0x%x)\n",
|
||||
path->flags);
|
||||
bgp_fs_nlri_get_string((unsigned char *)
|
||||
p->u.prefix_flowspec.ptr,
|
||||
p->u.prefix_flowspec.prefixlen,
|
||||
return_string,
|
||||
display,
|
||||
json_nlri_path,
|
||||
family2afi(p->u.prefix_flowspec
|
||||
.family));
|
||||
if (display == NLRI_STRING_FORMAT_LARGE)
|
||||
vty_out(vty, "%s", return_string);
|
||||
else if (display == NLRI_STRING_FORMAT_DEBUG)
|
||||
vty_out(vty, "%s", return_string);
|
||||
else if (display == NLRI_STRING_FORMAT_MIN)
|
||||
vty_out(vty, " %-30s", return_string);
|
||||
else if (json_paths && display == NLRI_STRING_FORMAT_JSON)
|
||||
json_object_array_add(json_paths, json_nlri_path);
|
||||
if (!path)
|
||||
return;
|
||||
if (path->attr->ecommunity) {
|
||||
if (path->attr &&
|
||||
(path->attr->ecommunity || path->attr->ipv6_ecommunity)) {
|
||||
/* Print attribute */
|
||||
attr = path->attr;
|
||||
s = ecommunity_ecom2str(attr->ecommunity,
|
||||
ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
|
||||
if (!s)
|
||||
if (attr->ecommunity)
|
||||
s1 = ecommunity_ecom2str(attr->ecommunity,
|
||||
ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
|
||||
if (attr->ipv6_ecommunity)
|
||||
s2 = ecommunity_ecom2str(attr->ipv6_ecommunity,
|
||||
ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
|
||||
if (!s1 && !s2)
|
||||
return;
|
||||
if (display == NLRI_STRING_FORMAT_LARGE)
|
||||
vty_out(vty, "\t%s\n", s);
|
||||
vty_out(vty, "\t%s%s%s\n", s1 ? s1 : "",
|
||||
s2 && s1 ? " " : "", s2 ? s2 : "");
|
||||
else if (display == NLRI_STRING_FORMAT_MIN)
|
||||
vty_out(vty, "%s", s);
|
||||
vty_out(vty, "%s%s", s1 ? s1 : "", s2 ? s2 : "");
|
||||
else if (json_paths) {
|
||||
json_ecom_path = json_object_new_object();
|
||||
json_object_string_add(json_ecom_path,
|
||||
"ecomlist", s);
|
||||
if (s1)
|
||||
json_object_string_add(json_ecom_path,
|
||||
"ecomlist", s1);
|
||||
if (s2)
|
||||
json_object_string_add(json_ecom_path,
|
||||
"ecom6list", s2);
|
||||
if (display == NLRI_STRING_FORMAT_JSON)
|
||||
json_object_array_add(json_paths,
|
||||
json_ecom_path);
|
||||
}
|
||||
if (attr->nexthop.s_addr != 0 &&
|
||||
display == NLRI_STRING_FORMAT_LARGE)
|
||||
vty_out(vty, "\tNLRI NH %-16s\n",
|
||||
inet_ntoa(attr->nexthop));
|
||||
XFREE(MTYPE_ECOMMUNITY_STR, s);
|
||||
if (display == NLRI_STRING_FORMAT_LARGE) {
|
||||
char local_buff[INET6_ADDRSTRLEN];
|
||||
|
||||
local_buff[0] = '\0';
|
||||
if (p->u.prefix_flowspec.family == AF_INET &&
|
||||
attr->nexthop.s_addr != 0)
|
||||
inet_ntop(AF_INET,
|
||||
&attr->nexthop.s_addr,
|
||||
local_buff,
|
||||
INET6_ADDRSTRLEN);
|
||||
else if (p->u.prefix_flowspec.family == AF_INET6 &&
|
||||
attr->mp_nexthop_len != 0 &&
|
||||
attr->mp_nexthop_len != BGP_ATTR_NHLEN_IPV4 &&
|
||||
attr->mp_nexthop_len != BGP_ATTR_NHLEN_VPNV4)
|
||||
inet_ntop(AF_INET6,
|
||||
&attr->mp_nexthop_global,
|
||||
local_buff,
|
||||
INET6_ADDRSTRLEN);
|
||||
if (local_buff[0] != '\0')
|
||||
vty_out(vty, "\tNLRI NH %s\n",
|
||||
local_buff);
|
||||
}
|
||||
XFREE(MTYPE_ECOMMUNITY_STR, s1);
|
||||
XFREE(MTYPE_ECOMMUNITY_STR, s2);
|
||||
}
|
||||
peer_uptime(path->uptime, timebuf, BGP_UPTIME_LEN, 0, NULL);
|
||||
if (display == NLRI_STRING_FORMAT_LARGE) {
|
||||
@ -469,10 +507,17 @@ int bgp_fs_config_write_pbr(struct vty *vty, struct bgp *bgp,
|
||||
struct bgp_pbr_interface_head *head;
|
||||
bool bgp_pbr_interface_any;
|
||||
|
||||
if (!bgp_pbr_cfg || safi != SAFI_FLOWSPEC || afi != AFI_IP)
|
||||
if (!bgp_pbr_cfg || safi != SAFI_FLOWSPEC)
|
||||
return 0;
|
||||
head = &(bgp_pbr_cfg->ifaces_by_name_ipv4);
|
||||
bgp_pbr_interface_any = bgp_pbr_cfg->pbr_interface_any_ipv4;
|
||||
if (afi == AFI_IP) {
|
||||
head = &(bgp_pbr_cfg->ifaces_by_name_ipv4);
|
||||
bgp_pbr_interface_any = bgp_pbr_cfg->pbr_interface_any_ipv4;
|
||||
} else if (afi == AFI_IP6) {
|
||||
head = &(bgp_pbr_cfg->ifaces_by_name_ipv6);
|
||||
bgp_pbr_interface_any = bgp_pbr_cfg->pbr_interface_any_ipv6;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
if (!RB_EMPTY(bgp_pbr_interface_head, head) ||
|
||||
!bgp_pbr_interface_any)
|
||||
declare_node = true;
|
||||
@ -483,7 +528,8 @@ int bgp_fs_config_write_pbr(struct vty *vty, struct bgp *bgp,
|
||||
}
|
||||
|
||||
static int bgp_fs_local_install_interface(struct bgp *bgp,
|
||||
const char *no, const char *ifname)
|
||||
const char *no, const char *ifname,
|
||||
afi_t afi)
|
||||
{
|
||||
struct bgp_pbr_interface *pbr_if;
|
||||
struct bgp_pbr_config *bgp_pbr_cfg = bgp->bgp_pbr_cfg;
|
||||
@ -492,14 +538,19 @@ static int bgp_fs_local_install_interface(struct bgp *bgp,
|
||||
|
||||
if (!bgp_pbr_cfg)
|
||||
return CMD_SUCCESS;
|
||||
head = &(bgp_pbr_cfg->ifaces_by_name_ipv4);
|
||||
bgp_pbr_interface_any = &(bgp_pbr_cfg->pbr_interface_any_ipv4);
|
||||
if (afi == AFI_IP) {
|
||||
head = &(bgp_pbr_cfg->ifaces_by_name_ipv4);
|
||||
bgp_pbr_interface_any = &(bgp_pbr_cfg->pbr_interface_any_ipv4);
|
||||
} else {
|
||||
head = &(bgp_pbr_cfg->ifaces_by_name_ipv6);
|
||||
bgp_pbr_interface_any = &(bgp_pbr_cfg->pbr_interface_any_ipv6);
|
||||
}
|
||||
if (no) {
|
||||
if (!ifname) {
|
||||
if (*bgp_pbr_interface_any) {
|
||||
*bgp_pbr_interface_any = false;
|
||||
/* remove all other interface list */
|
||||
bgp_pbr_reset(bgp, AFI_IP);
|
||||
bgp_pbr_reset(bgp, afi);
|
||||
}
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
@ -523,7 +574,7 @@ static int bgp_fs_local_install_interface(struct bgp *bgp,
|
||||
if (!*bgp_pbr_interface_any) {
|
||||
/* remove all other interface list
|
||||
*/
|
||||
bgp_pbr_reset(bgp, AFI_IP);
|
||||
bgp_pbr_reset(bgp, afi);
|
||||
*bgp_pbr_interface_any = true;
|
||||
}
|
||||
}
|
||||
@ -543,7 +594,8 @@ DEFUN (bgp_fs_local_install_ifname,
|
||||
char *ifname = argv_find(argv, argc, "INTERFACE", &idx) ?
|
||||
argv[idx]->arg : NULL;
|
||||
|
||||
return bgp_fs_local_install_interface(bgp, no, ifname);
|
||||
return bgp_fs_local_install_interface(bgp, no, ifname,
|
||||
bgp_node_afi(vty));
|
||||
}
|
||||
|
||||
extern int bgp_flowspec_display_match_per_ip(afi_t afi, struct bgp_table *rib,
|
||||
@ -581,4 +633,5 @@ void bgp_flowspec_vty_init(void)
|
||||
install_element(ENABLE_NODE, &no_debug_bgp_flowspec_cmd);
|
||||
install_element(CONFIG_NODE, &no_debug_bgp_flowspec_cmd);
|
||||
install_element(BGP_FLOWSPECV4_NODE, &bgp_fs_local_install_ifname_cmd);
|
||||
install_element(BGP_FLOWSPECV6_NODE, &bgp_fs_local_install_ifname_cmd);
|
||||
}
|
||||
|
@ -391,12 +391,11 @@ static bool ecom_intersect(struct ecommunity *e1, struct ecommunity *e2)
|
||||
|
||||
if (!e1 || !e2)
|
||||
return false;
|
||||
|
||||
for (i = 0; i < e1->size; ++i) {
|
||||
for (j = 0; j < e2->size; ++j) {
|
||||
if (!memcmp(e1->val + (i * ECOMMUNITY_SIZE),
|
||||
e2->val + (j * ECOMMUNITY_SIZE),
|
||||
ECOMMUNITY_SIZE)) {
|
||||
if (!memcmp(e1->val + (i * e1->unit_size),
|
||||
e2->val + (j * e2->unit_size),
|
||||
e1->unit_size)) {
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -2456,6 +2455,10 @@ vrf_id_t get_first_vrf_for_redirect_with_rt(struct ecommunity *eckey)
|
||||
{
|
||||
struct listnode *mnode, *mnnode;
|
||||
struct bgp *bgp;
|
||||
afi_t afi = AFI_IP;
|
||||
|
||||
if (eckey->unit_size == IPV6_ECOMMUNITY_SIZE)
|
||||
afi = AFI_IP6;
|
||||
|
||||
for (ALL_LIST_ELEMENTS(bm->bgp, mnode, mnnode, bgp)) {
|
||||
struct ecommunity *ec;
|
||||
@ -2463,7 +2466,10 @@ vrf_id_t get_first_vrf_for_redirect_with_rt(struct ecommunity *eckey)
|
||||
if (bgp->inst_type != BGP_INSTANCE_TYPE_VRF)
|
||||
continue;
|
||||
|
||||
ec = bgp->vpn_policy[AFI_IP].import_redirect_rtlist;
|
||||
ec = bgp->vpn_policy[afi].import_redirect_rtlist;
|
||||
|
||||
if (ec && eckey->unit_size != ec->unit_size)
|
||||
continue;
|
||||
|
||||
if (ecom_intersect(ec, eckey))
|
||||
return bgp->vrf_id;
|
||||
|
@ -544,7 +544,7 @@ static int make_prefix(int afi, struct bgp_path_info *pi, struct prefix *p)
|
||||
if (!pi->peer)
|
||||
return -1;
|
||||
return bgp_flowspec_get_first_nh(pi->peer->bgp,
|
||||
pi, p);
|
||||
pi, p, afi);
|
||||
}
|
||||
memset(p, 0, sizeof(struct prefix));
|
||||
switch (afi) {
|
||||
|
577
bgpd/bgp_pbr.c
577
bgpd/bgp_pbr.c
@ -42,6 +42,10 @@ DEFINE_MTYPE_STATIC(BGPD, PBR_RULE, "PBR rule")
|
||||
DEFINE_MTYPE_STATIC(BGPD, PBR, "BGP PBR Context")
|
||||
DEFINE_MTYPE_STATIC(BGPD, PBR_VALMASK, "BGP PBR Val Mask Value")
|
||||
|
||||
/* chain strings too long to fit in one line */
|
||||
#define FSPEC_ACTION_EXCEED_LIMIT "flowspec actions exceeds limit"
|
||||
#define IPV6_FRAGMENT_INVALID "fragment not valid for IPv6 for this implementation"
|
||||
|
||||
RB_GENERATE(bgp_pbr_interface_head, bgp_pbr_interface,
|
||||
id_entry, bgp_pbr_interface_compare);
|
||||
struct bgp_pbr_interface_head ifaces_by_name_ipv4 =
|
||||
@ -168,35 +172,62 @@ static int bgp_pbr_match_walkcb(struct hash_bucket *bucket, void *arg)
|
||||
return HASHWALK_CONTINUE;
|
||||
}
|
||||
|
||||
static int sprintf_bgp_pbr_match_val(char *str, struct bgp_pbr_match_val *mval,
|
||||
const char *prepend)
|
||||
static int snprintf_bgp_pbr_match_val(char *str, int len,
|
||||
struct bgp_pbr_match_val *mval,
|
||||
const char *prepend)
|
||||
{
|
||||
char *ptr = str;
|
||||
int delta;
|
||||
|
||||
if (prepend)
|
||||
ptr += sprintf(ptr, "%s", prepend);
|
||||
else {
|
||||
if (mval->unary_operator & OPERATOR_UNARY_OR)
|
||||
ptr += sprintf(ptr, ", or ");
|
||||
if (mval->unary_operator & OPERATOR_UNARY_AND)
|
||||
ptr += sprintf(ptr, ", and ");
|
||||
if (prepend) {
|
||||
delta = snprintf(ptr, len, "%s", prepend);
|
||||
ptr += delta;
|
||||
len -= delta;
|
||||
} else {
|
||||
if (mval->unary_operator & OPERATOR_UNARY_OR) {
|
||||
delta = snprintf(ptr, len, ", or ");
|
||||
ptr += delta;
|
||||
len -= delta;
|
||||
}
|
||||
if (mval->unary_operator & OPERATOR_UNARY_AND) {
|
||||
delta = snprintf(ptr, len, ", and ");
|
||||
ptr += delta;
|
||||
len -= delta;
|
||||
}
|
||||
}
|
||||
if (mval->compare_operator & OPERATOR_COMPARE_LESS_THAN)
|
||||
ptr += sprintf(ptr, "<");
|
||||
if (mval->compare_operator & OPERATOR_COMPARE_GREATER_THAN)
|
||||
ptr += sprintf(ptr, ">");
|
||||
if (mval->compare_operator & OPERATOR_COMPARE_EQUAL_TO)
|
||||
ptr += sprintf(ptr, "=");
|
||||
if (mval->compare_operator & OPERATOR_COMPARE_EXACT_MATCH)
|
||||
ptr += sprintf(ptr, "match");
|
||||
ptr += sprintf(ptr, " %u", mval->value);
|
||||
if (mval->compare_operator & OPERATOR_COMPARE_LESS_THAN) {
|
||||
delta = snprintf(ptr, len, "<");
|
||||
ptr += delta;
|
||||
len -= delta;
|
||||
}
|
||||
if (mval->compare_operator & OPERATOR_COMPARE_GREATER_THAN) {
|
||||
delta = snprintf(ptr, len, ">");
|
||||
ptr += delta;
|
||||
len -= delta;
|
||||
}
|
||||
if (mval->compare_operator & OPERATOR_COMPARE_EQUAL_TO) {
|
||||
delta = snprintf(ptr, len, "=");
|
||||
ptr += delta;
|
||||
len -= delta;
|
||||
}
|
||||
if (mval->compare_operator & OPERATOR_COMPARE_EXACT_MATCH) {
|
||||
delta = snprintf(ptr, len, "match");
|
||||
ptr += delta;
|
||||
len -= delta;
|
||||
}
|
||||
ptr += snprintf(ptr, len, " %u", mval->value);
|
||||
return (int)(ptr - str);
|
||||
}
|
||||
|
||||
#define INCREMENT_DISPLAY(_ptr, _cnt) do { \
|
||||
if (_cnt) \
|
||||
(_ptr) += sprintf((_ptr), "; "); \
|
||||
_cnt++; \
|
||||
#define INCREMENT_DISPLAY(_ptr, _cnt, _len) do { \
|
||||
int sn_delta; \
|
||||
\
|
||||
if (_cnt) { \
|
||||
sn_delta = snprintf((_ptr), (_len), "; ");\
|
||||
(_len) -= sn_delta; \
|
||||
(_ptr) += sn_delta; \
|
||||
} \
|
||||
(_cnt)++; \
|
||||
} while (0)
|
||||
|
||||
/* this structure can be used for port range,
|
||||
@ -222,6 +253,7 @@ struct bgp_pbr_val_mask {
|
||||
struct bgp_pbr_filter {
|
||||
uint8_t type;
|
||||
vrf_id_t vrf_id;
|
||||
uint8_t family;
|
||||
struct prefix *src;
|
||||
struct prefix *dst;
|
||||
uint8_t bitmask_iprule;
|
||||
@ -231,6 +263,7 @@ struct bgp_pbr_filter {
|
||||
struct bgp_pbr_range_port *dst_port;
|
||||
struct bgp_pbr_val_mask *tcp_flags;
|
||||
struct bgp_pbr_val_mask *dscp;
|
||||
struct bgp_pbr_val_mask *flow_label;
|
||||
struct bgp_pbr_val_mask *pkt_len_val;
|
||||
struct bgp_pbr_val_mask *fragment;
|
||||
};
|
||||
@ -242,6 +275,7 @@ struct bgp_pbr_filter {
|
||||
struct bgp_pbr_or_filter {
|
||||
struct list *tcpflags;
|
||||
struct list *dscp;
|
||||
struct list *flowlabel;
|
||||
struct list *pkt_len;
|
||||
struct list *fragment;
|
||||
struct list *icmp_type;
|
||||
@ -268,6 +302,7 @@ static bool bgp_pbr_extract_enumerate_unary_opposite(
|
||||
TCP_HEADER_ALL_FLAGS &
|
||||
~(value);
|
||||
} else if (type_entry == FLOWSPEC_DSCP ||
|
||||
type_entry == FLOWSPEC_FLOW_LABEL ||
|
||||
type_entry == FLOWSPEC_PKT_LEN ||
|
||||
type_entry == FLOWSPEC_FRAGMENT) {
|
||||
and_valmask->val = value;
|
||||
@ -282,6 +317,7 @@ static bool bgp_pbr_extract_enumerate_unary_opposite(
|
||||
TCP_HEADER_ALL_FLAGS &
|
||||
~(value);
|
||||
} else if (type_entry == FLOWSPEC_DSCP ||
|
||||
type_entry == FLOWSPEC_FLOW_LABEL ||
|
||||
type_entry == FLOWSPEC_FRAGMENT ||
|
||||
type_entry == FLOWSPEC_PKT_LEN) {
|
||||
and_valmask->val = value;
|
||||
@ -296,7 +332,7 @@ static bool bgp_pbr_extract_enumerate_unary_opposite(
|
||||
|
||||
/* TCP : FIN and SYN -> val = ALL; mask = 3
|
||||
* TCP : not (FIN and SYN) -> val = ALL; mask = ALL & ~(FIN|RST)
|
||||
* other variables type: dscp, pkt len, fragment
|
||||
* other variables type: dscp, pkt len, fragment, flow label
|
||||
* - value is copied in bgp_pbr_val_mask->val value
|
||||
* - if negate form is identifierd, bgp_pbr_val_mask->mask set to 1
|
||||
*/
|
||||
@ -351,6 +387,7 @@ static bool bgp_pbr_extract_enumerate_unary(struct bgp_pbr_match_val list[],
|
||||
and_valmask->mask |=
|
||||
TCP_HEADER_ALL_FLAGS & list[i].value;
|
||||
} else if (type_entry == FLOWSPEC_DSCP ||
|
||||
type_entry == FLOWSPEC_FLOW_LABEL ||
|
||||
type_entry == FLOWSPEC_ICMP_TYPE ||
|
||||
type_entry == FLOWSPEC_ICMP_CODE ||
|
||||
type_entry == FLOWSPEC_FRAGMENT ||
|
||||
@ -490,9 +527,17 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api)
|
||||
api->match_protocol_num);
|
||||
return 0;
|
||||
}
|
||||
if (api->src_prefix_offset > 0 ||
|
||||
api->dst_prefix_offset > 0) {
|
||||
if (BGP_DEBUG(pbr, PBR))
|
||||
zlog_debug("BGP: match prefix offset:"
|
||||
"implementation does not support it.");
|
||||
return 0;
|
||||
}
|
||||
if (api->match_protocol_num == 1 &&
|
||||
api->protocol[0].value != PROTOCOL_UDP &&
|
||||
api->protocol[0].value != PROTOCOL_ICMP &&
|
||||
api->protocol[0].value != PROTOCOL_ICMPV6 &&
|
||||
api->protocol[0].value != PROTOCOL_TCP) {
|
||||
if (BGP_DEBUG(pbr, PBR))
|
||||
zlog_debug("BGP: match protocol operations:protocol (%d) not supported. ignoring",
|
||||
@ -575,6 +620,27 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (api->match_flowlabel_num) {
|
||||
if (api->afi == AFI_IP) {
|
||||
if (BGP_DEBUG(pbr, PBR))
|
||||
zlog_debug("BGP: match Flow Label operations:"
|
||||
"Not for IPv4.");
|
||||
return 0;
|
||||
}
|
||||
if (!bgp_pbr_extract_enumerate(api->flow_label,
|
||||
api->match_flowlabel_num,
|
||||
OPERATOR_UNARY_OR | OPERATOR_UNARY_AND,
|
||||
NULL, FLOWSPEC_FLOW_LABEL)) {
|
||||
if (BGP_DEBUG(pbr, PBR))
|
||||
zlog_debug("BGP: match FlowLabel operations:"
|
||||
"too complex. ignoring.");
|
||||
return 0;
|
||||
}
|
||||
if (BGP_DEBUG(pbr, PBR))
|
||||
zlog_debug("BGP: match FlowLabel operations "
|
||||
"not supported. ignoring.");
|
||||
return 0;
|
||||
}
|
||||
if (api->match_fragment_num) {
|
||||
char fail_str[64];
|
||||
bool success;
|
||||
@ -598,6 +664,18 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api)
|
||||
"Value not valid (%d) for this implementation",
|
||||
api->fragment[i].value);
|
||||
}
|
||||
if (api->afi == AFI_IP6 &&
|
||||
api->fragment[i].value == 1) {
|
||||
success = false;
|
||||
snprintf(fail_str, sizeof(fail_str),
|
||||
"IPv6 dont fragment match invalid (%d)",
|
||||
api->fragment[i].value);
|
||||
}
|
||||
}
|
||||
if (api->afi == AFI_IP6) {
|
||||
success = false;
|
||||
snprintf(fail_str, sizeof(fail_str),
|
||||
"%s", IPV6_FRAGMENT_INVALID);
|
||||
}
|
||||
} else
|
||||
snprintf(fail_str, sizeof(fail_str),
|
||||
@ -643,7 +721,7 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api)
|
||||
if (api->actions[i].action == ACTION_MARKING) {
|
||||
if (BGP_DEBUG(pbr, PBR)) {
|
||||
bgp_pbr_print_policy_route(api);
|
||||
zlog_warn("PBR: iprule set DSCP %u not supported",
|
||||
zlog_warn("PBR: iprule set DSCP/Flow Label %u not supported",
|
||||
api->actions[i].u.marking_dscp);
|
||||
}
|
||||
}
|
||||
@ -668,6 +746,7 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api)
|
||||
}
|
||||
|
||||
/* return -1 if build or validation failed */
|
||||
|
||||
int bgp_pbr_build_and_validate_entry(const struct prefix *p,
|
||||
struct bgp_path_info *path,
|
||||
struct bgp_pbr_entry_main *api)
|
||||
@ -679,13 +758,13 @@ int bgp_pbr_build_and_validate_entry(const struct prefix *p,
|
||||
struct bgp_pbr_entry_action *api_action;
|
||||
struct prefix *src = NULL, *dst = NULL;
|
||||
int valid_prefix = 0;
|
||||
afi_t afi = AFI_IP;
|
||||
struct bgp_pbr_entry_action *api_action_redirect_ip = NULL;
|
||||
bool discard_action_found = false;
|
||||
afi_t afi = family2afi(p->u.prefix_flowspec.family);
|
||||
|
||||
/* extract match from flowspec entries */
|
||||
ret = bgp_flowspec_match_rules_fill((uint8_t *)p->u.prefix_flowspec.ptr,
|
||||
p->u.prefix_flowspec.prefixlen, api);
|
||||
p->u.prefix_flowspec.prefixlen, api, afi);
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
/* extract actiosn from flowspec ecom list */
|
||||
@ -699,8 +778,10 @@ int bgp_pbr_build_and_validate_entry(const struct prefix *p,
|
||||
if (BGP_DEBUG(pbr, PBR_ERROR))
|
||||
flog_err(
|
||||
EC_BGP_FLOWSPEC_PACKET,
|
||||
"%s: flowspec actions exceeds limit (max %u)",
|
||||
__func__, action_count);
|
||||
"%s: %s (max %u)",
|
||||
__func__,
|
||||
FSPEC_ACTION_EXCEED_LIMIT,
|
||||
action_count);
|
||||
break;
|
||||
}
|
||||
api_action = &api->actions[action_count - 1];
|
||||
@ -737,9 +818,10 @@ int bgp_pbr_build_and_validate_entry(const struct prefix *p,
|
||||
* do not overwrite
|
||||
* draft-ietf-idr-flowspec-redirect
|
||||
*/
|
||||
if (api_action_redirect_ip) {
|
||||
if (api_action_redirect_ip->u.zr
|
||||
.redirect_ip_v4.s_addr
|
||||
if (api_action_redirect_ip &&
|
||||
p->u.prefix_flowspec.family == AF_INET) {
|
||||
if (api_action_redirect_ip->u
|
||||
.zr.redirect_ip_v4.s_addr
|
||||
!= INADDR_ANY)
|
||||
continue;
|
||||
if (path->attr->nexthop.s_addr
|
||||
@ -751,13 +833,44 @@ int bgp_pbr_build_and_validate_entry(const struct prefix *p,
|
||||
api_action_redirect_ip->u.zr.duplicate
|
||||
= ecom_eval->val[7];
|
||||
continue;
|
||||
} else {
|
||||
} else if (api_action_redirect_ip &&
|
||||
p->u.prefix_flowspec.family == AF_INET6) {
|
||||
if (memcmp(&api_action_redirect_ip->u
|
||||
.zr.redirect_ip_v6,
|
||||
&in6addr_any,
|
||||
sizeof(struct in6_addr)))
|
||||
continue;
|
||||
if (path->attr->mp_nexthop_len == 0 ||
|
||||
path->attr->mp_nexthop_len ==
|
||||
BGP_ATTR_NHLEN_IPV4 ||
|
||||
path->attr->mp_nexthop_len ==
|
||||
BGP_ATTR_NHLEN_VPNV4)
|
||||
continue;
|
||||
memcpy(&api_action_redirect_ip->u
|
||||
.zr.redirect_ip_v6,
|
||||
&path->attr->mp_nexthop_global,
|
||||
sizeof(struct in6_addr));
|
||||
api_action_redirect_ip->u.zr.duplicate
|
||||
= ecom_eval->val[7];
|
||||
continue;
|
||||
} else if (p->u.prefix_flowspec.family ==
|
||||
AF_INET) {
|
||||
api_action->action = ACTION_REDIRECT_IP;
|
||||
api_action->u.zr.redirect_ip_v4.s_addr =
|
||||
path->attr->nexthop.s_addr;
|
||||
api_action->u.zr.duplicate =
|
||||
ecom_eval->val[7];
|
||||
api_action_redirect_ip = api_action;
|
||||
} else if (p->u.prefix_flowspec.family ==
|
||||
AF_INET6) {
|
||||
api_action->action = ACTION_REDIRECT_IP;
|
||||
memcpy(&api_action->u
|
||||
.zr.redirect_ip_v6,
|
||||
&path->attr->mp_nexthop_global,
|
||||
sizeof(struct in6_addr));
|
||||
api_action->u.zr.duplicate
|
||||
= ecom_eval->val[7];
|
||||
api_action_redirect_ip = api_action;
|
||||
}
|
||||
} else if ((ecom_eval->val[0] ==
|
||||
(char)ECOMMUNITY_ENCODE_IP) &&
|
||||
@ -789,16 +902,56 @@ int bgp_pbr_build_and_validate_entry(const struct prefix *p,
|
||||
(char)ECOMMUNITY_ENCODE_TRANS_EXP)
|
||||
continue;
|
||||
ret = ecommunity_fill_pbr_action(ecom_eval,
|
||||
api_action);
|
||||
api_action,
|
||||
afi);
|
||||
if (ret != 0)
|
||||
continue;
|
||||
if ((api_action->action == ACTION_TRAFFICRATE) &&
|
||||
api->actions[i].u.r.rate == 0)
|
||||
if ((api_action->action == ACTION_TRAFFICRATE)
|
||||
&& api->actions[i].u.r.rate == 0)
|
||||
discard_action_found = true;
|
||||
}
|
||||
api->action_num++;
|
||||
}
|
||||
}
|
||||
if (path && path->attr && path->attr->ipv6_ecommunity) {
|
||||
struct ecommunity_val_ipv6 *ipv6_ecom_eval;
|
||||
|
||||
ecom = path->attr->ipv6_ecommunity;
|
||||
for (i = 0; i < ecom->size; i++) {
|
||||
ipv6_ecom_eval = (struct ecommunity_val_ipv6 *)
|
||||
(ecom->val + (i * ecom->unit_size));
|
||||
action_count++;
|
||||
if (action_count > ACTIONS_MAX_NUM) {
|
||||
if (BGP_DEBUG(pbr, PBR_ERROR))
|
||||
flog_err(
|
||||
EC_BGP_FLOWSPEC_PACKET,
|
||||
"%s: flowspec actions exceeds limit (max %u)",
|
||||
__func__, action_count);
|
||||
break;
|
||||
}
|
||||
api_action = &api->actions[action_count - 1];
|
||||
if ((ipv6_ecom_eval->val[1] ==
|
||||
(char)ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6) &&
|
||||
(ipv6_ecom_eval->val[0] ==
|
||||
(char)ECOMMUNITY_ENCODE_TRANS_EXP)) {
|
||||
struct ecommunity *eckey = ecommunity_new();
|
||||
struct ecommunity_val_ipv6 ecom_copy;
|
||||
|
||||
eckey->unit_size = IPV6_ECOMMUNITY_SIZE;
|
||||
memcpy(&ecom_copy, ipv6_ecom_eval,
|
||||
sizeof(struct ecommunity_val_ipv6));
|
||||
ecom_copy.val[1] = ECOMMUNITY_ROUTE_TARGET;
|
||||
ecommunity_add_val_ipv6(eckey, &ecom_copy,
|
||||
false, false);
|
||||
api_action->action = ACTION_REDIRECT;
|
||||
api_action->u.redirect_vrf =
|
||||
get_first_vrf_for_redirect_with_rt(
|
||||
eckey);
|
||||
ecommunity_free(&eckey);
|
||||
api->action_num++;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* if ECOMMUNITY_TRAFFIC_RATE = 0 as action
|
||||
* then reduce the API action list to that action
|
||||
*/
|
||||
@ -969,11 +1122,13 @@ uint32_t bgp_pbr_match_hash_key(const void *arg)
|
||||
|
||||
key = jhash_1word(pbm->vrf_id, 0x4312abde);
|
||||
key = jhash_1word(pbm->flags, key);
|
||||
key = jhash_1word(pbm->family, key);
|
||||
key = jhash(&pbm->pkt_len_min, 2, key);
|
||||
key = jhash(&pbm->pkt_len_max, 2, key);
|
||||
key = jhash(&pbm->tcp_flags, 2, key);
|
||||
key = jhash(&pbm->tcp_mask_flags, 2, key);
|
||||
key = jhash(&pbm->dscp_value, 1, key);
|
||||
key = jhash(&pbm->flow_label, 2, key);
|
||||
key = jhash(&pbm->fragment, 1, key);
|
||||
key = jhash(&pbm->protocol, 1, key);
|
||||
return jhash_1word(pbm->type, key);
|
||||
@ -989,6 +1144,9 @@ bool bgp_pbr_match_hash_equal(const void *arg1, const void *arg2)
|
||||
if (r1->vrf_id != r2->vrf_id)
|
||||
return false;
|
||||
|
||||
if (r1->family != r2->family)
|
||||
return false;
|
||||
|
||||
if (r1->type != r2->type)
|
||||
return false;
|
||||
|
||||
@ -1013,6 +1171,9 @@ bool bgp_pbr_match_hash_equal(const void *arg1, const void *arg2)
|
||||
if (r1->dscp_value != r2->dscp_value)
|
||||
return false;
|
||||
|
||||
if (r1->flow_label != r2->flow_label)
|
||||
return false;
|
||||
|
||||
if (r1->fragment != r2->fragment)
|
||||
return false;
|
||||
|
||||
@ -1121,6 +1282,7 @@ uint32_t bgp_pbr_action_hash_key(const void *arg)
|
||||
pbra = arg;
|
||||
key = jhash_1word(pbra->table_id, 0x4312abde);
|
||||
key = jhash_1word(pbra->fwmark, key);
|
||||
key = jhash_1word(pbra->afi, key);
|
||||
return key;
|
||||
}
|
||||
|
||||
@ -1138,6 +1300,9 @@ bool bgp_pbr_action_hash_equal(const void *arg1, const void *arg2)
|
||||
if (r1->vrf_id != r2->vrf_id)
|
||||
return false;
|
||||
|
||||
if (r1->afi != r2->afi)
|
||||
return false;
|
||||
|
||||
if (memcmp(&r1->nh, &r2->nh, sizeof(struct nexthop)))
|
||||
return false;
|
||||
|
||||
@ -1244,6 +1409,7 @@ void bgp_pbr_cleanup(struct bgp *bgp)
|
||||
if (bgp->bgp_pbr_cfg == NULL)
|
||||
return;
|
||||
bgp_pbr_reset(bgp, AFI_IP);
|
||||
bgp_pbr_reset(bgp, AFI_IP6);
|
||||
XFREE(MTYPE_PBR, bgp->bgp_pbr_cfg);
|
||||
}
|
||||
|
||||
@ -1274,132 +1440,227 @@ void bgp_pbr_print_policy_route(struct bgp_pbr_entry_main *api)
|
||||
char *ptr = return_string;
|
||||
char buff[64];
|
||||
int nb_items = 0;
|
||||
int delta, len = sizeof(return_string);
|
||||
|
||||
ptr += sprintf(ptr, "MATCH : ");
|
||||
delta = snprintf(ptr, sizeof(return_string), "MATCH : ");
|
||||
len -= delta;
|
||||
ptr += delta;
|
||||
if (api->match_bitmask & PREFIX_SRC_PRESENT) {
|
||||
struct prefix *p = &(api->src_prefix);
|
||||
|
||||
ptr += sprintf(ptr, "@src %s", prefix2str(p, buff, 64));
|
||||
INCREMENT_DISPLAY(ptr, nb_items);
|
||||
if (api->src_prefix_offset)
|
||||
delta = snprintf(ptr, len, "@src %s/off%u",
|
||||
prefix2str(p, buff, 64),
|
||||
api->src_prefix_offset);
|
||||
else
|
||||
delta = snprintf(ptr, len, "@src %s",
|
||||
prefix2str(p, buff, 64));
|
||||
len -= delta;
|
||||
ptr += delta;
|
||||
INCREMENT_DISPLAY(ptr, nb_items, len);
|
||||
}
|
||||
if (api->match_bitmask & PREFIX_DST_PRESENT) {
|
||||
struct prefix *p = &(api->dst_prefix);
|
||||
|
||||
INCREMENT_DISPLAY(ptr, nb_items);
|
||||
ptr += sprintf(ptr, "@dst %s", prefix2str(p, buff, 64));
|
||||
INCREMENT_DISPLAY(ptr, nb_items, len);
|
||||
if (api->dst_prefix_offset)
|
||||
delta = snprintf(ptr, len, "@dst %s/off%u",
|
||||
prefix2str(p, buff, 64),
|
||||
api->dst_prefix_offset);
|
||||
else
|
||||
delta = snprintf(ptr, len, "@dst %s",
|
||||
prefix2str(p, buff, 64));
|
||||
len -= delta;
|
||||
ptr += delta;
|
||||
}
|
||||
|
||||
if (api->match_protocol_num)
|
||||
INCREMENT_DISPLAY(ptr, nb_items);
|
||||
for (i = 0; i < api->match_protocol_num; i++)
|
||||
ptr += sprintf_bgp_pbr_match_val(ptr, &api->protocol[i],
|
||||
i > 0 ? NULL : "@proto ");
|
||||
INCREMENT_DISPLAY(ptr, nb_items, len);
|
||||
for (i = 0; i < api->match_protocol_num; i++) {
|
||||
delta = snprintf_bgp_pbr_match_val(ptr, len, &api->protocol[i],
|
||||
i > 0 ? NULL : "@proto ");
|
||||
len -= delta;
|
||||
ptr += delta;
|
||||
}
|
||||
|
||||
if (api->match_src_port_num)
|
||||
INCREMENT_DISPLAY(ptr, nb_items);
|
||||
for (i = 0; i < api->match_src_port_num; i++)
|
||||
ptr += sprintf_bgp_pbr_match_val(ptr, &api->src_port[i],
|
||||
i > 0 ? NULL : "@srcport ");
|
||||
INCREMENT_DISPLAY(ptr, nb_items, len);
|
||||
for (i = 0; i < api->match_src_port_num; i++) {
|
||||
delta = snprintf_bgp_pbr_match_val(ptr, len, &api->src_port[i],
|
||||
i > 0 ? NULL : "@srcport ");
|
||||
len -= delta;
|
||||
ptr += delta;
|
||||
}
|
||||
|
||||
if (api->match_dst_port_num)
|
||||
INCREMENT_DISPLAY(ptr, nb_items);
|
||||
for (i = 0; i < api->match_dst_port_num; i++)
|
||||
ptr += sprintf_bgp_pbr_match_val(ptr, &api->dst_port[i],
|
||||
i > 0 ? NULL : "@dstport ");
|
||||
INCREMENT_DISPLAY(ptr, nb_items, len);
|
||||
for (i = 0; i < api->match_dst_port_num; i++) {
|
||||
delta = snprintf_bgp_pbr_match_val(ptr, len, &api->dst_port[i],
|
||||
i > 0 ? NULL : "@dstport ");
|
||||
len -= delta;
|
||||
ptr += delta;
|
||||
}
|
||||
|
||||
if (api->match_port_num)
|
||||
INCREMENT_DISPLAY(ptr, nb_items);
|
||||
for (i = 0; i < api->match_port_num; i++)
|
||||
ptr += sprintf_bgp_pbr_match_val(ptr, &api->port[i],
|
||||
i > 0 ? NULL : "@port ");
|
||||
INCREMENT_DISPLAY(ptr, nb_items, len);
|
||||
for (i = 0; i < api->match_port_num; i++) {
|
||||
delta = snprintf_bgp_pbr_match_val(ptr, len, &api->port[i],
|
||||
i > 0 ? NULL : "@port ");
|
||||
len -= delta;
|
||||
ptr += delta;
|
||||
}
|
||||
|
||||
if (api->match_icmp_type_num)
|
||||
INCREMENT_DISPLAY(ptr, nb_items);
|
||||
for (i = 0; i < api->match_icmp_type_num; i++)
|
||||
ptr += sprintf_bgp_pbr_match_val(ptr, &api->icmp_type[i],
|
||||
i > 0 ? NULL : "@icmptype ");
|
||||
INCREMENT_DISPLAY(ptr, nb_items, len);
|
||||
for (i = 0; i < api->match_icmp_type_num; i++) {
|
||||
delta = snprintf_bgp_pbr_match_val(ptr, len, &api->icmp_type[i],
|
||||
i > 0 ? NULL : "@icmptype ");
|
||||
len -= delta;
|
||||
ptr += delta;
|
||||
}
|
||||
|
||||
if (api->match_icmp_code_num)
|
||||
INCREMENT_DISPLAY(ptr, nb_items);
|
||||
for (i = 0; i < api->match_icmp_code_num; i++)
|
||||
ptr += sprintf_bgp_pbr_match_val(ptr, &api->icmp_code[i],
|
||||
i > 0 ? NULL : "@icmpcode ");
|
||||
INCREMENT_DISPLAY(ptr, nb_items, len);
|
||||
for (i = 0; i < api->match_icmp_code_num; i++) {
|
||||
delta = snprintf_bgp_pbr_match_val(ptr, len, &api->icmp_code[i],
|
||||
i > 0 ? NULL : "@icmpcode ");
|
||||
len -= delta;
|
||||
ptr += delta;
|
||||
}
|
||||
|
||||
if (api->match_packet_length_num)
|
||||
INCREMENT_DISPLAY(ptr, nb_items);
|
||||
for (i = 0; i < api->match_packet_length_num; i++)
|
||||
ptr += sprintf_bgp_pbr_match_val(ptr, &api->packet_length[i],
|
||||
i > 0 ? NULL : "@plen ");
|
||||
INCREMENT_DISPLAY(ptr, nb_items, len);
|
||||
for (i = 0; i < api->match_packet_length_num; i++) {
|
||||
delta = snprintf_bgp_pbr_match_val(ptr, len,
|
||||
&api->packet_length[i],
|
||||
i > 0 ? NULL : "@plen ");
|
||||
len -= delta;
|
||||
ptr += delta;
|
||||
}
|
||||
|
||||
if (api->match_dscp_num)
|
||||
INCREMENT_DISPLAY(ptr, nb_items);
|
||||
for (i = 0; i < api->match_dscp_num; i++)
|
||||
ptr += sprintf_bgp_pbr_match_val(ptr, &api->dscp[i],
|
||||
i > 0 ? NULL : "@dscp ");
|
||||
INCREMENT_DISPLAY(ptr, nb_items, len);
|
||||
for (i = 0; i < api->match_dscp_num; i++) {
|
||||
delta = snprintf_bgp_pbr_match_val(ptr, len, &api->dscp[i],
|
||||
i > 0 ? NULL : "@dscp ");
|
||||
len -= delta;
|
||||
ptr += delta;
|
||||
}
|
||||
|
||||
if (api->match_flowlabel_num)
|
||||
INCREMENT_DISPLAY(ptr, nb_items, len);
|
||||
for (i = 0; i < api->match_flowlabel_num; i++) {
|
||||
delta = snprintf_bgp_pbr_match_val(ptr, len,
|
||||
&api->flow_label[i],
|
||||
i > 0 ? NULL : "@flowlabel ");
|
||||
len -= delta;
|
||||
ptr += delta;
|
||||
}
|
||||
|
||||
if (api->match_tcpflags_num)
|
||||
INCREMENT_DISPLAY(ptr, nb_items);
|
||||
for (i = 0; i < api->match_tcpflags_num; i++)
|
||||
ptr += sprintf_bgp_pbr_match_val(ptr, &api->tcpflags[i],
|
||||
i > 0 ? NULL : "@tcpflags ");
|
||||
INCREMENT_DISPLAY(ptr, nb_items, len);
|
||||
for (i = 0; i < api->match_tcpflags_num; i++) {
|
||||
delta = snprintf_bgp_pbr_match_val(ptr, len, &api->tcpflags[i],
|
||||
i > 0 ? NULL : "@tcpflags ");
|
||||
len -= delta;
|
||||
ptr += delta;
|
||||
}
|
||||
|
||||
if (api->match_fragment_num)
|
||||
INCREMENT_DISPLAY(ptr, nb_items);
|
||||
for (i = 0; i < api->match_fragment_num; i++)
|
||||
ptr += sprintf_bgp_pbr_match_val(ptr, &api->fragment[i],
|
||||
i > 0 ? NULL : "@fragment ");
|
||||
if (!nb_items)
|
||||
INCREMENT_DISPLAY(ptr, nb_items, len);
|
||||
for (i = 0; i < api->match_fragment_num; i++) {
|
||||
delta = snprintf_bgp_pbr_match_val(ptr, len, &api->fragment[i],
|
||||
i > 0 ? NULL : "@fragment ");
|
||||
len -= delta;
|
||||
ptr += delta;
|
||||
}
|
||||
|
||||
len = sizeof(return_string);
|
||||
if (!nb_items) {
|
||||
ptr = return_string;
|
||||
else
|
||||
ptr += sprintf(ptr, "; ");
|
||||
if (api->action_num)
|
||||
ptr += sprintf(ptr, "SET : ");
|
||||
} else {
|
||||
len -= (ptr - return_string);
|
||||
delta = snprintf(ptr, len, "; ");
|
||||
len -= delta;
|
||||
ptr += delta;
|
||||
}
|
||||
if (api->action_num) {
|
||||
delta = snprintf(ptr, len, "SET : ");
|
||||
len -= delta;
|
||||
ptr += delta;
|
||||
}
|
||||
nb_items = 0;
|
||||
for (i = 0; i < api->action_num; i++) {
|
||||
switch (api->actions[i].action) {
|
||||
case ACTION_TRAFFICRATE:
|
||||
INCREMENT_DISPLAY(ptr, nb_items);
|
||||
ptr += sprintf(ptr, "@set rate %f",
|
||||
api->actions[i].u.r.rate);
|
||||
INCREMENT_DISPLAY(ptr, nb_items, len);
|
||||
delta = snprintf(ptr, len, "@set rate %f",
|
||||
api->actions[i].u.r.rate);
|
||||
len -= delta;
|
||||
ptr += delta;
|
||||
break;
|
||||
case ACTION_TRAFFIC_ACTION:
|
||||
INCREMENT_DISPLAY(ptr, nb_items);
|
||||
ptr += sprintf(ptr, "@action ");
|
||||
INCREMENT_DISPLAY(ptr, nb_items, len);
|
||||
delta = snprintf(ptr, len, "@action ");
|
||||
len -= delta;
|
||||
ptr += delta;
|
||||
if (api->actions[i].u.za.filter
|
||||
& TRAFFIC_ACTION_TERMINATE)
|
||||
ptr += sprintf(ptr,
|
||||
" terminate (apply filter(s))");
|
||||
& TRAFFIC_ACTION_TERMINATE) {
|
||||
delta = snprintf(ptr, len,
|
||||
" terminate (apply filter(s))");
|
||||
len -= delta;
|
||||
ptr += delta;
|
||||
}
|
||||
if (api->actions[i].u.za.filter
|
||||
& TRAFFIC_ACTION_DISTRIBUTE)
|
||||
ptr += sprintf(ptr, " distribute");
|
||||
& TRAFFIC_ACTION_DISTRIBUTE) {
|
||||
delta = snprintf(ptr, len, " distribute");
|
||||
len -= delta;
|
||||
ptr += delta;
|
||||
}
|
||||
if (api->actions[i].u.za.filter
|
||||
& TRAFFIC_ACTION_SAMPLE)
|
||||
ptr += sprintf(ptr, " sample");
|
||||
& TRAFFIC_ACTION_SAMPLE) {
|
||||
delta = snprintf(ptr, len, " sample");
|
||||
len -= delta;
|
||||
ptr += delta;
|
||||
}
|
||||
break;
|
||||
case ACTION_REDIRECT_IP:
|
||||
INCREMENT_DISPLAY(ptr, nb_items);
|
||||
char local_buff[INET_ADDRSTRLEN];
|
||||
case ACTION_REDIRECT_IP: {
|
||||
char local_buff[INET6_ADDRSTRLEN];
|
||||
void *ptr_ip;
|
||||
|
||||
if (inet_ntop(AF_INET,
|
||||
&api->actions[i].u.zr.redirect_ip_v4,
|
||||
local_buff, INET_ADDRSTRLEN) != NULL)
|
||||
ptr += sprintf(ptr,
|
||||
INCREMENT_DISPLAY(ptr, nb_items, len);
|
||||
if (api->afi == AF_INET)
|
||||
ptr_ip = &api->actions[i].u.zr.redirect_ip_v4;
|
||||
else
|
||||
ptr_ip = &api->actions[i].u.zr.redirect_ip_v6;
|
||||
if (inet_ntop(afi2family(api->afi),
|
||||
ptr_ip, local_buff,
|
||||
INET6_ADDRSTRLEN) != NULL) {
|
||||
delta = snprintf(ptr, len,
|
||||
"@redirect ip nh %s", local_buff);
|
||||
len -= delta;
|
||||
ptr += delta;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ACTION_REDIRECT: {
|
||||
struct vrf *vrf;
|
||||
|
||||
vrf = vrf_lookup_by_id(api->actions[i].u.redirect_vrf);
|
||||
INCREMENT_DISPLAY(ptr, nb_items);
|
||||
ptr += sprintf(ptr, "@redirect vrf %s(%u)",
|
||||
VRF_LOGNAME(vrf),
|
||||
api->actions[i].u.redirect_vrf);
|
||||
INCREMENT_DISPLAY(ptr, nb_items, len);
|
||||
delta = snprintf(ptr, len, "@redirect vrf %s(%u)",
|
||||
VRF_LOGNAME(vrf),
|
||||
api->actions[i].u.redirect_vrf);
|
||||
len -= delta;
|
||||
ptr += delta;
|
||||
break;
|
||||
}
|
||||
case ACTION_MARKING:
|
||||
INCREMENT_DISPLAY(ptr, nb_items);
|
||||
ptr += sprintf(ptr, "@set dscp %u",
|
||||
api->actions[i].u.marking_dscp);
|
||||
INCREMENT_DISPLAY(ptr, nb_items, len);
|
||||
delta = snprintf(ptr, len, "@set dscp/flowlabel %u",
|
||||
api->actions[i].u.marking_dscp);
|
||||
len -= delta;
|
||||
ptr += delta;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@ -1494,7 +1755,7 @@ static void bgp_pbr_flush_entry(struct bgp *bgp, struct bgp_pbr_action *bpa,
|
||||
if (bpa->installed && bpa->table_id != 0) {
|
||||
bgp_send_pbr_rule_action(bpa, NULL, false);
|
||||
bgp_zebra_announce_default(bpa->bgp, &(bpa->nh),
|
||||
AFI_IP,
|
||||
bpa->afi,
|
||||
bpa->table_id,
|
||||
false);
|
||||
bpa->installed = false;
|
||||
@ -1573,6 +1834,8 @@ static int bgp_pbr_get_remaining_entry(struct hash_bucket *bucket, void *arg)
|
||||
bpm_temp->pkt_len_min != bpm->pkt_len_min ||
|
||||
bpm_temp->pkt_len_max != bpm->pkt_len_max ||
|
||||
bpm_temp->dscp_value != bpm->dscp_value ||
|
||||
bpm_temp->flow_label != bpm->flow_label ||
|
||||
bpm_temp->family != bpm->family ||
|
||||
bpm_temp->fragment != bpm->fragment)
|
||||
return HASHWALK_CONTINUE;
|
||||
|
||||
@ -1644,16 +1907,17 @@ static void bgp_pbr_policyroute_remove_from_zebra_unit(
|
||||
return;
|
||||
}
|
||||
|
||||
temp.family = bpf->family;
|
||||
if (bpf->src) {
|
||||
temp.flags |= MATCH_IP_SRC_SET;
|
||||
prefix_copy(&temp2.src, bpf->src);
|
||||
} else
|
||||
temp2.src.family = AF_INET;
|
||||
temp2.src.family = bpf->family;
|
||||
if (bpf->dst) {
|
||||
temp.flags |= MATCH_IP_DST_SET;
|
||||
prefix_copy(&temp2.dst, bpf->dst);
|
||||
} else
|
||||
temp2.dst.family = AF_INET;
|
||||
temp2.dst.family = bpf->family;
|
||||
if (src_port && (src_port->min_port || bpf->protocol == IPPROTO_ICMP)) {
|
||||
if (bpf->protocol == IPPROTO_ICMP)
|
||||
temp.flags |= MATCH_ICMP_SET;
|
||||
@ -1696,6 +1960,14 @@ static void bgp_pbr_policyroute_remove_from_zebra_unit(
|
||||
temp.flags |= MATCH_DSCP_SET;
|
||||
temp.dscp_value = bpf->dscp->val;
|
||||
}
|
||||
if (bpf->flow_label) {
|
||||
if (bpf->flow_label->mask)
|
||||
temp.flags |= MATCH_FLOW_LABEL_INVERSE_SET;
|
||||
else
|
||||
temp.flags |= MATCH_FLOW_LABEL_SET;
|
||||
temp.flow_label = bpf->flow_label->val;
|
||||
}
|
||||
|
||||
if (bpf->fragment) {
|
||||
if (bpf->fragment->mask)
|
||||
temp.flags |= MATCH_FRAGMENT_INVERSE_SET;
|
||||
@ -1742,6 +2014,8 @@ static uint8_t bgp_pbr_next_type_entry(uint8_t type_entry)
|
||||
if (type_entry == FLOWSPEC_TCP_FLAGS)
|
||||
return FLOWSPEC_DSCP;
|
||||
if (type_entry == FLOWSPEC_DSCP)
|
||||
return FLOWSPEC_FLOW_LABEL;
|
||||
if (type_entry == FLOWSPEC_FLOW_LABEL)
|
||||
return FLOWSPEC_PKT_LEN;
|
||||
if (type_entry == FLOWSPEC_PKT_LEN)
|
||||
return FLOWSPEC_FRAGMENT;
|
||||
@ -1835,6 +2109,9 @@ static void bgp_pbr_policyroute_remove_from_zebra_recursive(
|
||||
} else if (type_entry == FLOWSPEC_DSCP && bpof->dscp) {
|
||||
orig_list = bpof->dscp;
|
||||
target_val = &bpf->dscp;
|
||||
} else if (type_entry == FLOWSPEC_FLOW_LABEL && bpof->flowlabel) {
|
||||
orig_list = bpof->flowlabel;
|
||||
target_val = &bpf->flow_label;
|
||||
} else if (type_entry == FLOWSPEC_PKT_LEN && bpof->pkt_len) {
|
||||
orig_list = bpof->pkt_len;
|
||||
target_val = &bpf->pkt_len_val;
|
||||
@ -1872,6 +2149,9 @@ static void bgp_pbr_policyroute_remove_from_zebra(
|
||||
else if (bpof->dscp)
|
||||
bgp_pbr_policyroute_remove_from_zebra_recursive(
|
||||
bgp, path, bpf, bpof, FLOWSPEC_DSCP);
|
||||
else if (bpof->flowlabel)
|
||||
bgp_pbr_policyroute_remove_from_zebra_recursive(
|
||||
bgp, path, bpf, bpof, FLOWSPEC_FLOW_LABEL);
|
||||
else if (bpof->pkt_len)
|
||||
bgp_pbr_policyroute_remove_from_zebra_recursive(
|
||||
bgp, path, bpf, bpof, FLOWSPEC_PKT_LEN);
|
||||
@ -1888,6 +2168,8 @@ static void bgp_pbr_policyroute_remove_from_zebra(
|
||||
list_delete_all_node(bpof->tcpflags);
|
||||
if (bpof->dscp)
|
||||
list_delete_all_node(bpof->dscp);
|
||||
if (bpof->flowlabel)
|
||||
list_delete_all_node(bpof->flowlabel);
|
||||
if (bpof->pkt_len)
|
||||
list_delete_all_node(bpof->pkt_len);
|
||||
if (bpof->fragment)
|
||||
@ -1979,6 +2261,15 @@ static void bgp_pbr_dump_entry(struct bgp_pbr_filter *bpf, bool add)
|
||||
? "!" : "",
|
||||
bpf->dscp->val);
|
||||
}
|
||||
if (bpf->flow_label) {
|
||||
snprintf(buffer + remaining_len,
|
||||
sizeof(buffer)
|
||||
- remaining_len,
|
||||
"%s flow_label %d",
|
||||
bpf->flow_label->mask
|
||||
? "!" : "",
|
||||
bpf->flow_label->val);
|
||||
}
|
||||
zlog_debug("BGP: %s FS PBR from %s to %s, %s %s",
|
||||
add ? "adding" : "removing",
|
||||
bpf->src == NULL ? "<all>" :
|
||||
@ -2027,6 +2318,7 @@ static void bgp_pbr_policyroute_add_to_zebra_unit(struct bgp *bgp,
|
||||
if (nh)
|
||||
memcpy(&temp3.nh, nh, sizeof(struct nexthop));
|
||||
temp3.vrf_id = bpf->vrf_id;
|
||||
temp3.afi = family2afi(bpf->family);
|
||||
bpa = hash_get(bgp->pbr_action_hash, &temp3,
|
||||
bgp_pbr_action_alloc_intern);
|
||||
|
||||
@ -2085,7 +2377,8 @@ static void bgp_pbr_policyroute_add_to_zebra_unit(struct bgp *bgp,
|
||||
if (!bpa->installed && !bpa->install_in_progress) {
|
||||
bgp_send_pbr_rule_action(bpa, NULL, true);
|
||||
bgp_zebra_announce_default(bgp, nh,
|
||||
AFI_IP, bpa->table_id, true);
|
||||
bpa->afi,
|
||||
bpa->table_id, true);
|
||||
}
|
||||
/* ip rule add */
|
||||
if (bpr && !bpr->installed)
|
||||
@ -2111,6 +2404,7 @@ static void bgp_pbr_policyroute_add_to_zebra_unit(struct bgp *bgp,
|
||||
/* then look for bpm */
|
||||
memset(&temp, 0, sizeof(temp));
|
||||
temp.vrf_id = bpf->vrf_id;
|
||||
temp.family = bpf->family;
|
||||
if (bpf->src)
|
||||
temp.flags |= MATCH_IP_SRC_SET;
|
||||
if (bpf->dst)
|
||||
@ -2162,6 +2456,13 @@ static void bgp_pbr_policyroute_add_to_zebra_unit(struct bgp *bgp,
|
||||
temp.flags |= MATCH_DSCP_SET;
|
||||
temp.dscp_value = bpf->dscp->val;
|
||||
}
|
||||
if (bpf->flow_label) {
|
||||
if (bpf->flow_label->mask)
|
||||
temp.flags |= MATCH_FLOW_LABEL_INVERSE_SET;
|
||||
else
|
||||
temp.flags |= MATCH_FLOW_LABEL_SET;
|
||||
temp.flow_label = bpf->flow_label->val;
|
||||
}
|
||||
if (bpf->fragment) {
|
||||
if (bpf->fragment->mask)
|
||||
temp.flags |= MATCH_FRAGMENT_INVERSE_SET;
|
||||
@ -2179,7 +2480,8 @@ static void bgp_pbr_policyroute_add_to_zebra_unit(struct bgp *bgp,
|
||||
if (bpm->unique == 0) {
|
||||
bpm->unique = ++bgp_pbr_match_counter_unique;
|
||||
/* 0 value is forbidden */
|
||||
sprintf(bpm->ipset_name, "match%p", bpm);
|
||||
snprintf(bpm->ipset_name, sizeof(bpm->ipset_name),
|
||||
"match%p", bpm);
|
||||
bpm->entry_hash = hash_create_size(8,
|
||||
bgp_pbr_match_entry_hash_key,
|
||||
bgp_pbr_match_entry_hash_equal,
|
||||
@ -2197,11 +2499,11 @@ static void bgp_pbr_policyroute_add_to_zebra_unit(struct bgp *bgp,
|
||||
if (bpf->src)
|
||||
prefix_copy(&temp2.src, bpf->src);
|
||||
else
|
||||
temp2.src.family = AF_INET;
|
||||
temp2.src.family = bpf->family;
|
||||
if (bpf->dst)
|
||||
prefix_copy(&temp2.dst, bpf->dst);
|
||||
else
|
||||
temp2.dst.family = AF_INET;
|
||||
temp2.dst.family = bpf->family;
|
||||
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;
|
||||
@ -2248,7 +2550,7 @@ static void bgp_pbr_policyroute_add_to_zebra_unit(struct bgp *bgp,
|
||||
if (!bpa->installed && !bpa->install_in_progress) {
|
||||
bgp_send_pbr_rule_action(bpa, NULL, true);
|
||||
bgp_zebra_announce_default(bgp, nh,
|
||||
AFI_IP, bpa->table_id, true);
|
||||
bpa->afi, bpa->table_id, true);
|
||||
}
|
||||
|
||||
/* ipset create */
|
||||
@ -2400,8 +2702,11 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, struct bgp_path_info *path,
|
||||
bpf.type = api->type;
|
||||
memset(&nh, 0, sizeof(struct nexthop));
|
||||
nh.vrf_id = VRF_UNKNOWN;
|
||||
if (api->match_protocol_num)
|
||||
if (api->match_protocol_num) {
|
||||
proto = (uint8_t)api->protocol[0].value;
|
||||
if (api->afi == AF_INET6 && proto == IPPROTO_ICMPV6)
|
||||
proto = IPPROTO_ICMP;
|
||||
}
|
||||
/* if match_port is selected, then either src or dst port will be parsed
|
||||
* but not both at the same time
|
||||
*/
|
||||
@ -2510,6 +2815,7 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, struct bgp_path_info *path,
|
||||
bpf.protocol = proto;
|
||||
bpf.src_port = srcp;
|
||||
bpf.dst_port = dstp;
|
||||
bpf.family = afi2family(api->afi);
|
||||
if (!add) {
|
||||
bgp_pbr_policyroute_remove_from_zebra(bgp, path, &bpf, &bpof);
|
||||
return;
|
||||
@ -2559,10 +2865,18 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, struct bgp_path_info *path,
|
||||
*/
|
||||
break;
|
||||
case ACTION_REDIRECT_IP:
|
||||
nh.type = NEXTHOP_TYPE_IPV4;
|
||||
nh.gate.ipv4.s_addr =
|
||||
api->actions[i].u.zr.redirect_ip_v4.s_addr;
|
||||
nh.vrf_id = api->vrf_id;
|
||||
if (api->afi == AFI_IP) {
|
||||
nh.type = NEXTHOP_TYPE_IPV4;
|
||||
nh.gate.ipv4.s_addr =
|
||||
api->actions[i].u.zr.
|
||||
redirect_ip_v4.s_addr;
|
||||
} else {
|
||||
nh.type = NEXTHOP_TYPE_IPV6;
|
||||
memcpy(&nh.gate.ipv6,
|
||||
&api->actions[i].u.zr.redirect_ip_v6,
|
||||
sizeof(struct in6_addr));
|
||||
}
|
||||
bgp_pbr_policyroute_add_to_zebra(bgp, path, &bpf, &bpof,
|
||||
&nh, &rate);
|
||||
/* XXX combination with REDIRECT_VRF
|
||||
@ -2571,8 +2885,11 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, struct bgp_path_info *path,
|
||||
continue_loop = 0;
|
||||
break;
|
||||
case ACTION_REDIRECT:
|
||||
if (api->afi == AFI_IP)
|
||||
nh.type = NEXTHOP_TYPE_IPV4;
|
||||
else
|
||||
nh.type = NEXTHOP_TYPE_IPV6;
|
||||
nh.vrf_id = api->actions[i].u.redirect_vrf;
|
||||
nh.type = NEXTHOP_TYPE_IPV4;
|
||||
bgp_pbr_policyroute_add_to_zebra(bgp, path, &bpf, &bpof,
|
||||
&nh, &rate);
|
||||
continue_loop = 0;
|
||||
@ -2580,7 +2897,7 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, struct bgp_path_info *path,
|
||||
case ACTION_MARKING:
|
||||
if (BGP_DEBUG(pbr, PBR)) {
|
||||
bgp_pbr_print_policy_route(api);
|
||||
zlog_warn("PBR: Set DSCP %u Ignored",
|
||||
zlog_warn("PBR: Set DSCP/FlowLabel %u Ignored",
|
||||
api->actions[i].u.marking_dscp);
|
||||
}
|
||||
break;
|
||||
@ -2598,8 +2915,6 @@ void bgp_pbr_update_entry(struct bgp *bgp, const struct prefix *p,
|
||||
{
|
||||
struct bgp_pbr_entry_main api;
|
||||
|
||||
if (afi == AFI_IP6)
|
||||
return; /* IPv6 not supported */
|
||||
if (safi != SAFI_FLOWSPEC)
|
||||
return; /* not supported */
|
||||
/* Make Zebra API structure. */
|
||||
@ -2649,10 +2964,12 @@ void bgp_pbr_reset(struct bgp *bgp, afi_t afi)
|
||||
struct bgp_pbr_interface_head *head;
|
||||
struct bgp_pbr_interface *pbr_if;
|
||||
|
||||
if (!bgp_pbr_cfg || afi != AFI_IP)
|
||||
if (!bgp_pbr_cfg)
|
||||
return;
|
||||
head = &(bgp_pbr_cfg->ifaces_by_name_ipv4);
|
||||
|
||||
if (afi == AFI_IP)
|
||||
head = &(bgp_pbr_cfg->ifaces_by_name_ipv4);
|
||||
else
|
||||
head = &(bgp_pbr_cfg->ifaces_by_name_ipv6);
|
||||
while (!RB_EMPTY(bgp_pbr_interface_head, head)) {
|
||||
pbr_if = RB_ROOT(bgp_pbr_interface_head, head);
|
||||
RB_REMOVE(bgp_pbr_interface_head, head, pbr_if);
|
||||
|
@ -79,6 +79,7 @@ struct bgp_pbr_entry_action {
|
||||
vrf_id_t redirect_vrf;
|
||||
struct _pbr_redirect_ip {
|
||||
struct in_addr redirect_ip_v4;
|
||||
struct in6_addr redirect_ip_v6;
|
||||
uint8_t duplicate;
|
||||
} zr;
|
||||
uint8_t marking_dscp;
|
||||
@ -114,13 +115,17 @@ struct bgp_pbr_entry_main {
|
||||
uint8_t match_dscp_num;
|
||||
uint8_t match_tcpflags_num;
|
||||
uint8_t match_fragment_num;
|
||||
uint8_t match_flowlabel_num;
|
||||
|
||||
struct prefix src_prefix;
|
||||
struct prefix dst_prefix;
|
||||
uint8_t src_prefix_offset;
|
||||
uint8_t dst_prefix_offset;
|
||||
|
||||
#define PROTOCOL_UDP 17
|
||||
#define PROTOCOL_TCP 6
|
||||
#define PROTOCOL_ICMP 1
|
||||
#define PROTOCOL_ICMPV6 58
|
||||
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];
|
||||
@ -129,6 +134,7 @@ struct bgp_pbr_entry_main {
|
||||
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 flow_label[BGP_PBR_MATCH_VAL_MAX];
|
||||
|
||||
struct bgp_pbr_match_val tcpflags[BGP_PBR_MATCH_VAL_MAX];
|
||||
struct bgp_pbr_match_val fragment[BGP_PBR_MATCH_VAL_MAX];
|
||||
@ -154,6 +160,8 @@ extern int bgp_pbr_interface_compare(const struct bgp_pbr_interface *a,
|
||||
struct bgp_pbr_config {
|
||||
struct bgp_pbr_interface_head ifaces_by_name_ipv4;
|
||||
bool pbr_interface_any_ipv4;
|
||||
struct bgp_pbr_interface_head ifaces_by_name_ipv6;
|
||||
bool pbr_interface_any_ipv6;
|
||||
};
|
||||
|
||||
extern struct bgp_pbr_config *bgp_pbr_cfg;
|
||||
@ -179,6 +187,7 @@ struct bgp_pbr_match {
|
||||
uint32_t type;
|
||||
|
||||
uint32_t flags;
|
||||
uint8_t family;
|
||||
|
||||
uint16_t pkt_len_min;
|
||||
uint16_t pkt_len_max;
|
||||
@ -187,6 +196,7 @@ struct bgp_pbr_match {
|
||||
uint8_t dscp_value;
|
||||
uint8_t fragment;
|
||||
uint8_t protocol;
|
||||
uint16_t flow_label;
|
||||
|
||||
vrf_id_t vrf_id;
|
||||
|
||||
@ -254,6 +264,7 @@ struct bgp_pbr_action {
|
||||
bool install_in_progress;
|
||||
uint32_t refcnt;
|
||||
struct bgp *bgp;
|
||||
afi_t afi;
|
||||
};
|
||||
|
||||
extern struct bgp_pbr_rule *bgp_pbr_rule_lookup(vrf_id_t vrf_id,
|
||||
|
@ -10021,11 +10021,14 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi,
|
||||
if (dest_p->family == AF_FLOWSPEC) {
|
||||
char retstr[BGP_FLOWSPEC_STRING_DISPLAY_MAX];
|
||||
|
||||
|
||||
bgp_fs_nlri_get_string(
|
||||
(unsigned char *)
|
||||
dest_p->u.prefix_flowspec.ptr,
|
||||
dest_p->u.prefix_flowspec.prefixlen,
|
||||
retstr, NLRI_STRING_FORMAT_MIN, NULL);
|
||||
retstr, NLRI_STRING_FORMAT_MIN, NULL,
|
||||
family2afi(dest_p->u
|
||||
.prefix_flowspec.family));
|
||||
if (first)
|
||||
vty_out(vty, "\"%s/%d\": ", retstr,
|
||||
dest_p->u.prefix_flowspec
|
||||
|
@ -594,10 +594,14 @@ route_match_prefix_list_flowspec(afi_t afi, struct prefix_list *plist,
|
||||
|
||||
memset(&api, 0, sizeof(api));
|
||||
|
||||
if (family2afi(p->u.prefix_flowspec.family) != afi)
|
||||
return RMAP_NOMATCH;
|
||||
|
||||
/* extract match from flowspec entries */
|
||||
ret = bgp_flowspec_match_rules_fill(
|
||||
(uint8_t *)p->u.prefix_flowspec.ptr,
|
||||
p->u.prefix_flowspec.prefixlen, &api);
|
||||
p->u.prefix_flowspec.prefixlen, &api,
|
||||
afi);
|
||||
if (ret < 0)
|
||||
return RMAP_NOMATCH;
|
||||
if (api.match_bitmask & PREFIX_DST_PRESENT ||
|
||||
@ -2602,6 +2606,7 @@ route_set_ecommunity_lb(void *rule, const struct prefix *prefix,
|
||||
ecommunity_free(&old_ecom);
|
||||
} else {
|
||||
ecom_lb.size = 1;
|
||||
ecom_lb.unit_size = ECOMMUNITY_SIZE;
|
||||
ecom_lb.val = (uint8_t *)lb_eval.val;
|
||||
new_ecom = ecommunity_dup(&ecom_lb);
|
||||
}
|
||||
|
@ -7376,15 +7376,20 @@ DEFPY(
|
||||
}
|
||||
|
||||
static int set_ecom_list(struct vty *vty, int argc, struct cmd_token **argv,
|
||||
struct ecommunity **list)
|
||||
struct ecommunity **list, bool is_rt6)
|
||||
{
|
||||
struct ecommunity *ecom = NULL;
|
||||
struct ecommunity *ecomadd;
|
||||
|
||||
for (; argc; --argc, ++argv) {
|
||||
|
||||
ecomadd = ecommunity_str2com(argv[0]->arg,
|
||||
ECOMMUNITY_ROUTE_TARGET, 0);
|
||||
if (is_rt6)
|
||||
ecomadd = ecommunity_str2com_ipv6(argv[0]->arg,
|
||||
ECOMMUNITY_ROUTE_TARGET,
|
||||
0);
|
||||
else
|
||||
ecomadd = ecommunity_str2com(argv[0]->arg,
|
||||
ECOMMUNITY_ROUTE_TARGET,
|
||||
0);
|
||||
if (!ecomadd) {
|
||||
vty_out(vty, "Malformed community-list value\n");
|
||||
if (ecom)
|
||||
@ -7464,10 +7469,10 @@ DEFPY (af_rd_vpn_export,
|
||||
int ret;
|
||||
afi_t afi;
|
||||
int idx = 0;
|
||||
int yes = 1;
|
||||
bool yes = true;
|
||||
|
||||
if (argv_find(argv, argc, "no", &idx))
|
||||
yes = 0;
|
||||
yes = false;
|
||||
|
||||
if (yes) {
|
||||
ret = str2prefix_rd(rd_str, &prd);
|
||||
@ -7525,10 +7530,10 @@ DEFPY (af_label_vpn_export,
|
||||
mpls_label_t label = MPLS_LABEL_NONE;
|
||||
afi_t afi;
|
||||
int idx = 0;
|
||||
int yes = 1;
|
||||
bool yes = true;
|
||||
|
||||
if (argv_find(argv, argc, "no", &idx))
|
||||
yes = 0;
|
||||
yes = false;
|
||||
|
||||
/* If "no ...", squash trailing parameter */
|
||||
if (!yes)
|
||||
@ -7620,7 +7625,6 @@ DEFPY (af_nexthop_vpn_export,
|
||||
vty_out(vty, "%% Nexthop required\n");
|
||||
return CMD_WARNING_CONFIG_FAILED;
|
||||
}
|
||||
|
||||
if (!sockunion2hostprefix(nexthop_su, &p))
|
||||
return CMD_WARNING_CONFIG_FAILED;
|
||||
}
|
||||
@ -7686,10 +7690,10 @@ DEFPY (af_rt_vpn_imexport,
|
||||
vpn_policy_direction_t dir;
|
||||
afi_t afi;
|
||||
int idx = 0;
|
||||
int yes = 1;
|
||||
bool yes = true;
|
||||
|
||||
if (argv_find(argv, argc, "no", &idx))
|
||||
yes = 0;
|
||||
yes = false;
|
||||
|
||||
afi = vpn_policy_getafi(vty, bgp, false);
|
||||
if (afi == AFI_MAX)
|
||||
@ -7704,7 +7708,7 @@ DEFPY (af_rt_vpn_imexport,
|
||||
vty_out(vty, "%% Missing RTLIST\n");
|
||||
return CMD_WARNING_CONFIG_FAILED;
|
||||
}
|
||||
ret = set_ecom_list(vty, argc - idx, argv + idx, &ecom);
|
||||
ret = set_ecom_list(vty, argc - idx, argv + idx, &ecom, false);
|
||||
if (ret != CMD_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
@ -7766,10 +7770,10 @@ DEFPY (af_route_map_vpn_imexport,
|
||||
vpn_policy_direction_t dir;
|
||||
afi_t afi;
|
||||
int idx = 0;
|
||||
int yes = 1;
|
||||
bool yes = true;
|
||||
|
||||
if (argv_find(argv, argc, "no", &idx))
|
||||
yes = 0;
|
||||
yes = false;
|
||||
|
||||
afi = vpn_policy_getafi(vty, bgp, false);
|
||||
if (afi == AFI_MAX)
|
||||
@ -8008,12 +8012,12 @@ DEFPY (bgp_imexport_vpn,
|
||||
afi_t afi;
|
||||
safi_t safi;
|
||||
int idx = 0;
|
||||
int yes = 1;
|
||||
bool yes = true;
|
||||
int flag;
|
||||
vpn_policy_direction_t dir;
|
||||
|
||||
if (argv_find(argv, argc, "no", &idx))
|
||||
yes = 0;
|
||||
yes = false;
|
||||
|
||||
if (BGP_INSTANCE_TYPE_VRF != bgp->inst_type &&
|
||||
BGP_INSTANCE_TYPE_DEFAULT != bgp->inst_type) {
|
||||
@ -8061,34 +8065,44 @@ DEFPY (bgp_imexport_vpn,
|
||||
|
||||
DEFPY (af_routetarget_import,
|
||||
af_routetarget_import_cmd,
|
||||
"[no] <rt|route-target> redirect import RTLIST...",
|
||||
"[no] <rt|route-target|route-target6|rt6> redirect import RTLIST...",
|
||||
NO_STR
|
||||
"Specify route target list\n"
|
||||
"Specify route target list\n"
|
||||
"Specify route target list\n"
|
||||
"Specify route target list\n"
|
||||
"Flow-spec redirect type route target\n"
|
||||
"Import routes to this address-family\n"
|
||||
"Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN)\n")
|
||||
"Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN|IPV6:MN)\n")
|
||||
{
|
||||
VTY_DECLVAR_CONTEXT(bgp, bgp);
|
||||
int ret;
|
||||
struct ecommunity *ecom = NULL;
|
||||
afi_t afi;
|
||||
int idx = 0;
|
||||
int yes = 1;
|
||||
int idx = 0, idx_unused = 0;
|
||||
bool yes = true;
|
||||
bool rt6 = false;
|
||||
|
||||
if (argv_find(argv, argc, "no", &idx))
|
||||
yes = 0;
|
||||
yes = false;
|
||||
|
||||
if (argv_find(argv, argc, "rt6", &idx_unused) ||
|
||||
argv_find(argv, argc, "route-target6", &idx_unused))
|
||||
rt6 = true;
|
||||
|
||||
afi = vpn_policy_getafi(vty, bgp, false);
|
||||
if (afi == AFI_MAX)
|
||||
return CMD_WARNING_CONFIG_FAILED;
|
||||
|
||||
if (rt6 && afi != AFI_IP6)
|
||||
return CMD_WARNING_CONFIG_FAILED;
|
||||
|
||||
if (yes) {
|
||||
if (!argv_find(argv, argc, "RTLIST", &idx)) {
|
||||
vty_out(vty, "%% Missing RTLIST\n");
|
||||
return CMD_WARNING_CONFIG_FAILED;
|
||||
}
|
||||
ret = set_ecom_list(vty, argc - idx, argv + idx, &ecom);
|
||||
ret = set_ecom_list(vty, argc - idx, argv + idx, &ecom, rt6);
|
||||
if (ret != CMD_SUCCESS)
|
||||
return ret;
|
||||
}
|
||||
@ -14629,7 +14643,13 @@ static void bgp_vpn_policy_config_write_afi(struct vty *vty, struct bgp *bgp,
|
||||
ECOMMUNITY_FORMAT_ROUTE_MAP,
|
||||
ECOMMUNITY_ROUTE_TARGET);
|
||||
|
||||
vty_out(vty, "%*srt redirect import %s\n", indent, "", b);
|
||||
if (bgp->vpn_policy[afi].import_redirect_rtlist->unit_size
|
||||
!= ECOMMUNITY_SIZE)
|
||||
vty_out(vty, "%*srt6 redirect import %s\n",
|
||||
indent, "", b);
|
||||
else
|
||||
vty_out(vty, "%*srt redirect import %s\n",
|
||||
indent, "", b);
|
||||
XFREE(MTYPE_ECOMMUNITY_STR, b);
|
||||
}
|
||||
}
|
||||
|
@ -2356,7 +2356,10 @@ static void bgp_encode_pbr_rule_action(struct stream *s,
|
||||
struct bgp_pbr_rule *pbr)
|
||||
{
|
||||
struct prefix pfx;
|
||||
uint8_t fam = AF_INET;
|
||||
|
||||
if (pbra && pbra->nh.type == NEXTHOP_TYPE_IPV6)
|
||||
fam = AF_INET6;
|
||||
stream_putl(s, 0); /* seqno unused */
|
||||
if (pbr)
|
||||
stream_putl(s, pbr->priority);
|
||||
@ -2376,7 +2379,7 @@ static void bgp_encode_pbr_rule_action(struct stream *s,
|
||||
memcpy(&pfx, &(pbr->src), sizeof(struct prefix));
|
||||
else {
|
||||
memset(&pfx, 0, sizeof(pfx));
|
||||
pfx.family = AF_INET;
|
||||
pfx.family = fam;
|
||||
}
|
||||
stream_putc(s, pfx.family);
|
||||
stream_putc(s, pfx.prefixlen);
|
||||
@ -2388,7 +2391,7 @@ static void bgp_encode_pbr_rule_action(struct stream *s,
|
||||
memcpy(&pfx, &(pbr->dst), sizeof(struct prefix));
|
||||
else {
|
||||
memset(&pfx, 0, sizeof(pfx));
|
||||
pfx.family = AF_INET;
|
||||
pfx.family = fam;
|
||||
}
|
||||
stream_putc(s, pfx.family);
|
||||
stream_putc(s, pfx.prefixlen);
|
||||
@ -2412,7 +2415,7 @@ static void bgp_encode_pbr_ipset_match(struct stream *s,
|
||||
{
|
||||
stream_putl(s, pbim->unique);
|
||||
stream_putl(s, pbim->type);
|
||||
|
||||
stream_putc(s, pbim->family);
|
||||
stream_put(s, pbim->ipset_name,
|
||||
ZEBRA_IPSET_NAME_SIZE);
|
||||
}
|
||||
@ -2461,6 +2464,7 @@ static void bgp_encode_pbr_iptable_match(struct stream *s,
|
||||
stream_putl(s, bpa->fwmark);
|
||||
stream_put(s, pbm->ipset_name,
|
||||
ZEBRA_IPSET_NAME_SIZE);
|
||||
stream_putc(s, pbm->family);
|
||||
stream_putw(s, pbm->pkt_len_min);
|
||||
stream_putw(s, pbm->pkt_len_max);
|
||||
stream_putw(s, pbm->tcp_flags);
|
||||
@ -2468,6 +2472,7 @@ static void bgp_encode_pbr_iptable_match(struct stream *s,
|
||||
stream_putc(s, pbm->dscp_value);
|
||||
stream_putc(s, pbm->fragment);
|
||||
stream_putc(s, pbm->protocol);
|
||||
stream_putw(s, pbm->flow_label);
|
||||
}
|
||||
|
||||
/* BGP has established connection with Zebra. */
|
||||
@ -2979,7 +2984,8 @@ void bgp_send_pbr_ipset_entry_match(struct bgp_pbr_match_entry *pbrime,
|
||||
pbrime->install_in_progress = true;
|
||||
}
|
||||
|
||||
static void bgp_encode_pbr_interface_list(struct bgp *bgp, struct stream *s)
|
||||
static void bgp_encode_pbr_interface_list(struct bgp *bgp, struct stream *s,
|
||||
uint8_t family)
|
||||
{
|
||||
struct bgp_pbr_config *bgp_pbr_cfg = bgp->bgp_pbr_cfg;
|
||||
struct bgp_pbr_interface_head *head;
|
||||
@ -2988,8 +2994,10 @@ static void bgp_encode_pbr_interface_list(struct bgp *bgp, struct stream *s)
|
||||
|
||||
if (!bgp_pbr_cfg)
|
||||
return;
|
||||
head = &(bgp_pbr_cfg->ifaces_by_name_ipv4);
|
||||
|
||||
if (family == AF_INET)
|
||||
head = &(bgp_pbr_cfg->ifaces_by_name_ipv4);
|
||||
else
|
||||
head = &(bgp_pbr_cfg->ifaces_by_name_ipv6);
|
||||
RB_FOREACH (pbr_if, bgp_pbr_interface_head, head) {
|
||||
ifp = if_lookup_by_name(pbr_if->name, bgp->vrf_id);
|
||||
if (ifp)
|
||||
@ -2997,7 +3005,7 @@ static void bgp_encode_pbr_interface_list(struct bgp *bgp, struct stream *s)
|
||||
}
|
||||
}
|
||||
|
||||
static int bgp_pbr_get_ifnumber(struct bgp *bgp)
|
||||
static int bgp_pbr_get_ifnumber(struct bgp *bgp, uint8_t family)
|
||||
{
|
||||
struct bgp_pbr_config *bgp_pbr_cfg = bgp->bgp_pbr_cfg;
|
||||
struct bgp_pbr_interface_head *head;
|
||||
@ -3006,8 +3014,10 @@ static int bgp_pbr_get_ifnumber(struct bgp *bgp)
|
||||
|
||||
if (!bgp_pbr_cfg)
|
||||
return 0;
|
||||
head = &(bgp_pbr_cfg->ifaces_by_name_ipv4);
|
||||
|
||||
if (family == AF_INET)
|
||||
head = &(bgp_pbr_cfg->ifaces_by_name_ipv4);
|
||||
else
|
||||
head = &(bgp_pbr_cfg->ifaces_by_name_ipv6);
|
||||
RB_FOREACH (pbr_if, bgp_pbr_interface_head, head) {
|
||||
if (if_lookup_by_name(pbr_if->name, bgp->vrf_id))
|
||||
cnt++;
|
||||
@ -3038,10 +3048,10 @@ void bgp_send_pbr_iptable(struct bgp_pbr_action *pba,
|
||||
VRF_DEFAULT);
|
||||
|
||||
bgp_encode_pbr_iptable_match(s, pba, pbm);
|
||||
nb_interface = bgp_pbr_get_ifnumber(pba->bgp);
|
||||
nb_interface = bgp_pbr_get_ifnumber(pba->bgp, pbm->family);
|
||||
stream_putl(s, nb_interface);
|
||||
if (nb_interface)
|
||||
bgp_encode_pbr_interface_list(pba->bgp, s);
|
||||
bgp_encode_pbr_interface_list(pba->bgp, s, pbm->family);
|
||||
stream_putw_at(s, 0, stream_get_endp(s));
|
||||
ret = zclient_send_message(zclient);
|
||||
if (install) {
|
||||
@ -3063,14 +3073,14 @@ void bgp_zebra_announce_default(struct bgp *bgp, struct nexthop *nh,
|
||||
struct zapi_route api;
|
||||
struct prefix p;
|
||||
|
||||
if (!nh || nh->type != NEXTHOP_TYPE_IPV4
|
||||
if (!nh || (nh->type != NEXTHOP_TYPE_IPV4
|
||||
&& nh->type != NEXTHOP_TYPE_IPV6)
|
||||
|| nh->vrf_id == VRF_UNKNOWN)
|
||||
return;
|
||||
memset(&p, 0, sizeof(struct prefix));
|
||||
/* default route */
|
||||
if (afi != AFI_IP)
|
||||
if (afi != AFI_IP && afi != AFI_IP6)
|
||||
return;
|
||||
p.family = AF_INET;
|
||||
p.family = afi2family(afi);
|
||||
memset(&api, 0, sizeof(api));
|
||||
api.vrf_id = bgp->vrf_id;
|
||||
api.type = ZEBRA_ROUTE_BGP;
|
||||
@ -3086,7 +3096,7 @@ void bgp_zebra_announce_default(struct bgp *bgp, struct nexthop *nh,
|
||||
SET_FLAG(api.message, ZAPI_MESSAGE_DISTANCE);
|
||||
|
||||
/* redirect IP */
|
||||
if (nh->gate.ipv4.s_addr != INADDR_ANY) {
|
||||
if (afi == AFI_IP && nh->gate.ipv4.s_addr != INADDR_ANY) {
|
||||
char buff[PREFIX_STRLEN];
|
||||
|
||||
api_nh->vrf_id = nh->vrf_id;
|
||||
@ -3095,7 +3105,25 @@ void bgp_zebra_announce_default(struct bgp *bgp, struct nexthop *nh,
|
||||
|
||||
inet_ntop(AF_INET, &(nh->gate.ipv4), buff, INET_ADDRSTRLEN);
|
||||
if (BGP_DEBUG(zebra, ZEBRA))
|
||||
zlog_info("BGP: %s default route to %s table %d (redirect IP)",
|
||||
zlog_debug("BGP: %s default route to %s table %d (redirect IP)",
|
||||
announce ? "adding" : "withdrawing",
|
||||
buff, table_id);
|
||||
zclient_route_send(announce ? ZEBRA_ROUTE_ADD
|
||||
: ZEBRA_ROUTE_DELETE,
|
||||
zclient, &api);
|
||||
} else if (afi == AFI_IP6 &&
|
||||
memcmp(&nh->gate.ipv6,
|
||||
&in6addr_any, sizeof(struct in6_addr))) {
|
||||
char buff[PREFIX_STRLEN];
|
||||
|
||||
api_nh->vrf_id = nh->vrf_id;
|
||||
memcpy(&api_nh->gate.ipv6, &nh->gate.ipv6,
|
||||
sizeof(struct in6_addr));
|
||||
api_nh->type = NEXTHOP_TYPE_IPV6;
|
||||
|
||||
inet_ntop(AF_INET6, &(nh->gate.ipv6), buff, INET_ADDRSTRLEN);
|
||||
if (BGP_DEBUG(zebra, ZEBRA))
|
||||
zlog_debug("BGP: %s default route to %s table %d (redirect IP)",
|
||||
announce ? "adding" : "withdrawing",
|
||||
buff, table_id);
|
||||
zclient_route_send(announce ? ZEBRA_ROUTE_ADD
|
||||
|
@ -1534,6 +1534,7 @@ struct bgp_nlri {
|
||||
#define BGP_ATTR_AS_PATHLIMIT 21
|
||||
#define BGP_ATTR_PMSI_TUNNEL 22
|
||||
#define BGP_ATTR_ENCAP 23
|
||||
#define BGP_ATTR_IPV6_EXT_COMMUNITIES 25
|
||||
#define BGP_ATTR_LARGE_COMMUNITIES 32
|
||||
#define BGP_ATTR_PREFIX_SID 40
|
||||
#ifdef ENABLE_BGP_VNC_ATTR
|
||||
|
@ -22,6 +22,9 @@ more or less complex combination of the following:
|
||||
- Layer 3 information: DSCP value, Protocol type, packet length, fragmentation.
|
||||
- Misc layer 4 TCP flags.
|
||||
|
||||
Note that if originally Flowspec defined IPv4 rules, this is also possible to use
|
||||
IPv6 address-family. The same set of combinations as defined for IPv4 can be used.
|
||||
|
||||
A combination of the above rules is applied for traffic filtering. This is
|
||||
encoded as part of specific BGP extended communities and the action can range
|
||||
from the obvious rerouting (to nexthop or to separate VRF) to shaping, or
|
||||
@ -31,6 +34,7 @@ The following IETF drafts and RFCs have been used to implement FRR Flowspec:
|
||||
|
||||
- :rfc:`5575`
|
||||
- [Draft-IETF-IDR-Flowspec-redirect-IP]_
|
||||
- [Draft-IETF-IDR-Flow-Spec-V6]_
|
||||
|
||||
.. _design-principles-flowspec:
|
||||
|
||||
@ -108,9 +112,13 @@ As of today, it is only possible to configure Flowspec on the default VRF.
|
||||
|
||||
router bgp <AS>
|
||||
neighbor <A.B.C.D> remote-as <remoteAS>
|
||||
neighbor <A:B::C:D> remote-as <remoteAS2>
|
||||
address-family ipv4 flowspec
|
||||
neighbor <A.B.C.D> activate
|
||||
exit
|
||||
exit
|
||||
address-family ipv6 flowspec
|
||||
neighbor <A:B::C:D> activate
|
||||
exit
|
||||
exit
|
||||
|
||||
You can see Flowspec entries, by using one of the following show commands:
|
||||
@ -118,6 +126,8 @@ You can see Flowspec entries, by using one of the following show commands:
|
||||
.. index:: show bgp ipv4 flowspec [detail | A.B.C.D]
|
||||
.. clicmd:: show bgp ipv4 flowspec [detail | A.B.C.D]
|
||||
|
||||
.. index:: show bgp ipv6 flowspec [detail | A:B::C:D]
|
||||
.. clicmd:: show bgp ipv6 flowspec [detail | A:B::C:D]
|
||||
|
||||
Per-interface configuration
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@ -183,6 +193,28 @@ interfaces are created with private IP addressing scheme.
|
||||
exit
|
||||
exit
|
||||
|
||||
Similarly, it is possible to do the same for IPv6 flowspec rules, by using
|
||||
an IPv6 extended community. The format is defined on :rfc:`5701`, and that
|
||||
community contains an IPv6 address encoded in the attribute, and matches the
|
||||
locally configured imported route target IPv6 defined under the appropriate
|
||||
BGP VRF instance. Below example defines an IPv6 extended community containing
|
||||
`E:F::G:H` address followed by 2 bytes chosen by admin ( here `JJ`).
|
||||
|
||||
.. code-block:: frr
|
||||
|
||||
router bgp <ASx>
|
||||
neighbor <A:B::C:D> remote-as <ASz>
|
||||
address-family ipv6 flowspec
|
||||
neighbor A:B::C:D activate
|
||||
exit
|
||||
exit
|
||||
router bgp <ASy> vrf vrf2
|
||||
address-family ipv6 unicast
|
||||
rt6 redirect import <E:F::G:H:JJ>
|
||||
exit
|
||||
exit
|
||||
|
||||
|
||||
Flowspec monitoring & troubleshooting
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
@ -348,4 +380,5 @@ inside FRRouting.
|
||||
|
||||
.. [Draft-IETF-IDR-Flowspec-redirect-IP] <https://tools.ietf.org/id/draft-ietf-idr-flowspec-redirect-ip-02.txt>
|
||||
.. [Draft-IETF-IDR-Flowspec-Interface-Set] <https://tools.ietf.org/id/draft-ietf-idr-flowspec-interfaceset-03.txt>
|
||||
.. [Draft-IETF-IDR-Flow-Spec-V6] <https://tools.ietf.org/id/draft-ietf-idr-flow-spec-v6-10.txt>
|
||||
.. [Presentation] <https://docs.google.com/presentation/d/1ekQygUAG5yvQ3wWUyrw4Wcag0LgmbW1kV02IWcU4iUg/edit#slide=id.g378f0e1b5e_1_44>
|
||||
|
@ -129,6 +129,8 @@ struct pbr_rule {
|
||||
#define MATCH_FRAGMENT_INVERSE_SET (1 << 9)
|
||||
#define MATCH_ICMP_SET (1 << 10)
|
||||
#define MATCH_PROTOCOL_SET (1 << 11)
|
||||
#define MATCH_FLOW_LABEL_SET (1 << 12)
|
||||
#define MATCH_FLOW_LABEL_INVERSE_SET (1 << 13)
|
||||
|
||||
extern int zapi_pbr_rule_encode(uint8_t cmd, struct stream *s,
|
||||
struct pbr_rule *zrule);
|
||||
|
13
lib/prefix.c
13
lib/prefix.c
@ -188,6 +188,10 @@ int prefix_match(const struct prefix *n, const struct prefix *p)
|
||||
|
||||
if (n->family == AF_FLOWSPEC) {
|
||||
/* prefixlen is unused. look at fs prefix len */
|
||||
if (n->u.prefix_flowspec.family !=
|
||||
p->u.prefix_flowspec.family)
|
||||
return 0;
|
||||
|
||||
if (n->u.prefix_flowspec.prefixlen >
|
||||
p->u.prefix_flowspec.prefixlen)
|
||||
return 0;
|
||||
@ -325,6 +329,8 @@ void prefix_copy(union prefixptr udest, union prefixconstptr usrc)
|
||||
len = src->u.prefix_flowspec.prefixlen;
|
||||
dest->u.prefix_flowspec.prefixlen =
|
||||
src->u.prefix_flowspec.prefixlen;
|
||||
dest->u.prefix_flowspec.family =
|
||||
src->u.prefix_flowspec.family;
|
||||
dest->family = src->family;
|
||||
temp = XCALLOC(MTYPE_PREFIX_FLOWSPEC, len);
|
||||
dest->u.prefix_flowspec.ptr = (uintptr_t)temp;
|
||||
@ -374,6 +380,9 @@ int prefix_same(union prefixconstptr up1, union prefixconstptr up2)
|
||||
sizeof(struct evpn_addr)))
|
||||
return 1;
|
||||
if (p1->family == AF_FLOWSPEC) {
|
||||
if (p1->u.prefix_flowspec.family !=
|
||||
p2->u.prefix_flowspec.family)
|
||||
return 0;
|
||||
if (p1->u.prefix_flowspec.prefixlen !=
|
||||
p2->u.prefix_flowspec.prefixlen)
|
||||
return 0;
|
||||
@ -415,6 +424,10 @@ int prefix_cmp(union prefixconstptr up1, union prefixconstptr up2)
|
||||
pp1 = (const uint8_t *)p1->u.prefix_flowspec.ptr;
|
||||
pp2 = (const uint8_t *)p2->u.prefix_flowspec.ptr;
|
||||
|
||||
if (p1->u.prefix_flowspec.family !=
|
||||
p2->u.prefix_flowspec.family)
|
||||
return 1;
|
||||
|
||||
if (p1->u.prefix_flowspec.prefixlen !=
|
||||
p2->u.prefix_flowspec.prefixlen)
|
||||
return numcmp(p1->u.prefix_flowspec.prefixlen,
|
||||
|
@ -176,6 +176,7 @@ struct evpn_addr {
|
||||
#endif
|
||||
|
||||
struct flowspec_prefix {
|
||||
uint8_t family;
|
||||
uint16_t prefixlen; /* length in bytes */
|
||||
uintptr_t ptr;
|
||||
};
|
||||
|
0
tests/topotests/bgp_flowspec/__init__.py
Normal file
0
tests/topotests/bgp_flowspec/__init__.py
Normal file
54
tests/topotests/bgp_flowspec/exabgp.env
Normal file
54
tests/topotests/bgp_flowspec/exabgp.env
Normal file
@ -0,0 +1,54 @@
|
||||
|
||||
[exabgp.api]
|
||||
encoder = text
|
||||
highres = false
|
||||
respawn = false
|
||||
socket = ''
|
||||
|
||||
[exabgp.bgp]
|
||||
openwait = 60
|
||||
|
||||
[exabgp.cache]
|
||||
attributes = true
|
||||
nexthops = true
|
||||
|
||||
[exabgp.daemon]
|
||||
daemonize = true
|
||||
pid = '/var/run/exabgp/exabgp.pid'
|
||||
user = 'exabgp'
|
||||
##daemonize = false
|
||||
|
||||
[exabgp.log]
|
||||
all = false
|
||||
configuration = true
|
||||
daemon = true
|
||||
destination = '/var/log/exabgp.log'
|
||||
enable = true
|
||||
level = INFO
|
||||
message = false
|
||||
network = true
|
||||
packets = false
|
||||
parser = false
|
||||
processes = true
|
||||
reactor = true
|
||||
rib = false
|
||||
routes = false
|
||||
short = false
|
||||
timers = false
|
||||
|
||||
[exabgp.pdb]
|
||||
enable = false
|
||||
|
||||
[exabgp.profile]
|
||||
enable = false
|
||||
file = ''
|
||||
|
||||
[exabgp.reactor]
|
||||
speed = 1.0
|
||||
|
||||
[exabgp.tcp]
|
||||
acl = false
|
||||
bind = ''
|
||||
delay = 0
|
||||
once = false
|
||||
port = 179
|
32
tests/topotests/bgp_flowspec/peer1/exabgp.cfg
Normal file
32
tests/topotests/bgp_flowspec/peer1/exabgp.cfg
Normal file
@ -0,0 +1,32 @@
|
||||
neighbor 10.0.1.1 {
|
||||
router-id 10.0.1.101;
|
||||
local-address 10.0.1.101;
|
||||
local-as 100;
|
||||
peer-as 100;
|
||||
flow {
|
||||
route {
|
||||
match {
|
||||
source 1.1.1.2/32;
|
||||
destination 3.3.3.3/32;
|
||||
packet-length <200;
|
||||
}
|
||||
then {
|
||||
redirect 50.0.0.2;
|
||||
rate-limit 55;
|
||||
}
|
||||
}
|
||||
#end route 1
|
||||
route {
|
||||
match {
|
||||
source 1::2/128/0;
|
||||
destination 3::3/128/0;
|
||||
packet-length <200;
|
||||
}
|
||||
then {
|
||||
redirect 50::2;
|
||||
rate-limit 55;
|
||||
}
|
||||
}
|
||||
#end route 2
|
||||
}
|
||||
}
|
18
tests/topotests/bgp_flowspec/r1/bgpd.conf
Normal file
18
tests/topotests/bgp_flowspec/r1/bgpd.conf
Normal file
@ -0,0 +1,18 @@
|
||||
!
|
||||
hostname r1
|
||||
password zebra
|
||||
log stdout debugging
|
||||
router bgp 100
|
||||
bgp router-id 10.0.1.1
|
||||
neighbor 10.0.1.101 remote-as 100
|
||||
neighbor 10.0.1.101 update-source 10.0.1.1
|
||||
address-family ipv6 flowspec
|
||||
local-install r1-eth0
|
||||
neighbor 10.0.1.101 activate
|
||||
exit-address-family
|
||||
address-family ipv4 flowspec
|
||||
local-install r1-eth0
|
||||
neighbor 10.0.1.101 activate
|
||||
exit-address-family
|
||||
!
|
||||
!
|
53
tests/topotests/bgp_flowspec/r1/summary.txt
Normal file
53
tests/topotests/bgp_flowspec/r1/summary.txt
Normal file
@ -0,0 +1,53 @@
|
||||
{
|
||||
"ipv4Unicast":{
|
||||
"routerId":"10.0.1.1",
|
||||
"as":100,
|
||||
"vrfId":0,
|
||||
"vrfName":"default",
|
||||
"peerCount":1,
|
||||
"peers":{
|
||||
"10.0.1.101":{
|
||||
"outq":0,
|
||||
"inq":0,
|
||||
"pfxRcd":0,
|
||||
"pfxSnt":0,
|
||||
"state":"Established"
|
||||
}
|
||||
},
|
||||
"totalPeers":1
|
||||
},
|
||||
"ipv4Flowspec":{
|
||||
"routerId":"10.0.1.1",
|
||||
"as":100,
|
||||
"vrfId":0,
|
||||
"vrfName":"default",
|
||||
"peerCount":1,
|
||||
"peers":{
|
||||
"10.0.1.101":{
|
||||
"outq":0,
|
||||
"inq":0,
|
||||
"pfxRcd":1,
|
||||
"pfxSnt":0,
|
||||
"state":"Established"
|
||||
}
|
||||
},
|
||||
"totalPeers":1
|
||||
},
|
||||
"ipv6Flowspec":{
|
||||
"routerId":"10.0.1.1",
|
||||
"as":100,
|
||||
"vrfId":0,
|
||||
"vrfName":"default",
|
||||
"peerCount":1,
|
||||
"peers":{
|
||||
"10.0.1.101":{
|
||||
"outq":0,
|
||||
"inq":0,
|
||||
"pfxRcd":1,
|
||||
"pfxSnt":0,
|
||||
"state":"Established"
|
||||
}
|
||||
},
|
||||
"totalPeers":1
|
||||
}
|
||||
}
|
8
tests/topotests/bgp_flowspec/r1/zebra.conf
Normal file
8
tests/topotests/bgp_flowspec/r1/zebra.conf
Normal file
@ -0,0 +1,8 @@
|
||||
!
|
||||
hostname r1
|
||||
password zebra
|
||||
interface r1-eth0
|
||||
ip address 10.0.1.1/24
|
||||
ipv6 address 1001::1/112
|
||||
!
|
||||
|
208
tests/topotests/bgp_flowspec/test_bgp_flowspec_topo.py
Executable file
208
tests/topotests/bgp_flowspec/test_bgp_flowspec_topo.py
Executable file
@ -0,0 +1,208 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
#
|
||||
# test_bgp_flowspec_topo.py
|
||||
# Part of NetDEF Topology Tests
|
||||
#
|
||||
# Copyright (c) 2019 by 6WIND
|
||||
#
|
||||
# Permission to use, copy, modify, and/or distribute this software
|
||||
# for any purpose with or without fee is hereby granted, provided
|
||||
# that the above copyright notice and this permission notice appear
|
||||
# in all copies.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
|
||||
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
|
||||
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
|
||||
# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
|
||||
# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
|
||||
# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
|
||||
# OF THIS SOFTWARE.
|
||||
#
|
||||
|
||||
"""
|
||||
test_bgp_flowspec_topo.py: Test BGP topology with Flowspec EBGP peering
|
||||
|
||||
|
||||
+------+------+
|
||||
| peer1 |
|
||||
| BGP peer 1 |
|
||||
|192.168.0.161|
|
||||
| |
|
||||
+------+------+
|
||||
.2 | r1-eth0
|
||||
|
|
||||
~~~~~~~~~
|
||||
+---~~ s1 ~~------+
|
||||
~~ ~~
|
||||
~~~~~~~~~
|
||||
| 10.0.1.1 r1-eth0
|
||||
| 1001::1 r1-eth0
|
||||
+--------+--------+
|
||||
| r1 |
|
||||
|BGP 192.168.0.162|
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
+-----------------+
|
||||
|
||||
"""
|
||||
|
||||
import json
|
||||
import functools
|
||||
import os
|
||||
import sys
|
||||
import pytest
|
||||
import getopt
|
||||
|
||||
# Save the Current Working Directory to find configuration files.
|
||||
CWD = os.path.dirname(os.path.realpath(__file__))
|
||||
sys.path.append(os.path.join(CWD, "../"))
|
||||
|
||||
# pylint: disable=C0413
|
||||
# Import topogen and topotest helpers
|
||||
from lib import topotest
|
||||
from lib.topogen import Topogen, TopoRouter, get_topogen
|
||||
from lib.topolog import logger
|
||||
from lib.lutil import lUtil
|
||||
from lib.lutil import luCommand
|
||||
|
||||
# Required to instantiate the topology builder class.
|
||||
from mininet.topo import Topo
|
||||
|
||||
#####################################################
|
||||
##
|
||||
## Network Topology Definition
|
||||
##
|
||||
#####################################################
|
||||
|
||||
|
||||
class BGPFLOWSPECTopo1(Topo):
|
||||
"BGP EBGP Flowspec Topology 1"
|
||||
|
||||
def build(self, **_opts):
|
||||
tgen = get_topogen(self)
|
||||
|
||||
# Setup Routers
|
||||
tgen.add_router("r1")
|
||||
|
||||
# Setup Control Path Switch 1. r1-eth0
|
||||
switch = tgen.add_switch("s1")
|
||||
switch.add_link(tgen.gears["r1"])
|
||||
|
||||
## Add eBGP ExaBGP neighbors
|
||||
peer_ip = "10.0.1.101" ## peer
|
||||
peer_route = "via 10.0.1.1" ## router
|
||||
peer = tgen.add_exabgp_peer("peer1", ip=peer_ip, defaultRoute=peer_route)
|
||||
switch.add_link(peer)
|
||||
|
||||
|
||||
#####################################################
|
||||
##
|
||||
## Tests starting
|
||||
##
|
||||
#####################################################
|
||||
|
||||
|
||||
def setup_module(module):
|
||||
tgen = Topogen(BGPFLOWSPECTopo1, module.__name__)
|
||||
|
||||
tgen.start_topology()
|
||||
# check for zebra capability
|
||||
router = tgen.gears["r1"]
|
||||
|
||||
# Get r1 reference and run Daemons
|
||||
logger.info("Launching BGP and ZEBRA on r1")
|
||||
router = tgen.gears["r1"]
|
||||
router.load_config(
|
||||
TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format("r1"))
|
||||
)
|
||||
router.load_config(
|
||||
TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format("r1"))
|
||||
)
|
||||
router.start()
|
||||
|
||||
peer_list = tgen.exabgp_peers()
|
||||
for pname, peer in peer_list.iteritems():
|
||||
peer_dir = os.path.join(CWD, pname)
|
||||
env_file = os.path.join(CWD, "exabgp.env")
|
||||
peer.start(peer_dir, env_file)
|
||||
logger.info(pname)
|
||||
|
||||
|
||||
def teardown_module(module):
|
||||
tgen = get_topogen()
|
||||
tgen.stop_topology()
|
||||
|
||||
|
||||
def test_bgp_convergence():
|
||||
"Test for BGP topology convergence"
|
||||
tgen = get_topogen()
|
||||
|
||||
# Skip if previous fatal error condition is raised
|
||||
|
||||
if tgen.routers_have_failure():
|
||||
pytest.skip(tgen.errors)
|
||||
|
||||
logger.info("waiting for bgp convergence")
|
||||
|
||||
# Expected result
|
||||
router = tgen.gears["r1"]
|
||||
reffile = os.path.join(CWD, "r1/summary.txt")
|
||||
|
||||
expected = json.loads(open(reffile).read())
|
||||
|
||||
test_func = functools.partial(
|
||||
topotest.router_json_cmp, router, "show bgp summary json", expected
|
||||
)
|
||||
_, res = topotest.run_and_expect(test_func, None, count=90, wait=0.5)
|
||||
assertmsg = "BGP router network did not converge"
|
||||
assert res is None, assertmsg
|
||||
|
||||
|
||||
def test_bgp_flowspec():
|
||||
tgen = get_topogen()
|
||||
|
||||
# Skip if previous fatal error condition is raised
|
||||
if tgen.routers_have_failure():
|
||||
pytest.skip(tgen.errors)
|
||||
|
||||
router = tgen.gears["r1"]
|
||||
|
||||
logger.info("Check BGP FS entry for 3.3.3.3 with redirect IP")
|
||||
output = router.vtysh_cmd(
|
||||
"show bgp ipv4 flowspec 3.3.3.3", isjson=False, daemon="bgpd"
|
||||
)
|
||||
logger.info(output)
|
||||
if (
|
||||
"NH 50.0.0.2" not in output
|
||||
or "FS:redirect IP" not in output
|
||||
or "Packet Length < 200" not in output
|
||||
):
|
||||
assertmsg = "traffic to 3.3.3.3 should have been detected as FS entry. NOK"
|
||||
assert 0, assertmsg
|
||||
else:
|
||||
logger.info("Check BGP FS entry for 3.3.3.3 with redirect IP OK")
|
||||
|
||||
logger.info("Check BGP FS entry for 3::3 with redirect IP")
|
||||
output = router.vtysh_cmd(
|
||||
"show bgp ipv6 flowspec 3::3", isjson=False, daemon="bgpd"
|
||||
)
|
||||
logger.info(output)
|
||||
if (
|
||||
"NH 50::2" not in output
|
||||
or "FS:redirect IP" not in output
|
||||
or "Packet Length < 200" not in output
|
||||
):
|
||||
assertmsg = "traffic to 3::3 should have been detected as FS entry. NOK"
|
||||
assert 0, assertmsg
|
||||
else:
|
||||
logger.info("Check BGP FS entry for 3::3 with redirect IP OK")
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
args = ["-s"] + sys.argv[1:]
|
||||
ret = pytest.main(args)
|
||||
|
||||
sys.exit(ret)
|
@ -2841,6 +2841,7 @@ static inline void zread_ipset(ZAPI_HANDLER_ARGS)
|
||||
zpi.vrf_id = zvrf->vrf->vrf_id;
|
||||
STREAM_GETL(s, zpi.unique);
|
||||
STREAM_GETL(s, zpi.type);
|
||||
STREAM_GETC(s, zpi.family);
|
||||
STREAM_GET(&zpi.ipset_name, s, ZEBRA_IPSET_NAME_SIZE);
|
||||
|
||||
if (hdr->command == ZEBRA_IPSET_CREATE)
|
||||
@ -2951,6 +2952,7 @@ static inline void zread_iptable(ZAPI_HANDLER_ARGS)
|
||||
STREAM_GETL(s, zpi->action);
|
||||
STREAM_GETL(s, zpi->fwmark);
|
||||
STREAM_GET(&zpi->ipset_name, s, ZEBRA_IPSET_NAME_SIZE);
|
||||
STREAM_GETC(s, zpi->family);
|
||||
STREAM_GETW(s, zpi->pkt_len_min);
|
||||
STREAM_GETW(s, zpi->pkt_len_max);
|
||||
STREAM_GETW(s, zpi->tcp_flags);
|
||||
@ -2958,6 +2960,7 @@ static inline void zread_iptable(ZAPI_HANDLER_ARGS)
|
||||
STREAM_GETC(s, zpi->dscp_value);
|
||||
STREAM_GETC(s, zpi->fragment);
|
||||
STREAM_GETC(s, zpi->protocol);
|
||||
STREAM_GETW(s, zpi->flow_label);
|
||||
STREAM_GETL(s, zpi->nb_interface);
|
||||
zebra_pbr_iptable_update_interfacelist(s, zpi);
|
||||
|
||||
|
@ -83,6 +83,27 @@ const struct message icmp_typecode_str[] = {
|
||||
{0}
|
||||
};
|
||||
|
||||
const struct message icmpv6_typecode_str[] = {
|
||||
{ 128 << 8, "echo-request"},
|
||||
{ 129 << 8, "echo-reply"},
|
||||
{ 1 << 8, "no-route"},
|
||||
{ (1 << 8) + 1, "communication-prohibited"},
|
||||
{ (1 << 8) + 3, "address-unreachable"},
|
||||
{ (1 << 8) + 4, "port-unreachable"},
|
||||
{ (2 << 8), "packet-too-big"},
|
||||
{ 3 << 0, "ttl-zero-during-transit"},
|
||||
{ (3 << 8) + 1, "ttl-zero-during-reassembly"},
|
||||
{ 4 << 0, "bad-header"},
|
||||
{ (4 << 0) + 1, "unknown-header-type"},
|
||||
{ (4 << 0) + 2, "unknown-option"},
|
||||
{ 133 << 8, "router-solicitation"},
|
||||
{ 134 << 8, "router-advertisement"},
|
||||
{ 135 << 8, "neighbor-solicitation"},
|
||||
{ 136 << 8, "neighbor-advertisement"},
|
||||
{ 137 << 8, "redirect"},
|
||||
{0}
|
||||
};
|
||||
|
||||
/* definitions */
|
||||
static const struct message tcp_value_str[] = {
|
||||
{TCP_HEADER_FIN, "FIN"},
|
||||
@ -251,6 +272,8 @@ uint32_t zebra_pbr_ipset_hash_key(const void *arg)
|
||||
uint32_t *pnt = (uint32_t *)&ipset->ipset_name;
|
||||
uint32_t key = jhash_1word(ipset->vrf_id, 0x63ab42de);
|
||||
|
||||
key = jhash_1word(ipset->family, key);
|
||||
|
||||
return jhash2(pnt, ZEBRA_IPSET_NAME_HASH_SIZE, key);
|
||||
}
|
||||
|
||||
@ -267,6 +290,8 @@ bool zebra_pbr_ipset_hash_equal(const void *arg1, const void *arg2)
|
||||
return false;
|
||||
if (r1->vrf_id != r2->vrf_id)
|
||||
return false;
|
||||
if (r1->family != r2->family)
|
||||
return false;
|
||||
|
||||
if (strncmp(r1->ipset_name, r2->ipset_name,
|
||||
ZEBRA_IPSET_NAME_SIZE))
|
||||
@ -376,6 +401,8 @@ uint32_t zebra_pbr_iptable_hash_key(const void *arg)
|
||||
key = jhash2(pnt, ZEBRA_IPSET_NAME_HASH_SIZE,
|
||||
0x63ab42de);
|
||||
key = jhash_1word(iptable->fwmark, key);
|
||||
key = jhash_1word(iptable->family, key);
|
||||
key = jhash_1word(iptable->flow_label, key);
|
||||
key = jhash_1word(iptable->pkt_len_min, key);
|
||||
key = jhash_1word(iptable->pkt_len_max, key);
|
||||
key = jhash_1word(iptable->tcp_flags, key);
|
||||
@ -411,6 +438,10 @@ bool zebra_pbr_iptable_hash_equal(const void *arg1, const void *arg2)
|
||||
if (strncmp(r1->ipset_name, r2->ipset_name,
|
||||
ZEBRA_IPSET_NAME_SIZE))
|
||||
return false;
|
||||
if (r1->family != r2->family)
|
||||
return false;
|
||||
if (r1->flow_label != r2->flow_label)
|
||||
return false;
|
||||
if (r1->pkt_len_min != r2->pkt_len_min)
|
||||
return false;
|
||||
if (r1->pkt_len_max != r2->pkt_len_max)
|
||||
@ -876,7 +907,8 @@ static const char *zebra_pbr_prefix2str(union prefixconstptr pu,
|
||||
const struct prefix *p = pu.p;
|
||||
char buf[PREFIX2STR_BUFFER];
|
||||
|
||||
if (p->family == AF_INET && p->prefixlen == IPV4_MAX_PREFIXLEN) {
|
||||
if ((p->family == AF_INET && p->prefixlen == IPV4_MAX_PREFIXLEN) ||
|
||||
(p->family == AF_INET6 && p->prefixlen == IPV6_MAX_PREFIXLEN)) {
|
||||
snprintf(str, size, "%s", inet_ntop(p->family, &p->u.prefix,
|
||||
buf, PREFIX2STR_BUFFER));
|
||||
return str;
|
||||
@ -889,6 +921,9 @@ static void zebra_pbr_display_icmp(struct vty *vty,
|
||||
{
|
||||
char decoded_str[20];
|
||||
uint16_t port;
|
||||
struct zebra_pbr_ipset *zpi;
|
||||
|
||||
zpi = zpie->backpointer;
|
||||
|
||||
/* range icmp type */
|
||||
if (zpie->src_port_max || zpie->dst_port_max) {
|
||||
@ -901,8 +936,10 @@ static void zebra_pbr_display_icmp(struct vty *vty,
|
||||
memset(decoded_str, 0, sizeof(decoded_str));
|
||||
snprintf(decoded_str, sizeof(decoded_str), "%u/%u",
|
||||
zpie->src_port_min, zpie->dst_port_min);
|
||||
vty_out(vty, ":icmp:%s",
|
||||
lookup_msg(icmp_typecode_str,
|
||||
vty_out(vty, ":%s:%s",
|
||||
zpi->family == AF_INET6 ? "ipv6-icmp" : "icmp",
|
||||
lookup_msg(zpi->family == AF_INET6 ?
|
||||
icmpv6_typecode_str : icmp_typecode_str,
|
||||
port, decoded_str));
|
||||
}
|
||||
}
|
||||
@ -1012,8 +1049,9 @@ static int zebra_pbr_show_ipset_walkcb(struct hash_bucket *bucket, void *arg)
|
||||
struct vty *vty = uniqueipset->vty;
|
||||
struct zebra_ns *zns = uniqueipset->zns;
|
||||
|
||||
vty_out(vty, "IPset %s type %s\n", zpi->ipset_name,
|
||||
zebra_pbr_ipset_type2str(zpi->type));
|
||||
vty_out(vty, "IPset %s type %s family %s\n", zpi->ipset_name,
|
||||
zebra_pbr_ipset_type2str(zpi->type),
|
||||
family2str(zpi->family));
|
||||
unique.vty = vty;
|
||||
unique.zpi = zpi;
|
||||
unique.zns = zns;
|
||||
@ -1058,9 +1096,9 @@ void zebra_pbr_show_ipset_list(struct vty *vty, char *ipsetname)
|
||||
vty_out(vty, "No IPset %s found\n", ipsetname);
|
||||
return;
|
||||
}
|
||||
vty_out(vty, "IPset %s type %s\n", ipsetname,
|
||||
zebra_pbr_ipset_type2str(zpi->type));
|
||||
|
||||
vty_out(vty, "IPset %s type %s family %s\n", ipsetname,
|
||||
zebra_pbr_ipset_type2str(zpi->type),
|
||||
family2str(zpi->family));
|
||||
unique.vty = vty;
|
||||
unique.zpi = zpi;
|
||||
unique.zns = zns;
|
||||
@ -1101,7 +1139,9 @@ static void zebra_pbr_show_iptable_unit(struct zebra_pbr_iptable *iptable,
|
||||
int ret;
|
||||
uint64_t pkts = 0, bytes = 0;
|
||||
|
||||
vty_out(vty, "IPtable %s action %s (%u)\n", iptable->ipset_name,
|
||||
vty_out(vty, "IPtable %s family %s action %s (%u)\n",
|
||||
iptable->ipset_name,
|
||||
family2str(iptable->family),
|
||||
iptable->action == ZEBRA_IPTABLES_DROP ? "drop" : "redirect",
|
||||
iptable->unique);
|
||||
if (iptable->type == IPSET_NET_PORT ||
|
||||
@ -1140,6 +1180,12 @@ static void zebra_pbr_show_iptable_unit(struct zebra_pbr_iptable *iptable,
|
||||
iptable->filter_bm & MATCH_DSCP_INVERSE_SET ?
|
||||
"not" : "", iptable->dscp_value);
|
||||
}
|
||||
if (iptable->filter_bm & (MATCH_FLOW_LABEL_SET |
|
||||
MATCH_FLOW_LABEL_INVERSE_SET)) {
|
||||
vty_out(vty, "\t flowlabel %s %d\n",
|
||||
iptable->filter_bm & MATCH_FLOW_LABEL_INVERSE_SET ?
|
||||
"not" : "", iptable->flow_label);
|
||||
}
|
||||
if (iptable->fragment) {
|
||||
char val_str[10];
|
||||
|
||||
|
@ -79,6 +79,9 @@ struct zebra_pbr_ipset {
|
||||
* but value is an enum ipset_type
|
||||
*/
|
||||
uint32_t type;
|
||||
|
||||
uint8_t family;
|
||||
|
||||
char ipset_name[ZEBRA_IPSET_NAME_SIZE];
|
||||
};
|
||||
|
||||
@ -150,6 +153,9 @@ struct zebra_pbr_iptable {
|
||||
uint8_t protocol;
|
||||
|
||||
uint32_t nb_interface;
|
||||
uint16_t flow_label;
|
||||
|
||||
uint8_t family;
|
||||
|
||||
struct list *interface_name_list;
|
||||
|
||||
@ -157,6 +163,7 @@ struct zebra_pbr_iptable {
|
||||
};
|
||||
|
||||
extern const struct message icmp_typecode_str[];
|
||||
extern const struct message icmpv6_typecode_str[];
|
||||
|
||||
const char *zebra_pbr_ipset_type2str(uint32_t type);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user