Merge pull request #5799 from pguibert6WIND/flowspec_ipv6

Flowspec ipv6
This commit is contained in:
Donald Sharp 2020-08-26 08:26:46 -04:00 committed by GitHub
commit c6d41e93e0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 1737 additions and 399 deletions

View File

@ -84,6 +84,7 @@ static const struct message attr_str[] = {
#endif #endif
{BGP_ATTR_LARGE_COMMUNITIES, "LARGE_COMMUNITY"}, {BGP_ATTR_LARGE_COMMUNITIES, "LARGE_COMMUNITY"},
{BGP_ATTR_PREFIX_SID, "PREFIX_SID"}, {BGP_ATTR_PREFIX_SID, "PREFIX_SID"},
{BGP_ATTR_IPV6_EXT_COMMUNITIES, "IPV6_EXT_COMMUNITIES"},
{0}}; {0}};
static const struct message attr_flag_str[] = { 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)); MIX(lcommunity_hash_make(attr->lcommunity));
if (attr->ecommunity) if (attr->ecommunity)
MIX(ecommunity_hash_make(attr->ecommunity)); MIX(ecommunity_hash_make(attr->ecommunity));
if (attr->ipv6_ecommunity)
MIX(ecommunity_hash_make(attr->ipv6_ecommunity));
if (attr->cluster) if (attr->cluster)
MIX(cluster_hash_key_make(attr->cluster)); MIX(cluster_hash_key_make(attr->cluster));
if (attr->transit) if (attr->transit)
@ -700,6 +703,7 @@ bool attrhash_cmp(const void *p1, const void *p2)
&& attr1->label_index == attr2->label_index && attr1->label_index == attr2->label_index
&& attr1->mp_nexthop_len == attr2->mp_nexthop_len && attr1->mp_nexthop_len == attr2->mp_nexthop_len
&& attr1->ecommunity == attr2->ecommunity && attr1->ecommunity == attr2->ecommunity
&& attr1->ipv6_ecommunity == attr2->ipv6_ecommunity
&& attr1->lcommunity == attr2->lcommunity && attr1->lcommunity == attr2->lcommunity
&& attr1->cluster == attr2->cluster && attr1->cluster == attr2->cluster
&& attr1->transit == attr2->transit && attr1->transit == attr2->transit
@ -829,6 +833,15 @@ struct attr *bgp_attr_intern(struct attr *attr)
else else
attr->ecommunity->refcnt++; 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) {
if (!attr->lcommunity->refcnt) if (!attr->lcommunity->refcnt)
attr->lcommunity = lcommunity_intern(attr->lcommunity); attr->lcommunity = lcommunity_intern(attr->lcommunity);
@ -1034,6 +1047,10 @@ void bgp_attr_unintern_sub(struct attr *attr)
ecommunity_unintern(&attr->ecommunity); ecommunity_unintern(&attr->ecommunity);
UNSET_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES)); 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) if (attr->lcommunity)
lcommunity_unintern(&attr->lcommunity); lcommunity_unintern(&attr->lcommunity);
UNSET_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_LARGE_COMMUNITIES)); 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); community_free(&attr->community);
if (attr->ecommunity && !attr->ecommunity->refcnt) if (attr->ecommunity && !attr->ecommunity->refcnt)
ecommunity_free(&attr->ecommunity); ecommunity_free(&attr->ecommunity);
if (attr->ipv6_ecommunity && !attr->ipv6_ecommunity->refcnt)
ecommunity_free(&attr->ipv6_ecommunity);
if (attr->lcommunity && !attr->lcommunity->refcnt) if (attr->lcommunity && !attr->lcommunity->refcnt)
lcommunity_free(&attr->lcommunity); lcommunity_free(&attr->lcommunity);
if (attr->cluster && !attr->cluster->refcnt) { 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_LOCAL_PREF:
case BGP_ATTR_COMMUNITIES: case BGP_ATTR_COMMUNITIES:
case BGP_ATTR_EXT_COMMUNITIES: case BGP_ATTR_EXT_COMMUNITIES:
case BGP_ATTR_IPV6_EXT_COMMUNITIES:
case BGP_ATTR_LARGE_COMMUNITIES: case BGP_ATTR_LARGE_COMMUNITIES:
case BGP_ATTR_ORIGINATOR_ID: case BGP_ATTR_ORIGINATOR_ID:
case BGP_ATTR_CLUSTER_LIST: case BGP_ATTR_CLUSTER_LIST:
@ -1286,6 +1306,8 @@ const uint8_t attr_flags_values[] = {
[BGP_ATTR_LARGE_COMMUNITIES] = [BGP_ATTR_LARGE_COMMUNITIES] =
BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS, BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS,
[BGP_ATTR_PREFIX_SID] = 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; 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; 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 */ /* Parse Tunnel Encap attribute in an UPDATE */
static int bgp_attr_encap(uint8_t type, struct peer *peer, /* IN */ static int bgp_attr_encap(uint8_t type, struct peer *peer, /* IN */
bgp_size_t length, /* IN: attr's length field */ 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: case BGP_ATTR_PMSI_TUNNEL:
ret = bgp_attr_pmsi_tunnel(&attr_args); ret = bgp_attr_pmsi_tunnel(&attr_args);
break; break;
case BGP_ATTR_IPV6_EXT_COMMUNITIES:
ret = bgp_attr_ipv6_ext_communities(&attr_args);
break;
default: default:
ret = bgp_attr_unknown(&attr_args); ret = bgp_attr_unknown(&attr_args);
break; break;

View File

@ -177,6 +177,9 @@ struct attr {
/* Extended Communities attribute. */ /* Extended Communities attribute. */
struct ecommunity *ecommunity; struct ecommunity *ecommunity;
/* Extended Communities attribute. */
struct ecommunity *ipv6_ecommunity;
/* Large Communities attribute. */ /* Large Communities attribute. */
struct lcommunity *lcommunity; struct lcommunity *lcommunity;

View File

@ -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, bgp_fs_nlri_get_string((unsigned char *)fs->prefix.ptr,
fs->prefix.prefixlen, fs->prefix.prefixlen,
return_string, 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), snprintf(str, size, "FS %s Match{%s}", afi2str(afi),
return_string); return_string);
} else } else

View File

@ -48,7 +48,12 @@ static struct hash *ecomhash;
/* Allocate a new ecommunities. */ /* Allocate a new ecommunities. */
struct ecommunity *ecommunity_new(void) 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) 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 once and whether the new value should replace what is existing or
not. not.
*/ */
bool ecommunity_add_val(struct ecommunity *ecom, struct ecommunity_val *eval, static bool ecommunity_add_val_internal(struct ecommunity *ecom,
bool unique, bool overwrite) const void *eval,
bool unique, bool overwrite,
uint8_t ecom_size)
{ {
int c, ins_idx; 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. */ /* When this is fist value, just add it. */
if (ecom->val == NULL) { if (ecom->val == NULL) {
ecom->size = 1; ecom->size = 1;
ecom->val = XCALLOC(MTYPE_ECOMMUNITY_VAL, ECOMMUNITY_SIZE); ecom->val = XMALLOC(MTYPE_ECOMMUNITY_VAL,
memcpy(ecom->val, eval->val, ECOMMUNITY_SIZE); ecom_length_size(ecom, ecom_size));
memcpy(ecom->val, eval, ecom_size);
return true; return true;
} }
/* If the value already exists in the structure return 0. */ /* If the value already exists in the structure return 0. */
/* check also if the extended community itself exists. */ /* check also if the extended community itself exists. */
c = 0; c = 0;
ins_idx = -1; ins_idx = -1;
for (uint8_t *p = ecom->val; c < ecom->size; for (uint8_t *p = ecom->val; c < ecom->size;
p += ECOMMUNITY_SIZE, c++) { p += ecom_size, c++) {
if (unique) { if (unique) {
if (p[0] == eval->val[0] && if (ecom_size == ECOMMUNITY_SIZE) {
p[1] == eval->val[1]) { if (p[0] == eval4->val[0] &&
p[1] == eval4->val[1]) {
if (overwrite) { if (overwrite) {
memcpy(p, eval->val, ECOMMUNITY_SIZE); 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 true;
} }
return false; return false;
} }
} }
int ret = memcmp(p, eval->val, ECOMMUNITY_SIZE); }
int ret = memcmp(p, eval, ecom_size);
if (ret == 0) if (ret == 0)
return false; return false;
if (ret > 0) { 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. */ /* Add the value to the structure with numerical sorting. */
ecom->size++; ecom->size++;
ecom->val = XREALLOC(MTYPE_ECOMMUNITY_VAL, ecom->val, 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), memmove(ecom->val + ((ins_idx + 1) * ecom_size),
(ecom->size - 1 - ins_idx) * ECOMMUNITY_SIZE); ecom->val + (ins_idx * ecom_size),
memcpy(ecom->val + (ins_idx * ECOMMUNITY_SIZE), (ecom->size - 1 - ins_idx) * ecom_size);
eval->val, ECOMMUNITY_SIZE); memcpy(ecom->val + (ins_idx * ecom_size),
eval, ecom_size);
return true; return true;
} }
/* This function takes pointer to Extended Communites strucutre then /* Add a new Extended Communities value to Extended Communities
create a new Extended Communities structure by uniq and sort each * Attribute structure. When the value is already exists in the
Extended Communities value. */ * structure, we don't add the value. Newly added value is sorted by
struct ecommunity *ecommunity_uniq_sort(struct ecommunity *ecom) * 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; int i;
struct ecommunity *new; struct ecommunity *new;
struct ecommunity_val *eval; const void *eval;
if (!ecom) if (!ecom)
return NULL; return NULL;
new = ecommunity_new(); new = ecommunity_new();
new->unit_size = ecom_size;
for (i = 0; i < ecom->size; i++) { for (i = 0; i < ecom->size; i++) {
eval = (struct ecommunity_val *)(ecom->val eval = (void *)(ecom->val + (i * ecom_size));
+ (i * ECOMMUNITY_SIZE)); ecommunity_add_val_internal(new, eval, false, false, ecom_size);
ecommunity_add_val(new, eval, false, false);
} }
return new; 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. */ /* 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 tmp;
struct ecommunity *new; struct ecommunity *new;
/* Length check. */ /* Length check. */
if (length % ECOMMUNITY_SIZE) if (length % size_ecom)
return NULL; return NULL;
/* Prepare tmporary structure for making a new Extended Communities /* Prepare tmporary structure for making a new Extended Communities
Attribute. */ Attribute. */
tmp.size = length / ECOMMUNITY_SIZE; tmp.size = length / size_ecom;
tmp.val = pnt; tmp.val = pnt;
/* Create a new Extended Communities Attribute by uniq and sort each /* Create a new Extended Communities Attribute by uniq and sort each
Extended Communities value */ Extended Communities value */
new = ecommunity_uniq_sort(&tmp); new = ecommunity_uniq_sort_internal(&tmp, size_ecom);
return ecommunity_intern(new); 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. */ /* Duplicate the Extended Communities Attribute structure. */
struct ecommunity *ecommunity_dup(struct ecommunity *ecom) 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 = XCALLOC(MTYPE_ECOMMUNITY, sizeof(struct ecommunity));
new->size = ecom->size; new->size = ecom->size;
new->unit_size = ecom->unit_size;
if (new->size) { if (new->size) {
new->val = XMALLOC(MTYPE_ECOMMUNITY_VAL, new->val = XMALLOC(MTYPE_ECOMMUNITY_VAL,
ecom->size * ECOMMUNITY_SIZE); ecom->size * ecom->unit_size);
memcpy(new->val, ecom->val, ecom->size * ECOMMUNITY_SIZE); memcpy(new->val, ecom->val, ecom->size * ecom->unit_size);
} else } else
new->val = NULL; new->val = NULL;
return new; return new;
@ -216,14 +287,16 @@ struct ecommunity *ecommunity_merge(struct ecommunity *ecom1,
if (ecom1->val) if (ecom1->val)
ecom1->val = ecom1->val =
XREALLOC(MTYPE_ECOMMUNITY_VAL, ecom1->val, XREALLOC(MTYPE_ECOMMUNITY_VAL, ecom1->val,
(ecom1->size + ecom2->size) * ECOMMUNITY_SIZE); (ecom1->size + ecom2->size) *
ecom1->unit_size);
else else
ecom1->val = ecom1->val =
XMALLOC(MTYPE_ECOMMUNITY_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, memcpy(ecom1->val + (ecom1->size * ecom1->unit_size), ecom2->val,
ecom2->size * ECOMMUNITY_SIZE); ecom2->size * ecom1->unit_size);
ecom1->size += ecom2->size; ecom1->size += ecom2->size;
return ecom1; return ecom1;
@ -235,9 +308,7 @@ struct ecommunity *ecommunity_intern(struct ecommunity *ecom)
struct ecommunity *find; struct ecommunity *find;
assert(ecom->refcnt == 0); assert(ecom->refcnt == 0);
find = (struct ecommunity *)hash_get(ecomhash, ecom, hash_alloc_intern); find = (struct ecommunity *)hash_get(ecomhash, ecom, hash_alloc_intern);
if (find != ecom) if (find != ecom)
ecommunity_free(&ecom); ecommunity_free(&ecom);
@ -272,7 +343,7 @@ void ecommunity_unintern(struct ecommunity **ecom)
unsigned int ecommunity_hash_make(const void *arg) unsigned int ecommunity_hash_make(const void *arg)
{ {
const struct ecommunity *ecom = 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); return jhash(ecom->val, size, 0x564321ab);
} }
@ -289,9 +360,12 @@ bool ecommunity_cmp(const void *arg1, const void *arg2)
if (ecom1 == NULL || ecom2 == NULL) if (ecom1 == NULL || ecom2 == NULL)
return false; return false;
if (ecom1->unit_size != ecom2->unit_size)
return false;
return (ecom1->size == ecom2->size return (ecom1->size == ecom2->size
&& memcmp(ecom1->val, ecom2->val, ecom1->size * ECOMMUNITY_SIZE) && memcmp(ecom1->val, ecom2->val, ecom1->size *
== 0); ecom1->unit_size) == 0);
} }
/* Initialize Extended Comminities related hash. */ /* Initialize Extended Comminities related hash. */
@ -314,16 +388,21 @@ enum ecommunity_token {
ecommunity_token_rt, ecommunity_token_rt,
ecommunity_token_soo, ecommunity_token_soo,
ecommunity_token_val, ecommunity_token_val,
ecommunity_token_rt6,
ecommunity_token_val6,
}; };
/* static int ecommunity_encode_internal(uint8_t type, uint8_t sub_type,
* Encode BGP extended community from passed values. Supports types int trans, as_t as,
* defined in RFC 4360 and well-known sub-types. struct in_addr *ip,
*/ struct in6_addr *ip6,
static int ecommunity_encode(uint8_t type, uint8_t sub_type, int trans, as_t as, uint32_t val,
struct in_addr ip, uint32_t val, void *eval_ptr)
struct ecommunity_val *eval)
{ {
struct ecommunity_val *eval = (struct ecommunity_val *)eval_ptr;
struct ecommunity_val_ipv6 *eval6 =
(struct ecommunity_val_ipv6 *)eval_ptr;
assert(eval); assert(eval);
if (type == ECOMMUNITY_ENCODE_AS) { if (type == ECOMMUNITY_ENCODE_AS) {
if (as > BGP_AS_MAX) 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) { || type == ECOMMUNITY_ENCODE_AS4) {
if (val > UINT16_MAX) if (val > UINT16_MAX)
return -1; 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. */ /* 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[6] = (val >> 8) & 0xff;
eval->val[7] = val & 0xff; eval->val[7] = val & 0xff;
} else if (type == ECOMMUNITY_ENCODE_IP) { } 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[6] = (val >> 8) & 0xff;
eval->val[7] = val & 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 { } else {
eval->val[2] = (as >> 24) & 0xff; eval->val[2] = (as >> 24) & 0xff;
eval->val[3] = (as >> 16) & 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; 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. */ /* Get next Extended Communities token from the string. */
static const char *ecommunity_gettoken(const char *str, static const char *ecommunity_gettoken(const char *str,
struct ecommunity_val *eval, void *eval_ptr,
enum ecommunity_token *token) enum ecommunity_token *token)
{ {
int ret; int ret;
@ -374,11 +474,12 @@ static const char *ecommunity_gettoken(const char *str,
const char *p = str; const char *p = str;
char *endptr; char *endptr;
struct in_addr ip; struct in_addr ip;
struct in6_addr ip6;
as_t as = 0; as_t as = 0;
uint32_t val = 0; uint32_t val = 0;
uint8_t ecomm_type; uint8_t ecomm_type;
char buf[INET_ADDRSTRLEN + 1]; char buf[INET_ADDRSTRLEN + 1];
struct ecommunity_val *eval = (struct ecommunity_val *)eval_ptr;
/* Skip white space. */ /* Skip white space. */
while (isspace((unsigned char)*p)) { while (isspace((unsigned char)*p)) {
p++; p++;
@ -396,6 +497,9 @@ static const char *ecommunity_gettoken(const char *str,
p++; p++;
if (tolower((unsigned char)*p) == 't') { if (tolower((unsigned char)*p) == 't') {
p++; p++;
if (*p != '\0' && tolower((int)*p) == '6')
*token = ecommunity_token_rt6;
else
*token = ecommunity_token_rt; *token = ecommunity_token_rt;
return p; return p;
} }
@ -435,6 +539,7 @@ static const char *ecommunity_gettoken(const char *str,
* a) A.B.C.D:MN * a) A.B.C.D:MN
* b) EF:OPQR * b) EF:OPQR
* c) GHJK:MN * c) GHJK:MN
* d) <IPV6>:MN (only with rt6)
* *
* A.B.C.D: Four Byte IP * A.B.C.D: Four Byte IP
* EF: Two byte ASN * EF: Two byte ASN
@ -443,6 +548,38 @@ static const char *ecommunity_gettoken(const char *str,
* OPQR: Four byte value * 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 == '.') { while (isdigit((unsigned char)*p) || *p == ':' || *p == '.') {
if (*p == ':') { if (*p == ':') {
if (separator) if (separator)
@ -508,41 +645,18 @@ error:
return p; return p;
} }
/* Convert string to extended community attribute. static struct ecommunity *ecommunity_str2com_internal(const char *str, int type,
int keyword_included,
When type is already known, please specify both str and type. str bool is_ipv6_extcomm)
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)
{ {
struct ecommunity *ecom = NULL; struct ecommunity *ecom = NULL;
enum ecommunity_token token = ecommunity_token_unknown; enum ecommunity_token token = ecommunity_token_unknown;
struct ecommunity_val eval; struct ecommunity_val_ipv6 eval;
int keyword = 0; 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) { switch (token) {
case ecommunity_token_rt: case ecommunity_token_rt:
case ecommunity_token_soo: case ecommunity_token_soo:
@ -553,7 +667,8 @@ struct ecommunity *ecommunity_str2com(const char *str, int type,
} }
keyword = 1; keyword = 1;
if (token == ecommunity_token_rt) { if (token == ecommunity_token_rt ||
token == ecommunity_token_rt6) {
type = ECOMMUNITY_ROUTE_TARGET; type = ECOMMUNITY_ROUTE_TARGET;
} }
if (token == ecommunity_token_soo) { if (token == ecommunity_token_soo) {
@ -563,7 +678,6 @@ struct ecommunity *ecommunity_str2com(const char *str, int type,
case ecommunity_token_val: case ecommunity_token_val:
if (keyword_included) { if (keyword_included) {
if (!keyword) { if (!keyword) {
if (ecom)
ecommunity_free(&ecom); ecommunity_free(&ecom);
return NULL; return NULL;
} }
@ -572,7 +686,24 @@ struct ecommunity *ecommunity_str2com(const char *str, int type,
if (ecom == NULL) if (ecom == NULL)
ecom = ecommunity_new(); ecom = ecommunity_new();
eval.val[1] = type; 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; break;
case ecommunity_token_unknown: case ecommunity_token_unknown:
default: default:
@ -584,16 +715,59 @@ struct ecommunity *ecommunity_str2com(const char *str, int type,
return ecom; return ecom;
} }
static int ecommunity_rt_soo_str(char *buf, size_t bufsz, const uint8_t *pnt, /* Convert string to extended community attribute.
int type, int sub_type, int format) *
* 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; int len = 0;
const char *prefix; const char *prefix;
char buf_local[INET6_ADDRSTRLEN];
/* For parse Extended Community attribute tupple. */ /* For parse Extended Community attribute tupple. */
struct ecommunity_as eas; struct ecommunity_as eas;
struct ecommunity_ip eip; struct ecommunity_ip eip;
struct ecommunity_ip6 eip6;
/* Determine prefix for string, if any. */ /* Determine prefix for string, if any. */
switch (format) { 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); len = snprintf(buf, bufsz, "%s%u:%u", prefix, eas.as, eas.val);
} else if (type == ECOMMUNITY_ENCODE_AS) { } else if (type == ECOMMUNITY_ENCODE_AS) {
if (ecom_size == ECOMMUNITY_SIZE) {
eas.as = (*pnt++ << 8); eas.as = (*pnt++ << 8);
eas.as |= (*pnt++); eas.as |= (*pnt++);
pnt = ptr_get_be32(pnt, &eas.val); 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) { } else if (type == ECOMMUNITY_ENCODE_IP) {
memcpy(&eip.ip, pnt, 4); memcpy(&eip.ip, pnt, 4);
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; 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) static int ecommunity_lb_str(char *buf, size_t bufsz, const uint8_t *pnt)
{ {
int len = 0; int len = 0;
@ -852,15 +1050,47 @@ char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter)
type == ECOMMUNITY_EXTENDED_COMMUNITY_PART_2 || type == ECOMMUNITY_EXTENDED_COMMUNITY_PART_2 ||
type == ECOMMUNITY_EXTENDED_COMMUNITY_PART_3) { type == ECOMMUNITY_EXTENDED_COMMUNITY_PART_3) {
sub_type = *pnt++; sub_type = *pnt++;
if (sub_type == ECOMMUNITY_REDIRECT_VRF) {
char buf[16] = {}; if (sub_type == ECOMMUNITY_ROUTE_TARGET) {
ecommunity_rt_soo_str( char buf[ECOMMUNITY_STRLEN];
buf, sizeof(buf), pnt,
type & ~ECOMMUNITY_ENCODE_TRANS_EXP, 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_ROUTE_TARGET,
ECOMMUNITY_FORMAT_DISPLAY); ECOMMUNITY_FORMAT_DISPLAY);
snprintf(encbuf, sizeof(encbuf), snprintf(encbuf, sizeof(encbuf),
"FS:redirect VRF %s", buf); "FS:redirect VRF %s", buf);
snprintf(encbuf, sizeof(encbuf),
"FS:redirect VRF %s", buf);
} else if (type != ECOMMUNITY_ENCODE_TRANS_EXP) } else if (type != ECOMMUNITY_ENCODE_TRANS_EXP)
unk_ecom = 1; unk_ecom = 1;
else if (sub_type == ECOMMUNITY_TRAFFIC_ACTION) { else if (sub_type == ECOMMUNITY_TRAFFIC_ACTION) {
@ -935,8 +1165,9 @@ bool ecommunity_match(const struct ecommunity *ecom1,
/* Every community on com2 needs to be on com1 for this to match */ /* Every community on com2 needs to be on com1 for this to match */
while (i < ecom1->size && j < ecom2->size) { while (i < ecom1->size && j < ecom2->size) {
if (memcmp(ecom1->val + i * ECOMMUNITY_SIZE, if (memcmp(ecom1->val + i * ecom1->unit_size,
ecom2->val + j * ECOMMUNITY_SIZE, ECOMMUNITY_SIZE) ecom2->val + j * ecom2->unit_size,
ecom2->unit_size)
== 0) == 0)
j++; j++;
i++; 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. */ /* If the value already exists in the structure return 0. */
c = 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) { if (p == NULL) {
continue; continue;
} }
@ -984,7 +1215,7 @@ bool ecommunity_strip(struct ecommunity *ecom, uint8_t type,
* multiple times, handle that. * multiple times, handle that.
*/ */
c = 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[0] == type && p[1] == subtype) if (p[0] == type && p[1] == subtype)
found++; found++;
} }
@ -1001,12 +1232,12 @@ bool ecommunity_strip(struct ecommunity *ecom, uint8_t type,
/* Strip matching ext community(ies). */ /* Strip matching ext community(ies). */
new = XMALLOC(MTYPE_ECOMMUNITY_VAL, new = XMALLOC(MTYPE_ECOMMUNITY_VAL,
(ecom->size - found) * ECOMMUNITY_SIZE); (ecom->size - found) * ecom->unit_size);
q = new; 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)) { if (!(p[0] == type && p[1] == subtype)) {
memcpy(q, p, ECOMMUNITY_SIZE); memcpy(q, p, ecom->unit_size);
q += ECOMMUNITY_SIZE; q += ecom->unit_size;
} }
} }
XFREE(MTYPE_ECOMMUNITY_VAL, ecom->val); 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) if (ecom == NULL || ecom->val == NULL)
return false; return false;
c = 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 (!memcmp(p, eval->val, ECOMMUNITY_SIZE)) { if (!memcmp(p, eval->val, ecom->unit_size)) {
found = 1; found = 1;
break; break;
} }
@ -1039,20 +1270,21 @@ bool ecommunity_del_val(struct ecommunity *ecom, struct ecommunity_val *eval)
/* Delete the selected value */ /* Delete the selected value */
ecom->size--; ecom->size--;
p = XMALLOC(MTYPE_ECOMMUNITY_VAL, ecom->size * ECOMMUNITY_SIZE); p = XMALLOC(MTYPE_ECOMMUNITY_VAL, ecom->size * ecom->unit_size);
if (c != 0) if (c != 0)
memcpy(p, ecom->val, c * ECOMMUNITY_SIZE); memcpy(p, ecom->val, c * ecom->unit_size);
if ((ecom->size - c) != 0) if ((ecom->size - c) != 0)
memcpy(p + (c)*ECOMMUNITY_SIZE, memcpy(p + (c)*ecom->unit_size,
ecom->val + (c + 1) * ECOMMUNITY_SIZE, ecom->val + (c + 1) * ecom->unit_size,
(ecom->size - c) * ECOMMUNITY_SIZE); (ecom->size - c) * ecom->unit_size);
XFREE(MTYPE_ECOMMUNITY_VAL, ecom->val); XFREE(MTYPE_ECOMMUNITY_VAL, ecom->val);
ecom->val = p; ecom->val = p;
return true; return true;
} }
int ecommunity_fill_pbr_action(struct ecommunity_val *ecom_eval, 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) { if (ecom_eval->val[1] == ECOMMUNITY_TRAFFIC_RATE) {
api->action = ACTION_TRAFFICRATE; 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) { } else if (ecom_eval->val[1] == ECOMMUNITY_REDIRECT_VRF) {
/* must use external function */ /* must use external function */
return 0; 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 /* see draft-ietf-idr-flowspec-redirect-ip-02
* Q1: how come a ext. community can host ipv6 address * Q1: how come a ext. community can host ipv6 address
* Q2 : from cisco documentation: * Q2 : from cisco documentation:

View File

@ -63,6 +63,10 @@
* 0x0c Flow-spec Redirect to IPv4 - draft-ietf-idr-flowspec-redirect * 0x0c Flow-spec Redirect to IPv4 - draft-ietf-idr-flowspec-redirect
*/ */
#define ECOMMUNITY_FLOWSPEC_REDIRECT_IPV4 0x0c #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 */ /* Low-order octet of the Extended Communities type field for EVPN types */
#define ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY 0x00 #define ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY 0x00
@ -90,6 +94,7 @@
/* Extended Communities value is eight octet long. */ /* Extended Communities value is eight octet long. */
#define ECOMMUNITY_SIZE 8 #define ECOMMUNITY_SIZE 8
#define IPV6_ECOMMUNITY_SIZE 20
/* Extended Communities type flag. */ /* Extended Communities type flag. */
#define ECOMMUNITY_FLAG_NON_TRANSITIVE 0x40 #define ECOMMUNITY_FLAG_NON_TRANSITIVE 0x40
@ -99,6 +104,11 @@ struct ecommunity {
/* Reference counter. */ /* Reference counter. */
unsigned long refcnt; 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. */ /* Size of Extended Communities attribute. */
int size; int size;
@ -119,11 +129,23 @@ struct ecommunity_ip {
uint16_t val; uint16_t val;
}; };
struct ecommunity_ip6 {
struct in6_addr ip;
uint16_t val;
};
/* Extended community value is eight octet. */ /* Extended community value is eight octet. */
struct ecommunity_val { struct ecommunity_val {
char val[ECOMMUNITY_SIZE]; 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. * Encode BGP Route Target AS:nn.
*/ */
@ -193,6 +215,8 @@ extern void ecommunity_init(void);
extern void ecommunity_finish(void); extern void ecommunity_finish(void);
extern void ecommunity_free(struct ecommunity **); extern void ecommunity_free(struct ecommunity **);
extern struct ecommunity *ecommunity_parse(uint8_t *, unsigned short); 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_dup(struct ecommunity *);
extern struct ecommunity *ecommunity_merge(struct ecommunity *, extern struct ecommunity *ecommunity_merge(struct ecommunity *,
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 void ecommunity_unintern(struct ecommunity **);
extern unsigned int ecommunity_hash_make(const void *); extern unsigned int ecommunity_hash_make(const void *);
extern struct ecommunity *ecommunity_str2com(const char *, int, int); 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 char *ecommunity_ecom2str(struct ecommunity *, int, int);
extern void ecommunity_strfree(char **s); extern void ecommunity_strfree(char **s);
extern bool ecommunity_match(const struct ecommunity *, 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 char *ecommunity_str(struct ecommunity *);
extern struct ecommunity_val *ecommunity_lookup(const struct ecommunity *, extern struct ecommunity_val *ecommunity_lookup(const struct ecommunity *,
uint8_t, uint8_t); uint8_t, uint8_t);
extern bool ecommunity_add_val(struct ecommunity *ecom, extern bool ecommunity_add_val(struct ecommunity *ecom,
struct ecommunity_val *eval, struct ecommunity_val *eval,
bool unique, bool overwrite); bool unique, bool overwrite);
extern bool ecommunity_add_val_ipv6(struct ecommunity *ecom,
struct ecommunity_val_ipv6 *eval,
bool unique, bool overwrite);
/* for vpn */ /* for vpn */
extern struct ecommunity *ecommunity_new(void); extern struct ecommunity *ecommunity_new(void);
@ -222,7 +252,8 @@ extern bool ecommunity_del_val(struct ecommunity *ecom,
struct ecommunity_val *eval); struct ecommunity_val *eval);
struct bgp_pbr_entry_action; struct bgp_pbr_entry_action;
extern int ecommunity_fill_pbr_action(struct ecommunity_val *ecom_eval, 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( extern void bgp_compute_aggregate_ecommunity(
struct bgp_aggregate *aggregate, struct bgp_aggregate *aggregate,

View File

@ -726,6 +726,7 @@ static void build_evpn_type5_route_extcomm(struct bgp *bgp_vrf,
memset(&ecom_encap, 0, sizeof(ecom_encap)); memset(&ecom_encap, 0, sizeof(ecom_encap));
encode_encap_extcomm(tnl_type, &eval); encode_encap_extcomm(tnl_type, &eval);
ecom_encap.size = 1; ecom_encap.size = 1;
ecom_encap.unit_size = ECOMMUNITY_SIZE;
ecom_encap.val = (uint8_t *)eval.val; ecom_encap.val = (uint8_t *)eval.val;
/* Add Encap */ /* 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)); memset(&ecom_encap, 0, sizeof(ecom_encap));
encode_encap_extcomm(tnl_type, &eval); encode_encap_extcomm(tnl_type, &eval);
ecom_encap.size = 1; ecom_encap.size = 1;
ecom_encap.unit_size = ECOMMUNITY_SIZE;
ecom_encap.val = (uint8_t *)eval.val; ecom_encap.val = (uint8_t *)eval.val;
/* Add Encap */ /* 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)); memset(&ecom_sticky, 0, sizeof(ecom_sticky));
encode_mac_mobility_extcomm(1, seqnum, &eval_sticky); encode_mac_mobility_extcomm(1, seqnum, &eval_sticky);
ecom_sticky.size = 1; ecom_sticky.size = 1;
ecom_sticky.unit_size = ECOMMUNITY_SIZE;
ecom_sticky.val = (uint8_t *)eval_sticky.val; ecom_sticky.val = (uint8_t *)eval_sticky.val;
attr->ecommunity = attr->ecommunity =
ecommunity_merge(attr->ecommunity, &ecom_sticky); 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)); memset(&ecom_default_gw, 0, sizeof(ecom_default_gw));
encode_default_gw_extcomm(&eval_default_gw); encode_default_gw_extcomm(&eval_default_gw);
ecom_default_gw.size = 1; ecom_default_gw.size = 1;
ecom_default_gw.unit_size = ECOMMUNITY_SIZE;
ecom_default_gw.val = (uint8_t *)eval_default_gw.val; ecom_default_gw.val = (uint8_t *)eval_default_gw.val;
attr->ecommunity = attr->ecommunity =
ecommunity_merge(attr->ecommunity, &ecom_default_gw); 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)); memset(&ecom_na, 0, sizeof(ecom_na));
encode_na_flag_extcomm(&eval_na, attr->router_flag, proxy); encode_na_flag_extcomm(&eval_na, attr->router_flag, proxy);
ecom_na.size = 1; ecom_na.size = 1;
ecom_na.unit_size = ECOMMUNITY_SIZE;
ecom_na.val = (uint8_t *)eval_na.val; ecom_na.val = (uint8_t *)eval_na.val;
attr->ecommunity = ecommunity_merge(attr->ecommunity, attr->ecommunity = ecommunity_merge(attr->ecommunity,
&ecom_na); &ecom_na);
@ -871,7 +876,8 @@ static void add_mac_mobility_to_attr(uint32_t seq_num, struct attr *attr)
if (attr->ecommunity) { if (attr->ecommunity) {
for (i = 0; i < attr->ecommunity->size; i++) { 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++; type = *pnt++;
sub_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 && sub_type
== ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY) { == ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY) {
ecom_val_ptr = ecom_val_ptr =
(attr->ecommunity->val + (i * 8)); (attr->ecommunity->val +
(i * attr->ecommunity->unit_size));
break; break;
} }
} }
@ -887,12 +894,14 @@ static void add_mac_mobility_to_attr(uint32_t seq_num, struct attr *attr)
/* Update the existing MM ecommunity */ /* Update the existing MM ecommunity */
if (ecom_val_ptr) { 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 */ /* Add MM to existing */
else { else {
memset(&ecom_tmp, 0, sizeof(ecom_tmp)); memset(&ecom_tmp, 0, sizeof(ecom_tmp));
ecom_tmp.size = 1; ecom_tmp.size = 1;
ecom_tmp.unit_size = ECOMMUNITY_SIZE;
ecom_tmp.val = (uint8_t *)eval.val; ecom_tmp.val = (uint8_t *)eval.val;
if (attr->ecommunity) if (attr->ecommunity)
@ -2713,9 +2722,9 @@ static int is_route_matching_for_vrf(struct bgp *bgp_vrf,
struct vrf_irt_node *irt; struct vrf_irt_node *irt;
/* Only deal with RTs */ /* Only deal with RTs */
pnt = (ecom->val + (i * ECOMMUNITY_SIZE)); pnt = (ecom->val + (i * ecom->unit_size));
eval = (struct ecommunity_val *)(ecom->val eval = (struct ecommunity_val *)(ecom->val
+ (i * ECOMMUNITY_SIZE)); + (i * ecom->unit_size));
type = *pnt++; type = *pnt++;
sub_type = *pnt++; sub_type = *pnt++;
if (sub_type != ECOMMUNITY_ROUTE_TARGET) 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 if (type == ECOMMUNITY_ENCODE_AS
|| type == ECOMMUNITY_ENCODE_AS4 || type == ECOMMUNITY_ENCODE_AS4
|| type == ECOMMUNITY_ENCODE_IP) { || type == ECOMMUNITY_ENCODE_IP) {
memcpy(&eval_tmp, eval, ECOMMUNITY_SIZE); memcpy(&eval_tmp, eval, ecom->unit_size);
mask_ecom_global_admin(&eval_tmp, eval); mask_ecom_global_admin(&eval_tmp, eval);
irt = lookup_vrf_import_rt(&eval_tmp); 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; struct irt_node *irt;
/* Only deal with RTs */ /* Only deal with RTs */
pnt = (ecom->val + (i * ECOMMUNITY_SIZE)); pnt = (ecom->val + (i * ecom->unit_size));
eval = (struct ecommunity_val *)(ecom->val eval = (struct ecommunity_val *)(ecom->val
+ (i * ECOMMUNITY_SIZE)); + (i * ecom->unit_size));
type = *pnt++; type = *pnt++;
sub_type = *pnt++; sub_type = *pnt++;
if (sub_type != ECOMMUNITY_ROUTE_TARGET) 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 if (type == ECOMMUNITY_ENCODE_AS
|| type == ECOMMUNITY_ENCODE_AS4 || type == ECOMMUNITY_ENCODE_AS4
|| type == ECOMMUNITY_ENCODE_IP) { || type == ECOMMUNITY_ENCODE_IP) {
memcpy(&eval_tmp, eval, ECOMMUNITY_SIZE); memcpy(&eval_tmp, eval, ecom->unit_size);
mask_ecom_global_admin(&eval_tmp, eval); mask_ecom_global_admin(&eval_tmp, eval);
irt = lookup_import_rt(bgp, &eval_tmp); 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; struct bgp_evpn_es *es;
/* Only deal with RTs */ /* Only deal with RTs */
pnt = (ecom->val + (i * ECOMMUNITY_SIZE)); pnt = (ecom->val + (i * ecom->unit_size));
eval = (struct ecommunity_val *)(ecom->val eval = (struct ecommunity_val *)(ecom->val
+ (i * ECOMMUNITY_SIZE)); + (i * ecom->unit_size));
type = *pnt++; type = *pnt++;
sub_type = *pnt++; sub_type = *pnt++;
if (sub_type != ECOMMUNITY_ROUTE_TARGET) 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 if (type == ECOMMUNITY_ENCODE_AS
|| type == ECOMMUNITY_ENCODE_AS4 || type == ECOMMUNITY_ENCODE_AS4
|| type == ECOMMUNITY_ENCODE_IP) { || type == ECOMMUNITY_ENCODE_IP) {
memcpy(&eval_tmp, eval, ECOMMUNITY_SIZE); memcpy(&eval_tmp, eval, ecom->unit_size);
mask_ecom_global_admin(&eval_tmp, eval); mask_ecom_global_admin(&eval_tmp, eval);
irt = lookup_import_rt(bgp, &eval_tmp); irt = lookup_import_rt(bgp, &eval_tmp);
vrf_irt = lookup_vrf_import_rt(&eval_tmp); vrf_irt = lookup_vrf_import_rt(&eval_tmp);

View File

@ -523,6 +523,7 @@ static void bgp_evpn_type4_route_extcomm_build(struct bgp_evpn_es *es,
memset(&ecom_encap, 0, sizeof(ecom_encap)); memset(&ecom_encap, 0, sizeof(ecom_encap));
encode_encap_extcomm(tnl_type, &eval); encode_encap_extcomm(tnl_type, &eval);
ecom_encap.size = 1; ecom_encap.size = 1;
ecom_encap.unit_size = ECOMMUNITY_SIZE;
ecom_encap.val = (uint8_t *)eval.val; ecom_encap.val = (uint8_t *)eval.val;
attr->ecommunity = ecommunity_dup(&ecom_encap); 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); es_get_system_mac(&es->esi, &mac);
encode_es_rt_extcomm(&eval_es_rt, &mac); encode_es_rt_extcomm(&eval_es_rt, &mac);
ecom_es_rt.size = 1; ecom_es_rt.size = 1;
ecom_es_rt.unit_size = ECOMMUNITY_SIZE;
ecom_es_rt.val = (uint8_t *)eval_es_rt.val; ecom_es_rt.val = (uint8_t *)eval_es_rt.val;
attr->ecommunity = attr->ecommunity =
ecommunity_merge(attr->ecommunity, &ecom_es_rt); 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)); memset(&ecom_encap, 0, sizeof(ecom_encap));
encode_encap_extcomm(tnl_type, &eval); encode_encap_extcomm(tnl_type, &eval);
ecom_encap.size = 1; ecom_encap.size = 1;
ecom_encap.unit_size = ECOMMUNITY_SIZE;
ecom_encap.val = (uint8_t *)eval.val; ecom_encap.val = (uint8_t *)eval.val;
attr->ecommunity = ecommunity_dup(&ecom_encap); 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, encode_esi_label_extcomm(&eval_esi_label,
false /*single_active*/); false /*single_active*/);
ecom_esi_label.size = 1; ecom_esi_label.size = 1;
ecom_esi_label.unit_size = ECOMMUNITY_SIZE;
ecom_esi_label.val = (uint8_t *)eval_esi_label.val; ecom_esi_label.val = (uint8_t *)eval_esi_label.val;
attr->ecommunity = attr->ecommunity =
ecommunity_merge(attr->ecommunity, &ecom_esi_label); 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)); memset(&ecom_encap, 0, sizeof(ecom_encap));
encode_encap_extcomm(tnl_type, &eval); encode_encap_extcomm(tnl_type, &eval);
ecom_encap.size = 1; ecom_encap.size = 1;
ecom_encap.unit_size = ECOMMUNITY_SIZE;
ecom_encap.val = (uint8_t *)eval.val; ecom_encap.val = (uint8_t *)eval.val;
attr->ecommunity = ecommunity_dup(&ecom_encap); attr->ecommunity = ecommunity_dup(&ecom_encap);

View File

@ -33,7 +33,8 @@
#include "bgpd/bgp_debug.h" #include "bgpd/bgp_debug.h"
#include "bgpd/bgp_errors.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; uint32_t offset = 0;
int type; int type;
@ -47,6 +48,14 @@ static int bgp_fs_nlri_validate(uint8_t *nlri_content, uint32_t len)
case FLOWSPEC_SRC_PREFIX: case FLOWSPEC_SRC_PREFIX:
ret = bgp_flowspec_ip_address( ret = bgp_flowspec_ip_address(
BGP_FLOWSPEC_VALIDATE_ONLY, BGP_FLOWSPEC_VALIDATE_ONLY,
nlri_content + offset,
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, nlri_content + offset,
len - offset, NULL, &error); len - offset, NULL, &error);
break; break;
@ -103,11 +112,6 @@ int bgp_nlri_parse_flowspec(struct peer *peer, struct attr *attr,
afi = packet->afi; afi = packet->afi;
safi = packet->safi; 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) { if (packet->length >= FLOWSPEC_NLRI_SIZELIMIT_EXTENDED) {
flog_err(EC_BGP_FLOWSPEC_PACKET, flog_err(EC_BGP_FLOWSPEC_PACKET,
"BGP flowspec nlri length maximum reached (%u)", "BGP flowspec nlri length maximum reached (%u)",
@ -137,7 +141,7 @@ int bgp_nlri_parse_flowspec(struct peer *peer, struct attr *attr,
psize); psize);
return BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW; 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( flog_err(
EC_BGP_FLOWSPEC_PACKET, EC_BGP_FLOWSPEC_PACKET,
"Bad flowspec format or NLRI options not supported"); "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; p.prefixlen = 0;
/* Flowspec encoding is in bytes */ /* Flowspec encoding is in bytes */
p.u.prefix_flowspec.prefixlen = psize; p.u.prefix_flowspec.prefixlen = psize;
p.u.prefix_flowspec.family = afi2family(afi);
temp = XCALLOC(MTYPE_TMP, psize); temp = XCALLOC(MTYPE_TMP, psize);
memcpy(temp, pnt, psize); memcpy(temp, pnt, psize);
p.u.prefix_flowspec.ptr = (uintptr_t) temp; 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.ptr,
p.u.prefix_flowspec.prefixlen, p.u.prefix_flowspec.prefixlen,
return_string, return_string,
NLRI_STRING_FORMAT_MIN, NULL); NLRI_STRING_FORMAT_MIN, NULL,
afi);
snprintf(ec_string, sizeof(ec_string), snprintf(ec_string, sizeof(ec_string),
"EC{none}"); "EC{none}");
if (attr && attr->ecommunity) { if (attr && attr->ecommunity) {

View File

@ -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, extern void bgp_fs_nlri_get_string(unsigned char *nlri_content, size_t len,
char *return_string, int format, 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, extern void route_vty_out_flowspec(struct vty *vty, const struct prefix *p,
struct bgp_path_info *path, int display, struct bgp_path_info *path, int display,

View File

@ -41,5 +41,6 @@
#define FLOWSPEC_PKT_LEN 10 #define FLOWSPEC_PKT_LEN 10
#define FLOWSPEC_DSCP 11 #define FLOWSPEC_DSCP 11
#define FLOWSPEC_FRAGMENT 12 #define FLOWSPEC_FRAGMENT 12
#define FLOWSPEC_FLOW_LABEL 13 /* For IPv6 only */
#endif /* _FRR_BGP_FLOWSPEC_PRIVATE_H */ #endif /* _FRR_BGP_FLOWSPEC_PRIVATE_H */

View File

@ -76,6 +76,7 @@ static int bgp_flowspec_call_non_opaque_decode(uint8_t *nlri_content, int len,
return ret; return ret;
} }
bool bgp_flowspec_contains_prefix(const struct prefix *pfs, bool bgp_flowspec_contains_prefix(const struct prefix *pfs,
struct prefix *input, int prefix_check) struct prefix *input, int prefix_check)
{ {
@ -84,6 +85,7 @@ bool bgp_flowspec_contains_prefix(const struct prefix *pfs,
int ret = 0, error = 0; int ret = 0, error = 0;
uint8_t *nlri_content = (uint8_t *)pfs->u.prefix_flowspec.ptr; uint8_t *nlri_content = (uint8_t *)pfs->u.prefix_flowspec.ptr;
size_t len = pfs->u.prefix_flowspec.prefixlen; size_t len = pfs->u.prefix_flowspec.prefixlen;
afi_t afi = family2afi(pfs->u.prefix_flowspec.family);
struct prefix compare; struct prefix compare;
error = 0; error = 0;
@ -98,7 +100,8 @@ bool bgp_flowspec_contains_prefix(const struct prefix *pfs,
BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE, BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE,
nlri_content+offset, nlri_content+offset,
len - offset, len - offset,
&compare, &error); &compare, &error,
afi, NULL);
if (ret <= 0) if (ret <= 0)
break; break;
if (prefix_check && if (prefix_check &&
@ -115,6 +118,16 @@ bool bgp_flowspec_contains_prefix(const struct prefix *pfs,
&compare.u.prefix6.s6_addr)) &compare.u.prefix6.s6_addr))
return true; return true;
break; 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_IP_PROTOCOL:
case FLOWSPEC_PORT: case FLOWSPEC_PORT:
case FLOWSPEC_DEST_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, int bgp_flowspec_ip_address(enum bgp_flowspec_util_nlri_t type,
uint8_t *nlri_ptr, uint8_t *nlri_ptr,
uint32_t max_len, 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 */ char *display = (char *)result; /* for return_string */
struct prefix *prefix = (struct prefix *)result; struct prefix *prefix = (struct prefix *)result;
uint32_t offset = 0; uint32_t offset = 0;
struct prefix prefix_local; struct prefix prefix_local;
int psize; int psize;
uint8_t prefix_offset = 0;
*error = 0; *error = 0;
memset(&prefix_local, 0, sizeof(struct prefix)); 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]; prefix_local.prefixlen = nlri_ptr[offset];
psize = PSIZE(prefix_local.prefixlen); psize = PSIZE(prefix_local.prefixlen);
offset++; offset++;
/* TODO Flowspec IPv6 Support */ prefix_local.family = afi2family(afi);
prefix_local.family = AF_INET; if (prefix_local.family == AF_INET6) {
prefix_offset = nlri_ptr[offset];
if (ipv6_offset)
*ipv6_offset = prefix_offset;
offset++;
}
/* Prefix length check. */ /* Prefix length check. */
if (prefix_local.prefixlen > prefix_blen(&prefix_local) * 8) if (prefix_local.prefixlen > prefix_blen(&prefix_local) * 8)
*error = -1; *error = -1;
@ -189,11 +210,30 @@ int bgp_flowspec_ip_address(enum bgp_flowspec_util_nlri_t type,
offset += psize; offset += psize;
switch (type) { switch (type) {
case BGP_FLOWSPEC_RETURN_STRING: case BGP_FLOWSPEC_RETURN_STRING:
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, prefix2str(&prefix_local, display,
BGP_FLOWSPEC_STRING_DISPLAY_MAX); BGP_FLOWSPEC_STRING_DISPLAY_MAX);
break; break;
case BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE: case BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE:
if (prefix) {
if (prefix_local.family == AF_INET)
PREFIX_COPY_IPV4(prefix, &prefix_local) PREFIX_COPY_IPV4(prefix, &prefix_local)
else
PREFIX_COPY_IPV6(prefix, &prefix_local)
}
break; break;
case BGP_FLOWSPEC_VALIDATE_ONLY: case BGP_FLOWSPEC_VALIDATE_ONLY:
default: 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, 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; int offset = 0, error = 0;
struct prefix *prefix; 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 *match_num;
uint8_t bitmask = 0; uint8_t bitmask = 0;
int ret = 0, type; int ret = 0, type;
uint8_t *prefix_offset;
while (offset < len - 1 && error >= 0) { while (offset < len - 1 && error >= 0) {
type = nlri_content[offset]; 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) { if (type == FLOWSPEC_DEST_PREFIX) {
bitmask |= PREFIX_DST_PRESENT; bitmask |= PREFIX_DST_PRESENT;
prefix = &bpem->dst_prefix; prefix = &bpem->dst_prefix;
prefix_offset = &bpem->dst_prefix_offset;
} else { } else {
bitmask |= PREFIX_SRC_PRESENT; bitmask |= PREFIX_SRC_PRESENT;
prefix = &bpem->src_prefix; prefix = &bpem->src_prefix;
prefix_offset = &bpem->src_prefix_offset;
} }
ret = bgp_flowspec_ip_address( ret = bgp_flowspec_ip_address(
BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE, BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE,
nlri_content + offset, nlri_content + offset,
len - offset, len - offset,
prefix, &error); prefix, &error,
afi, prefix_offset);
if (error < 0) if (error < 0)
flog_err(EC_BGP_FLOWSPEC_PACKET, flog_err(EC_BGP_FLOWSPEC_PACKET,
"%s: flowspec_ip_address error %d", "%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 if (prefix->family == AF_INET
&& prefix->u.prefix4.s_addr == INADDR_ANY) && prefix->u.prefix4.s_addr == INADDR_ANY)
bpem->match_bitmask_iprule |= bitmask; 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 else
bpem->match_bitmask |= bitmask; bpem->match_bitmask |= bitmask;
} }
offset += ret; offset += ret;
break; 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: case FLOWSPEC_IP_PROTOCOL:
match_num = &(bpem->match_protocol_num); match_num = &(bpem->match_protocol_num);
mval = (struct bgp_pbr_match_val *) 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_packet_length_num || bpem->match_icmp_code_num ||
bpem->match_icmp_type_num || bpem->match_port_num || bpem->match_icmp_type_num || bpem->match_port_num ||
bpem->match_src_port_num || bpem->match_dst_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; bpem->type = BGP_PBR_IPSET;
else if ((bpem->match_bitmask_iprule & PREFIX_SRC_PRESENT) || else if ((bpem->match_bitmask_iprule & PREFIX_SRC_PRESENT) ||
(bpem->match_bitmask_iprule & PREFIX_DST_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 */ /* return 1 if FS entry invalid or no NH IP */
bool bgp_flowspec_get_first_nh(struct bgp *bgp, struct bgp_path_info *pi, 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; struct bgp_pbr_entry_main api;
int i; 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]; api_action = &api.actions[i];
if (api_action->action != ACTION_REDIRECT_IP) if (api_action->action != ACTION_REDIRECT_IP)
continue; continue;
p->family = AF_INET; p->family = afi2family(afi);
if (afi == AFI_IP) {
p->prefixlen = IPV4_MAX_BITLEN; p->prefixlen = IPV4_MAX_BITLEN;
p->u.prefix4 = api_action->u.zr.redirect_ip_v4; 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 false;
} }
return true; return true;

View File

@ -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, extern int bgp_flowspec_ip_address(enum bgp_flowspec_util_nlri_t type,
uint8_t *nlri_ptr, uint8_t *nlri_ptr,
uint32_t max_len, 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, extern int bgp_flowspec_bitmask_decode(enum bgp_flowspec_util_nlri_t type,
uint8_t *nlri_ptr, 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; struct bgp_pbr_entry_main;
extern int bgp_flowspec_match_rules_fill(uint8_t *nlri_content, int len, 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, extern bool bgp_flowspec_contains_prefix(const struct prefix *pfs,
struct prefix *input, struct prefix *input,
int prefix_check); int prefix_check);
extern bool bgp_flowspec_get_first_nh(struct bgp *bgp, struct bgp_path_info *pi, 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 */ #endif /* _FRR_BGP_FLOWSPEC_UTIL_H */

View File

@ -50,6 +50,7 @@ static const struct message bgp_flowspec_display_large[] = {
{FLOWSPEC_PKT_LEN, "Packet Length"}, {FLOWSPEC_PKT_LEN, "Packet Length"},
{FLOWSPEC_DSCP, "DSCP field"}, {FLOWSPEC_DSCP, "DSCP field"},
{FLOWSPEC_FRAGMENT, "Packet Fragment"}, {FLOWSPEC_FRAGMENT, "Packet Fragment"},
{FLOWSPEC_FLOW_LABEL, "Packet Flow Label"},
{0} {0}
}; };
@ -66,6 +67,7 @@ static const struct message bgp_flowspec_display_min[] = {
{FLOWSPEC_PKT_LEN, "pktlen"}, {FLOWSPEC_PKT_LEN, "pktlen"},
{FLOWSPEC_DSCP, "dscp"}, {FLOWSPEC_DSCP, "dscp"},
{FLOWSPEC_FRAGMENT, "pktfrag"}, {FLOWSPEC_FRAGMENT, "pktfrag"},
{FLOWSPEC_FLOW_LABEL, "flwlbl"},
{0} {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, void bgp_fs_nlri_get_string(unsigned char *nlri_content, size_t len,
char *return_string, int format, char *return_string, int format,
json_object *json_path) json_object *json_path,
afi_t afi)
{ {
uint32_t offset = 0; uint32_t offset = 0;
int type; int type;
@ -127,7 +130,8 @@ void bgp_fs_nlri_get_string(unsigned char *nlri_content, size_t len,
type_util, type_util,
nlri_content+offset, nlri_content+offset,
len - offset, len - offset,
local_string, &error); local_string, &error,
afi, NULL);
if (ret <= 0) if (ret <= 0)
break; break;
if (json_path) { if (json_path) {
@ -145,6 +149,7 @@ void bgp_fs_nlri_get_string(unsigned char *nlri_content, size_t len,
len_string -= len_written; len_string -= len_written;
ptr += len_written; ptr += len_written;
break; break;
case FLOWSPEC_FLOW_LABEL:
case FLOWSPEC_IP_PROTOCOL: case FLOWSPEC_IP_PROTOCOL:
case FLOWSPEC_PORT: case FLOWSPEC_PORT:
case FLOWSPEC_DEST_PORT: case FLOWSPEC_DEST_PORT:
@ -258,15 +263,18 @@ void route_vty_out_flowspec(struct vty *vty, const struct prefix *p,
{ {
struct attr *attr; struct attr *attr;
char return_string[BGP_FLOWSPEC_STRING_DISPLAY_MAX]; char return_string[BGP_FLOWSPEC_STRING_DISPLAY_MAX];
char *s; char *s1 = NULL, *s2 = NULL;
json_object *json_nlri_path = NULL; json_object *json_nlri_path = NULL;
json_object *json_ecom_path = NULL; json_object *json_ecom_path = NULL;
json_object *json_time_path = NULL; json_object *json_time_path = NULL;
char timebuf[BGP_UPTIME_LEN]; char timebuf[BGP_UPTIME_LEN];
struct bgp_dest *dest = NULL;
/* Print prefix */ if (path)
if (p != NULL) { dest = path->net;
if (p->family != AF_FLOWSPEC) if (dest)
bgp_dest_get_bgp_table_info(dest);
if (p == NULL || p->family != AF_FLOWSPEC)
return; return;
if (json_paths) { if (json_paths) {
if (display == NLRI_STRING_FORMAT_JSON) if (display == NLRI_STRING_FORMAT_JSON)
@ -282,7 +290,9 @@ void route_vty_out_flowspec(struct vty *vty, const struct prefix *p,
p->u.prefix_flowspec.prefixlen, p->u.prefix_flowspec.prefixlen,
return_string, return_string,
display, display,
json_nlri_path); json_nlri_path,
family2afi(p->u.prefix_flowspec
.family));
if (display == NLRI_STRING_FORMAT_LARGE) if (display == NLRI_STRING_FORMAT_LARGE)
vty_out(vty, "%s", return_string); vty_out(vty, "%s", return_string);
else if (display == NLRI_STRING_FORMAT_DEBUG) else if (display == NLRI_STRING_FORMAT_DEBUG)
@ -291,33 +301,61 @@ void route_vty_out_flowspec(struct vty *vty, const struct prefix *p,
vty_out(vty, " %-30s", return_string); vty_out(vty, " %-30s", return_string);
else if (json_paths && display == NLRI_STRING_FORMAT_JSON) else if (json_paths && display == NLRI_STRING_FORMAT_JSON)
json_object_array_add(json_paths, json_nlri_path); json_object_array_add(json_paths, json_nlri_path);
}
if (!path) if (!path)
return; return;
if (path->attr->ecommunity) { if (path->attr &&
(path->attr->ecommunity || path->attr->ipv6_ecommunity)) {
/* Print attribute */ /* Print attribute */
attr = path->attr; attr = path->attr;
s = ecommunity_ecom2str(attr->ecommunity, if (attr->ecommunity)
s1 = ecommunity_ecom2str(attr->ecommunity,
ECOMMUNITY_FORMAT_ROUTE_MAP, 0); ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
if (!s) if (attr->ipv6_ecommunity)
s2 = ecommunity_ecom2str(attr->ipv6_ecommunity,
ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
if (!s1 && !s2)
return; return;
if (display == NLRI_STRING_FORMAT_LARGE) 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) 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) { else if (json_paths) {
json_ecom_path = json_object_new_object(); json_ecom_path = json_object_new_object();
if (s1)
json_object_string_add(json_ecom_path, json_object_string_add(json_ecom_path,
"ecomlist", s); "ecomlist", s1);
if (s2)
json_object_string_add(json_ecom_path,
"ecom6list", s2);
if (display == NLRI_STRING_FORMAT_JSON) if (display == NLRI_STRING_FORMAT_JSON)
json_object_array_add(json_paths, json_object_array_add(json_paths,
json_ecom_path); json_ecom_path);
} }
if (attr->nexthop.s_addr != 0 && if (display == NLRI_STRING_FORMAT_LARGE) {
display == NLRI_STRING_FORMAT_LARGE) char local_buff[INET6_ADDRSTRLEN];
vty_out(vty, "\tNLRI NH %-16s\n",
inet_ntoa(attr->nexthop)); local_buff[0] = '\0';
XFREE(MTYPE_ECOMMUNITY_STR, s); 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); peer_uptime(path->uptime, timebuf, BGP_UPTIME_LEN, 0, NULL);
if (display == NLRI_STRING_FORMAT_LARGE) { 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; struct bgp_pbr_interface_head *head;
bool bgp_pbr_interface_any; bool bgp_pbr_interface_any;
if (!bgp_pbr_cfg || safi != SAFI_FLOWSPEC || afi != AFI_IP) if (!bgp_pbr_cfg || safi != SAFI_FLOWSPEC)
return 0; return 0;
if (afi == AFI_IP) {
head = &(bgp_pbr_cfg->ifaces_by_name_ipv4); head = &(bgp_pbr_cfg->ifaces_by_name_ipv4);
bgp_pbr_interface_any = bgp_pbr_cfg->pbr_interface_any_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) || if (!RB_EMPTY(bgp_pbr_interface_head, head) ||
!bgp_pbr_interface_any) !bgp_pbr_interface_any)
declare_node = true; 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, 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_interface *pbr_if;
struct bgp_pbr_config *bgp_pbr_cfg = bgp->bgp_pbr_cfg; 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) if (!bgp_pbr_cfg)
return CMD_SUCCESS; return CMD_SUCCESS;
if (afi == AFI_IP) {
head = &(bgp_pbr_cfg->ifaces_by_name_ipv4); head = &(bgp_pbr_cfg->ifaces_by_name_ipv4);
bgp_pbr_interface_any = &(bgp_pbr_cfg->pbr_interface_any_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 (no) {
if (!ifname) { if (!ifname) {
if (*bgp_pbr_interface_any) { if (*bgp_pbr_interface_any) {
*bgp_pbr_interface_any = false; *bgp_pbr_interface_any = false;
/* remove all other interface list */ /* remove all other interface list */
bgp_pbr_reset(bgp, AFI_IP); bgp_pbr_reset(bgp, afi);
} }
return CMD_SUCCESS; return CMD_SUCCESS;
} }
@ -523,7 +574,7 @@ static int bgp_fs_local_install_interface(struct bgp *bgp,
if (!*bgp_pbr_interface_any) { if (!*bgp_pbr_interface_any) {
/* remove all other interface list /* remove all other interface list
*/ */
bgp_pbr_reset(bgp, AFI_IP); bgp_pbr_reset(bgp, afi);
*bgp_pbr_interface_any = true; *bgp_pbr_interface_any = true;
} }
} }
@ -543,7 +594,8 @@ DEFUN (bgp_fs_local_install_ifname,
char *ifname = argv_find(argv, argc, "INTERFACE", &idx) ? char *ifname = argv_find(argv, argc, "INTERFACE", &idx) ?
argv[idx]->arg : NULL; 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, 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(ENABLE_NODE, &no_debug_bgp_flowspec_cmd);
install_element(CONFIG_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_FLOWSPECV4_NODE, &bgp_fs_local_install_ifname_cmd);
install_element(BGP_FLOWSPECV6_NODE, &bgp_fs_local_install_ifname_cmd);
} }

View File

@ -391,12 +391,11 @@ static bool ecom_intersect(struct ecommunity *e1, struct ecommunity *e2)
if (!e1 || !e2) if (!e1 || !e2)
return false; return false;
for (i = 0; i < e1->size; ++i) { for (i = 0; i < e1->size; ++i) {
for (j = 0; j < e2->size; ++j) { for (j = 0; j < e2->size; ++j) {
if (!memcmp(e1->val + (i * ECOMMUNITY_SIZE), if (!memcmp(e1->val + (i * e1->unit_size),
e2->val + (j * ECOMMUNITY_SIZE), e2->val + (j * e2->unit_size),
ECOMMUNITY_SIZE)) { e1->unit_size)) {
return true; return true;
} }
@ -2456,6 +2455,10 @@ vrf_id_t get_first_vrf_for_redirect_with_rt(struct ecommunity *eckey)
{ {
struct listnode *mnode, *mnnode; struct listnode *mnode, *mnnode;
struct bgp *bgp; 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)) { for (ALL_LIST_ELEMENTS(bm->bgp, mnode, mnnode, bgp)) {
struct ecommunity *ec; 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) if (bgp->inst_type != BGP_INSTANCE_TYPE_VRF)
continue; 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)) if (ecom_intersect(ec, eckey))
return bgp->vrf_id; return bgp->vrf_id;

View File

@ -544,7 +544,7 @@ static int make_prefix(int afi, struct bgp_path_info *pi, struct prefix *p)
if (!pi->peer) if (!pi->peer)
return -1; return -1;
return bgp_flowspec_get_first_nh(pi->peer->bgp, return bgp_flowspec_get_first_nh(pi->peer->bgp,
pi, p); pi, p, afi);
} }
memset(p, 0, sizeof(struct prefix)); memset(p, 0, sizeof(struct prefix));
switch (afi) { switch (afi) {

View File

@ -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, "BGP PBR Context")
DEFINE_MTYPE_STATIC(BGPD, PBR_VALMASK, "BGP PBR Val Mask Value") 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, RB_GENERATE(bgp_pbr_interface_head, bgp_pbr_interface,
id_entry, bgp_pbr_interface_compare); id_entry, bgp_pbr_interface_compare);
struct bgp_pbr_interface_head ifaces_by_name_ipv4 = 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; return HASHWALK_CONTINUE;
} }
static int sprintf_bgp_pbr_match_val(char *str, struct bgp_pbr_match_val *mval, static int snprintf_bgp_pbr_match_val(char *str, int len,
struct bgp_pbr_match_val *mval,
const char *prepend) const char *prepend)
{ {
char *ptr = str; char *ptr = str;
int delta;
if (prepend) if (prepend) {
ptr += sprintf(ptr, "%s", prepend); delta = snprintf(ptr, len, "%s", prepend);
else { ptr += delta;
if (mval->unary_operator & OPERATOR_UNARY_OR) len -= delta;
ptr += sprintf(ptr, ", or "); } else {
if (mval->unary_operator & OPERATOR_UNARY_AND) if (mval->unary_operator & OPERATOR_UNARY_OR) {
ptr += sprintf(ptr, ", and "); delta = snprintf(ptr, len, ", or ");
ptr += delta;
len -= delta;
} }
if (mval->compare_operator & OPERATOR_COMPARE_LESS_THAN) if (mval->unary_operator & OPERATOR_UNARY_AND) {
ptr += sprintf(ptr, "<"); delta = snprintf(ptr, len, ", and ");
if (mval->compare_operator & OPERATOR_COMPARE_GREATER_THAN) ptr += delta;
ptr += sprintf(ptr, ">"); len -= delta;
if (mval->compare_operator & OPERATOR_COMPARE_EQUAL_TO) }
ptr += sprintf(ptr, "="); }
if (mval->compare_operator & OPERATOR_COMPARE_EXACT_MATCH) if (mval->compare_operator & OPERATOR_COMPARE_LESS_THAN) {
ptr += sprintf(ptr, "match"); delta = snprintf(ptr, len, "<");
ptr += sprintf(ptr, " %u", mval->value); 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); return (int)(ptr - str);
} }
#define INCREMENT_DISPLAY(_ptr, _cnt) do { \ #define INCREMENT_DISPLAY(_ptr, _cnt, _len) do { \
if (_cnt) \ int sn_delta; \
(_ptr) += sprintf((_ptr), "; "); \ \
_cnt++; \ if (_cnt) { \
sn_delta = snprintf((_ptr), (_len), "; ");\
(_len) -= sn_delta; \
(_ptr) += sn_delta; \
} \
(_cnt)++; \
} while (0) } while (0)
/* this structure can be used for port range, /* this structure can be used for port range,
@ -222,6 +253,7 @@ struct bgp_pbr_val_mask {
struct bgp_pbr_filter { struct bgp_pbr_filter {
uint8_t type; uint8_t type;
vrf_id_t vrf_id; vrf_id_t vrf_id;
uint8_t family;
struct prefix *src; struct prefix *src;
struct prefix *dst; struct prefix *dst;
uint8_t bitmask_iprule; uint8_t bitmask_iprule;
@ -231,6 +263,7 @@ struct bgp_pbr_filter {
struct bgp_pbr_range_port *dst_port; struct bgp_pbr_range_port *dst_port;
struct bgp_pbr_val_mask *tcp_flags; struct bgp_pbr_val_mask *tcp_flags;
struct bgp_pbr_val_mask *dscp; 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 *pkt_len_val;
struct bgp_pbr_val_mask *fragment; struct bgp_pbr_val_mask *fragment;
}; };
@ -242,6 +275,7 @@ struct bgp_pbr_filter {
struct bgp_pbr_or_filter { struct bgp_pbr_or_filter {
struct list *tcpflags; struct list *tcpflags;
struct list *dscp; struct list *dscp;
struct list *flowlabel;
struct list *pkt_len; struct list *pkt_len;
struct list *fragment; struct list *fragment;
struct list *icmp_type; struct list *icmp_type;
@ -268,6 +302,7 @@ static bool bgp_pbr_extract_enumerate_unary_opposite(
TCP_HEADER_ALL_FLAGS & TCP_HEADER_ALL_FLAGS &
~(value); ~(value);
} else if (type_entry == FLOWSPEC_DSCP || } else if (type_entry == FLOWSPEC_DSCP ||
type_entry == FLOWSPEC_FLOW_LABEL ||
type_entry == FLOWSPEC_PKT_LEN || type_entry == FLOWSPEC_PKT_LEN ||
type_entry == FLOWSPEC_FRAGMENT) { type_entry == FLOWSPEC_FRAGMENT) {
and_valmask->val = value; and_valmask->val = value;
@ -282,6 +317,7 @@ static bool bgp_pbr_extract_enumerate_unary_opposite(
TCP_HEADER_ALL_FLAGS & TCP_HEADER_ALL_FLAGS &
~(value); ~(value);
} else if (type_entry == FLOWSPEC_DSCP || } else if (type_entry == FLOWSPEC_DSCP ||
type_entry == FLOWSPEC_FLOW_LABEL ||
type_entry == FLOWSPEC_FRAGMENT || type_entry == FLOWSPEC_FRAGMENT ||
type_entry == FLOWSPEC_PKT_LEN) { type_entry == FLOWSPEC_PKT_LEN) {
and_valmask->val = value; 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 : FIN and SYN -> val = ALL; mask = 3
* TCP : not (FIN and SYN) -> val = ALL; mask = ALL & ~(FIN|RST) * 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 * - value is copied in bgp_pbr_val_mask->val value
* - if negate form is identifierd, bgp_pbr_val_mask->mask set to 1 * - 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 |= and_valmask->mask |=
TCP_HEADER_ALL_FLAGS & list[i].value; TCP_HEADER_ALL_FLAGS & list[i].value;
} else if (type_entry == FLOWSPEC_DSCP || } else if (type_entry == FLOWSPEC_DSCP ||
type_entry == FLOWSPEC_FLOW_LABEL ||
type_entry == FLOWSPEC_ICMP_TYPE || type_entry == FLOWSPEC_ICMP_TYPE ||
type_entry == FLOWSPEC_ICMP_CODE || type_entry == FLOWSPEC_ICMP_CODE ||
type_entry == FLOWSPEC_FRAGMENT || 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); api->match_protocol_num);
return 0; 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 && if (api->match_protocol_num == 1 &&
api->protocol[0].value != PROTOCOL_UDP && api->protocol[0].value != PROTOCOL_UDP &&
api->protocol[0].value != PROTOCOL_ICMP && api->protocol[0].value != PROTOCOL_ICMP &&
api->protocol[0].value != PROTOCOL_ICMPV6 &&
api->protocol[0].value != PROTOCOL_TCP) { api->protocol[0].value != PROTOCOL_TCP) {
if (BGP_DEBUG(pbr, PBR)) if (BGP_DEBUG(pbr, PBR))
zlog_debug("BGP: match protocol operations:protocol (%d) not supported. ignoring", 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; 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) { if (api->match_fragment_num) {
char fail_str[64]; char fail_str[64];
bool success; 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", "Value not valid (%d) for this implementation",
api->fragment[i].value); 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 } else
snprintf(fail_str, sizeof(fail_str), 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 (api->actions[i].action == ACTION_MARKING) {
if (BGP_DEBUG(pbr, PBR)) { if (BGP_DEBUG(pbr, PBR)) {
bgp_pbr_print_policy_route(api); 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); 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 */ /* return -1 if build or validation failed */
int bgp_pbr_build_and_validate_entry(const struct prefix *p, int bgp_pbr_build_and_validate_entry(const struct prefix *p,
struct bgp_path_info *path, struct bgp_path_info *path,
struct bgp_pbr_entry_main *api) 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 bgp_pbr_entry_action *api_action;
struct prefix *src = NULL, *dst = NULL; struct prefix *src = NULL, *dst = NULL;
int valid_prefix = 0; int valid_prefix = 0;
afi_t afi = AFI_IP;
struct bgp_pbr_entry_action *api_action_redirect_ip = NULL; struct bgp_pbr_entry_action *api_action_redirect_ip = NULL;
bool discard_action_found = false; bool discard_action_found = false;
afi_t afi = family2afi(p->u.prefix_flowspec.family);
/* extract match from flowspec entries */ /* extract match from flowspec entries */
ret = bgp_flowspec_match_rules_fill((uint8_t *)p->u.prefix_flowspec.ptr, 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) if (ret < 0)
return -1; return -1;
/* extract actiosn from flowspec ecom list */ /* 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)) if (BGP_DEBUG(pbr, PBR_ERROR))
flog_err( flog_err(
EC_BGP_FLOWSPEC_PACKET, EC_BGP_FLOWSPEC_PACKET,
"%s: flowspec actions exceeds limit (max %u)", "%s: %s (max %u)",
__func__, action_count); __func__,
FSPEC_ACTION_EXCEED_LIMIT,
action_count);
break; break;
} }
api_action = &api->actions[action_count - 1]; 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 * do not overwrite
* draft-ietf-idr-flowspec-redirect * draft-ietf-idr-flowspec-redirect
*/ */
if (api_action_redirect_ip) { if (api_action_redirect_ip &&
if (api_action_redirect_ip->u.zr p->u.prefix_flowspec.family == AF_INET) {
.redirect_ip_v4.s_addr if (api_action_redirect_ip->u
.zr.redirect_ip_v4.s_addr
!= INADDR_ANY) != INADDR_ANY)
continue; continue;
if (path->attr->nexthop.s_addr 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 api_action_redirect_ip->u.zr.duplicate
= ecom_eval->val[7]; = ecom_eval->val[7];
continue; 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->action = ACTION_REDIRECT_IP;
api_action->u.zr.redirect_ip_v4.s_addr = api_action->u.zr.redirect_ip_v4.s_addr =
path->attr->nexthop.s_addr; path->attr->nexthop.s_addr;
api_action->u.zr.duplicate = api_action->u.zr.duplicate =
ecom_eval->val[7]; ecom_eval->val[7];
api_action_redirect_ip = api_action; 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] == } else if ((ecom_eval->val[0] ==
(char)ECOMMUNITY_ENCODE_IP) && (char)ECOMMUNITY_ENCODE_IP) &&
@ -789,16 +902,56 @@ int bgp_pbr_build_and_validate_entry(const struct prefix *p,
(char)ECOMMUNITY_ENCODE_TRANS_EXP) (char)ECOMMUNITY_ENCODE_TRANS_EXP)
continue; continue;
ret = ecommunity_fill_pbr_action(ecom_eval, ret = ecommunity_fill_pbr_action(ecom_eval,
api_action); api_action,
afi);
if (ret != 0) if (ret != 0)
continue; continue;
if ((api_action->action == ACTION_TRAFFICRATE) && if ((api_action->action == ACTION_TRAFFICRATE)
api->actions[i].u.r.rate == 0) && api->actions[i].u.r.rate == 0)
discard_action_found = true; discard_action_found = true;
} }
api->action_num++; 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 /* if ECOMMUNITY_TRAFFIC_RATE = 0 as action
* then reduce the API action list to that 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->vrf_id, 0x4312abde);
key = jhash_1word(pbm->flags, key); 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_min, 2, key);
key = jhash(&pbm->pkt_len_max, 2, key); key = jhash(&pbm->pkt_len_max, 2, key);
key = jhash(&pbm->tcp_flags, 2, key); key = jhash(&pbm->tcp_flags, 2, key);
key = jhash(&pbm->tcp_mask_flags, 2, key); key = jhash(&pbm->tcp_mask_flags, 2, key);
key = jhash(&pbm->dscp_value, 1, key); key = jhash(&pbm->dscp_value, 1, key);
key = jhash(&pbm->flow_label, 2, key);
key = jhash(&pbm->fragment, 1, key); key = jhash(&pbm->fragment, 1, key);
key = jhash(&pbm->protocol, 1, key); key = jhash(&pbm->protocol, 1, key);
return jhash_1word(pbm->type, 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) if (r1->vrf_id != r2->vrf_id)
return false; return false;
if (r1->family != r2->family)
return false;
if (r1->type != r2->type) if (r1->type != r2->type)
return false; 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) if (r1->dscp_value != r2->dscp_value)
return false; return false;
if (r1->flow_label != r2->flow_label)
return false;
if (r1->fragment != r2->fragment) if (r1->fragment != r2->fragment)
return false; return false;
@ -1121,6 +1282,7 @@ uint32_t bgp_pbr_action_hash_key(const void *arg)
pbra = arg; pbra = arg;
key = jhash_1word(pbra->table_id, 0x4312abde); key = jhash_1word(pbra->table_id, 0x4312abde);
key = jhash_1word(pbra->fwmark, key); key = jhash_1word(pbra->fwmark, key);
key = jhash_1word(pbra->afi, key);
return 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) if (r1->vrf_id != r2->vrf_id)
return false; return false;
if (r1->afi != r2->afi)
return false;
if (memcmp(&r1->nh, &r2->nh, sizeof(struct nexthop))) if (memcmp(&r1->nh, &r2->nh, sizeof(struct nexthop)))
return false; return false;
@ -1244,6 +1409,7 @@ void bgp_pbr_cleanup(struct bgp *bgp)
if (bgp->bgp_pbr_cfg == NULL) if (bgp->bgp_pbr_cfg == NULL)
return; return;
bgp_pbr_reset(bgp, AFI_IP); bgp_pbr_reset(bgp, AFI_IP);
bgp_pbr_reset(bgp, AFI_IP6);
XFREE(MTYPE_PBR, bgp->bgp_pbr_cfg); 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 *ptr = return_string;
char buff[64]; char buff[64];
int nb_items = 0; 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) { if (api->match_bitmask & PREFIX_SRC_PRESENT) {
struct prefix *p = &(api->src_prefix); struct prefix *p = &(api->src_prefix);
ptr += sprintf(ptr, "@src %s", prefix2str(p, buff, 64)); if (api->src_prefix_offset)
INCREMENT_DISPLAY(ptr, nb_items); 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) { if (api->match_bitmask & PREFIX_DST_PRESENT) {
struct prefix *p = &(api->dst_prefix); struct prefix *p = &(api->dst_prefix);
INCREMENT_DISPLAY(ptr, nb_items); INCREMENT_DISPLAY(ptr, nb_items, len);
ptr += sprintf(ptr, "@dst %s", prefix2str(p, buff, 64)); 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) if (api->match_protocol_num)
INCREMENT_DISPLAY(ptr, nb_items); INCREMENT_DISPLAY(ptr, nb_items, len);
for (i = 0; i < api->match_protocol_num; i++) for (i = 0; i < api->match_protocol_num; i++) {
ptr += sprintf_bgp_pbr_match_val(ptr, &api->protocol[i], delta = snprintf_bgp_pbr_match_val(ptr, len, &api->protocol[i],
i > 0 ? NULL : "@proto "); i > 0 ? NULL : "@proto ");
len -= delta;
ptr += delta;
}
if (api->match_src_port_num) if (api->match_src_port_num)
INCREMENT_DISPLAY(ptr, nb_items); INCREMENT_DISPLAY(ptr, nb_items, len);
for (i = 0; i < api->match_src_port_num; i++) for (i = 0; i < api->match_src_port_num; i++) {
ptr += sprintf_bgp_pbr_match_val(ptr, &api->src_port[i], delta = snprintf_bgp_pbr_match_val(ptr, len, &api->src_port[i],
i > 0 ? NULL : "@srcport "); i > 0 ? NULL : "@srcport ");
len -= delta;
ptr += delta;
}
if (api->match_dst_port_num) if (api->match_dst_port_num)
INCREMENT_DISPLAY(ptr, nb_items); INCREMENT_DISPLAY(ptr, nb_items, len);
for (i = 0; i < api->match_dst_port_num; i++) for (i = 0; i < api->match_dst_port_num; i++) {
ptr += sprintf_bgp_pbr_match_val(ptr, &api->dst_port[i], delta = snprintf_bgp_pbr_match_val(ptr, len, &api->dst_port[i],
i > 0 ? NULL : "@dstport "); i > 0 ? NULL : "@dstport ");
len -= delta;
ptr += delta;
}
if (api->match_port_num) if (api->match_port_num)
INCREMENT_DISPLAY(ptr, nb_items); INCREMENT_DISPLAY(ptr, nb_items, len);
for (i = 0; i < api->match_port_num; i++) for (i = 0; i < api->match_port_num; i++) {
ptr += sprintf_bgp_pbr_match_val(ptr, &api->port[i], delta = snprintf_bgp_pbr_match_val(ptr, len, &api->port[i],
i > 0 ? NULL : "@port "); i > 0 ? NULL : "@port ");
len -= delta;
ptr += delta;
}
if (api->match_icmp_type_num) if (api->match_icmp_type_num)
INCREMENT_DISPLAY(ptr, nb_items); INCREMENT_DISPLAY(ptr, nb_items, len);
for (i = 0; i < api->match_icmp_type_num; i++) for (i = 0; i < api->match_icmp_type_num; i++) {
ptr += sprintf_bgp_pbr_match_val(ptr, &api->icmp_type[i], delta = snprintf_bgp_pbr_match_val(ptr, len, &api->icmp_type[i],
i > 0 ? NULL : "@icmptype "); i > 0 ? NULL : "@icmptype ");
len -= delta;
ptr += delta;
}
if (api->match_icmp_code_num) if (api->match_icmp_code_num)
INCREMENT_DISPLAY(ptr, nb_items); INCREMENT_DISPLAY(ptr, nb_items, len);
for (i = 0; i < api->match_icmp_code_num; i++) for (i = 0; i < api->match_icmp_code_num; i++) {
ptr += sprintf_bgp_pbr_match_val(ptr, &api->icmp_code[i], delta = snprintf_bgp_pbr_match_val(ptr, len, &api->icmp_code[i],
i > 0 ? NULL : "@icmpcode "); i > 0 ? NULL : "@icmpcode ");
len -= delta;
ptr += delta;
}
if (api->match_packet_length_num) if (api->match_packet_length_num)
INCREMENT_DISPLAY(ptr, nb_items); INCREMENT_DISPLAY(ptr, nb_items, len);
for (i = 0; i < api->match_packet_length_num; i++) for (i = 0; i < api->match_packet_length_num; i++) {
ptr += sprintf_bgp_pbr_match_val(ptr, &api->packet_length[i], delta = snprintf_bgp_pbr_match_val(ptr, len,
&api->packet_length[i],
i > 0 ? NULL : "@plen "); i > 0 ? NULL : "@plen ");
len -= delta;
ptr += delta;
}
if (api->match_dscp_num) if (api->match_dscp_num)
INCREMENT_DISPLAY(ptr, nb_items); INCREMENT_DISPLAY(ptr, nb_items, len);
for (i = 0; i < api->match_dscp_num; i++) for (i = 0; i < api->match_dscp_num; i++) {
ptr += sprintf_bgp_pbr_match_val(ptr, &api->dscp[i], delta = snprintf_bgp_pbr_match_val(ptr, len, &api->dscp[i],
i > 0 ? NULL : "@dscp "); 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) if (api->match_tcpflags_num)
INCREMENT_DISPLAY(ptr, nb_items); INCREMENT_DISPLAY(ptr, nb_items, len);
for (i = 0; i < api->match_tcpflags_num; i++) for (i = 0; i < api->match_tcpflags_num; i++) {
ptr += sprintf_bgp_pbr_match_val(ptr, &api->tcpflags[i], delta = snprintf_bgp_pbr_match_val(ptr, len, &api->tcpflags[i],
i > 0 ? NULL : "@tcpflags "); i > 0 ? NULL : "@tcpflags ");
len -= delta;
ptr += delta;
}
if (api->match_fragment_num) if (api->match_fragment_num)
INCREMENT_DISPLAY(ptr, nb_items); INCREMENT_DISPLAY(ptr, nb_items, len);
for (i = 0; i < api->match_fragment_num; i++) for (i = 0; i < api->match_fragment_num; i++) {
ptr += sprintf_bgp_pbr_match_val(ptr, &api->fragment[i], delta = snprintf_bgp_pbr_match_val(ptr, len, &api->fragment[i],
i > 0 ? NULL : "@fragment "); i > 0 ? NULL : "@fragment ");
if (!nb_items) len -= delta;
ptr += delta;
}
len = sizeof(return_string);
if (!nb_items) {
ptr = return_string; ptr = return_string;
else } else {
ptr += sprintf(ptr, "; "); len -= (ptr - return_string);
if (api->action_num) delta = snprintf(ptr, len, "; ");
ptr += sprintf(ptr, "SET : "); len -= delta;
ptr += delta;
}
if (api->action_num) {
delta = snprintf(ptr, len, "SET : ");
len -= delta;
ptr += delta;
}
nb_items = 0; nb_items = 0;
for (i = 0; i < api->action_num; i++) { for (i = 0; i < api->action_num; i++) {
switch (api->actions[i].action) { switch (api->actions[i].action) {
case ACTION_TRAFFICRATE: case ACTION_TRAFFICRATE:
INCREMENT_DISPLAY(ptr, nb_items); INCREMENT_DISPLAY(ptr, nb_items, len);
ptr += sprintf(ptr, "@set rate %f", delta = snprintf(ptr, len, "@set rate %f",
api->actions[i].u.r.rate); api->actions[i].u.r.rate);
len -= delta;
ptr += delta;
break; break;
case ACTION_TRAFFIC_ACTION: case ACTION_TRAFFIC_ACTION:
INCREMENT_DISPLAY(ptr, nb_items); INCREMENT_DISPLAY(ptr, nb_items, len);
ptr += sprintf(ptr, "@action "); delta = snprintf(ptr, len, "@action ");
len -= delta;
ptr += delta;
if (api->actions[i].u.za.filter if (api->actions[i].u.za.filter
& TRAFFIC_ACTION_TERMINATE) & TRAFFIC_ACTION_TERMINATE) {
ptr += sprintf(ptr, delta = snprintf(ptr, len,
" terminate (apply filter(s))"); " terminate (apply filter(s))");
len -= delta;
ptr += delta;
}
if (api->actions[i].u.za.filter if (api->actions[i].u.za.filter
& TRAFFIC_ACTION_DISTRIBUTE) & TRAFFIC_ACTION_DISTRIBUTE) {
ptr += sprintf(ptr, " distribute"); delta = snprintf(ptr, len, " distribute");
len -= delta;
ptr += delta;
}
if (api->actions[i].u.za.filter if (api->actions[i].u.za.filter
& TRAFFIC_ACTION_SAMPLE) & TRAFFIC_ACTION_SAMPLE) {
ptr += sprintf(ptr, " sample"); delta = snprintf(ptr, len, " sample");
len -= delta;
ptr += delta;
}
break; break;
case ACTION_REDIRECT_IP: case ACTION_REDIRECT_IP: {
INCREMENT_DISPLAY(ptr, nb_items); char local_buff[INET6_ADDRSTRLEN];
char local_buff[INET_ADDRSTRLEN]; void *ptr_ip;
if (inet_ntop(AF_INET, INCREMENT_DISPLAY(ptr, nb_items, len);
&api->actions[i].u.zr.redirect_ip_v4, if (api->afi == AF_INET)
local_buff, INET_ADDRSTRLEN) != NULL) ptr_ip = &api->actions[i].u.zr.redirect_ip_v4;
ptr += sprintf(ptr, 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); "@redirect ip nh %s", local_buff);
len -= delta;
ptr += delta;
}
break; break;
}
case ACTION_REDIRECT: { case ACTION_REDIRECT: {
struct vrf *vrf; struct vrf *vrf;
vrf = vrf_lookup_by_id(api->actions[i].u.redirect_vrf); vrf = vrf_lookup_by_id(api->actions[i].u.redirect_vrf);
INCREMENT_DISPLAY(ptr, nb_items); INCREMENT_DISPLAY(ptr, nb_items, len);
ptr += sprintf(ptr, "@redirect vrf %s(%u)", delta = snprintf(ptr, len, "@redirect vrf %s(%u)",
VRF_LOGNAME(vrf), VRF_LOGNAME(vrf),
api->actions[i].u.redirect_vrf); api->actions[i].u.redirect_vrf);
len -= delta;
ptr += delta;
break; break;
} }
case ACTION_MARKING: case ACTION_MARKING:
INCREMENT_DISPLAY(ptr, nb_items); INCREMENT_DISPLAY(ptr, nb_items, len);
ptr += sprintf(ptr, "@set dscp %u", delta = snprintf(ptr, len, "@set dscp/flowlabel %u",
api->actions[i].u.marking_dscp); api->actions[i].u.marking_dscp);
len -= delta;
ptr += delta;
break; break;
default: default:
break; 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) { if (bpa->installed && bpa->table_id != 0) {
bgp_send_pbr_rule_action(bpa, NULL, false); bgp_send_pbr_rule_action(bpa, NULL, false);
bgp_zebra_announce_default(bpa->bgp, &(bpa->nh), bgp_zebra_announce_default(bpa->bgp, &(bpa->nh),
AFI_IP, bpa->afi,
bpa->table_id, bpa->table_id,
false); false);
bpa->installed = 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_min != bpm->pkt_len_min ||
bpm_temp->pkt_len_max != bpm->pkt_len_max || bpm_temp->pkt_len_max != bpm->pkt_len_max ||
bpm_temp->dscp_value != bpm->dscp_value || bpm_temp->dscp_value != bpm->dscp_value ||
bpm_temp->flow_label != bpm->flow_label ||
bpm_temp->family != bpm->family ||
bpm_temp->fragment != bpm->fragment) bpm_temp->fragment != bpm->fragment)
return HASHWALK_CONTINUE; return HASHWALK_CONTINUE;
@ -1644,16 +1907,17 @@ static void bgp_pbr_policyroute_remove_from_zebra_unit(
return; return;
} }
temp.family = bpf->family;
if (bpf->src) { if (bpf->src) {
temp.flags |= MATCH_IP_SRC_SET; temp.flags |= MATCH_IP_SRC_SET;
prefix_copy(&temp2.src, bpf->src); prefix_copy(&temp2.src, bpf->src);
} else } else
temp2.src.family = AF_INET; temp2.src.family = bpf->family;
if (bpf->dst) { if (bpf->dst) {
temp.flags |= MATCH_IP_DST_SET; temp.flags |= MATCH_IP_DST_SET;
prefix_copy(&temp2.dst, bpf->dst); prefix_copy(&temp2.dst, bpf->dst);
} else } else
temp2.dst.family = AF_INET; temp2.dst.family = bpf->family;
if (src_port && (src_port->min_port || bpf->protocol == IPPROTO_ICMP)) { if (src_port && (src_port->min_port || bpf->protocol == IPPROTO_ICMP)) {
if (bpf->protocol == IPPROTO_ICMP) if (bpf->protocol == IPPROTO_ICMP)
temp.flags |= MATCH_ICMP_SET; temp.flags |= MATCH_ICMP_SET;
@ -1696,6 +1960,14 @@ static void bgp_pbr_policyroute_remove_from_zebra_unit(
temp.flags |= MATCH_DSCP_SET; temp.flags |= MATCH_DSCP_SET;
temp.dscp_value = bpf->dscp->val; 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) {
if (bpf->fragment->mask) if (bpf->fragment->mask)
temp.flags |= MATCH_FRAGMENT_INVERSE_SET; 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) if (type_entry == FLOWSPEC_TCP_FLAGS)
return FLOWSPEC_DSCP; return FLOWSPEC_DSCP;
if (type_entry == FLOWSPEC_DSCP) if (type_entry == FLOWSPEC_DSCP)
return FLOWSPEC_FLOW_LABEL;
if (type_entry == FLOWSPEC_FLOW_LABEL)
return FLOWSPEC_PKT_LEN; return FLOWSPEC_PKT_LEN;
if (type_entry == FLOWSPEC_PKT_LEN) if (type_entry == FLOWSPEC_PKT_LEN)
return FLOWSPEC_FRAGMENT; return FLOWSPEC_FRAGMENT;
@ -1835,6 +2109,9 @@ static void bgp_pbr_policyroute_remove_from_zebra_recursive(
} else if (type_entry == FLOWSPEC_DSCP && bpof->dscp) { } else if (type_entry == FLOWSPEC_DSCP && bpof->dscp) {
orig_list = bpof->dscp; orig_list = bpof->dscp;
target_val = &bpf->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) { } else if (type_entry == FLOWSPEC_PKT_LEN && bpof->pkt_len) {
orig_list = bpof->pkt_len; orig_list = bpof->pkt_len;
target_val = &bpf->pkt_len_val; target_val = &bpf->pkt_len_val;
@ -1872,6 +2149,9 @@ static void bgp_pbr_policyroute_remove_from_zebra(
else if (bpof->dscp) else if (bpof->dscp)
bgp_pbr_policyroute_remove_from_zebra_recursive( bgp_pbr_policyroute_remove_from_zebra_recursive(
bgp, path, bpf, bpof, FLOWSPEC_DSCP); 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) else if (bpof->pkt_len)
bgp_pbr_policyroute_remove_from_zebra_recursive( bgp_pbr_policyroute_remove_from_zebra_recursive(
bgp, path, bpf, bpof, FLOWSPEC_PKT_LEN); 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); list_delete_all_node(bpof->tcpflags);
if (bpof->dscp) if (bpof->dscp)
list_delete_all_node(bpof->dscp); list_delete_all_node(bpof->dscp);
if (bpof->flowlabel)
list_delete_all_node(bpof->flowlabel);
if (bpof->pkt_len) if (bpof->pkt_len)
list_delete_all_node(bpof->pkt_len); list_delete_all_node(bpof->pkt_len);
if (bpof->fragment) if (bpof->fragment)
@ -1979,6 +2261,15 @@ static void bgp_pbr_dump_entry(struct bgp_pbr_filter *bpf, bool add)
? "!" : "", ? "!" : "",
bpf->dscp->val); 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", zlog_debug("BGP: %s FS PBR from %s to %s, %s %s",
add ? "adding" : "removing", add ? "adding" : "removing",
bpf->src == NULL ? "<all>" : bpf->src == NULL ? "<all>" :
@ -2027,6 +2318,7 @@ static void bgp_pbr_policyroute_add_to_zebra_unit(struct bgp *bgp,
if (nh) if (nh)
memcpy(&temp3.nh, nh, sizeof(struct nexthop)); memcpy(&temp3.nh, nh, sizeof(struct nexthop));
temp3.vrf_id = bpf->vrf_id; temp3.vrf_id = bpf->vrf_id;
temp3.afi = family2afi(bpf->family);
bpa = hash_get(bgp->pbr_action_hash, &temp3, bpa = hash_get(bgp->pbr_action_hash, &temp3,
bgp_pbr_action_alloc_intern); 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) { if (!bpa->installed && !bpa->install_in_progress) {
bgp_send_pbr_rule_action(bpa, NULL, true); bgp_send_pbr_rule_action(bpa, NULL, true);
bgp_zebra_announce_default(bgp, nh, bgp_zebra_announce_default(bgp, nh,
AFI_IP, bpa->table_id, true); bpa->afi,
bpa->table_id, true);
} }
/* ip rule add */ /* ip rule add */
if (bpr && !bpr->installed) if (bpr && !bpr->installed)
@ -2111,6 +2404,7 @@ static void bgp_pbr_policyroute_add_to_zebra_unit(struct bgp *bgp,
/* then look for bpm */ /* then look for bpm */
memset(&temp, 0, sizeof(temp)); memset(&temp, 0, sizeof(temp));
temp.vrf_id = bpf->vrf_id; temp.vrf_id = bpf->vrf_id;
temp.family = bpf->family;
if (bpf->src) if (bpf->src)
temp.flags |= MATCH_IP_SRC_SET; temp.flags |= MATCH_IP_SRC_SET;
if (bpf->dst) 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.flags |= MATCH_DSCP_SET;
temp.dscp_value = bpf->dscp->val; 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) {
if (bpf->fragment->mask) if (bpf->fragment->mask)
temp.flags |= MATCH_FRAGMENT_INVERSE_SET; 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) { if (bpm->unique == 0) {
bpm->unique = ++bgp_pbr_match_counter_unique; bpm->unique = ++bgp_pbr_match_counter_unique;
/* 0 value is forbidden */ /* 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, bpm->entry_hash = hash_create_size(8,
bgp_pbr_match_entry_hash_key, bgp_pbr_match_entry_hash_key,
bgp_pbr_match_entry_hash_equal, 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) if (bpf->src)
prefix_copy(&temp2.src, bpf->src); prefix_copy(&temp2.src, bpf->src);
else else
temp2.src.family = AF_INET; temp2.src.family = bpf->family;
if (bpf->dst) if (bpf->dst)
prefix_copy(&temp2.dst, bpf->dst); prefix_copy(&temp2.dst, bpf->dst);
else else
temp2.dst.family = AF_INET; temp2.dst.family = bpf->family;
temp2.src_port_min = src_port ? src_port->min_port : 0; temp2.src_port_min = src_port ? src_port->min_port : 0;
temp2.dst_port_min = dst_port ? dst_port->min_port : 0; temp2.dst_port_min = dst_port ? dst_port->min_port : 0;
temp2.src_port_max = src_port ? src_port->max_port : 0; temp2.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) { if (!bpa->installed && !bpa->install_in_progress) {
bgp_send_pbr_rule_action(bpa, NULL, true); bgp_send_pbr_rule_action(bpa, NULL, true);
bgp_zebra_announce_default(bgp, nh, bgp_zebra_announce_default(bgp, nh,
AFI_IP, bpa->table_id, true); bpa->afi, bpa->table_id, true);
} }
/* ipset create */ /* ipset create */
@ -2400,8 +2702,11 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, struct bgp_path_info *path,
bpf.type = api->type; bpf.type = api->type;
memset(&nh, 0, sizeof(struct nexthop)); memset(&nh, 0, sizeof(struct nexthop));
nh.vrf_id = VRF_UNKNOWN; nh.vrf_id = VRF_UNKNOWN;
if (api->match_protocol_num) if (api->match_protocol_num) {
proto = (uint8_t)api->protocol[0].value; 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 /* if match_port is selected, then either src or dst port will be parsed
* but not both at the same time * 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.protocol = proto;
bpf.src_port = srcp; bpf.src_port = srcp;
bpf.dst_port = dstp; bpf.dst_port = dstp;
bpf.family = afi2family(api->afi);
if (!add) { if (!add) {
bgp_pbr_policyroute_remove_from_zebra(bgp, path, &bpf, &bpof); bgp_pbr_policyroute_remove_from_zebra(bgp, path, &bpf, &bpof);
return; return;
@ -2559,10 +2865,18 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, struct bgp_path_info *path,
*/ */
break; break;
case ACTION_REDIRECT_IP: case ACTION_REDIRECT_IP:
nh.vrf_id = api->vrf_id;
if (api->afi == AFI_IP) {
nh.type = NEXTHOP_TYPE_IPV4; nh.type = NEXTHOP_TYPE_IPV4;
nh.gate.ipv4.s_addr = nh.gate.ipv4.s_addr =
api->actions[i].u.zr.redirect_ip_v4.s_addr; api->actions[i].u.zr.
nh.vrf_id = api->vrf_id; 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, bgp_pbr_policyroute_add_to_zebra(bgp, path, &bpf, &bpof,
&nh, &rate); &nh, &rate);
/* XXX combination with REDIRECT_VRF /* 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; continue_loop = 0;
break; break;
case ACTION_REDIRECT: case ACTION_REDIRECT:
nh.vrf_id = api->actions[i].u.redirect_vrf; if (api->afi == AFI_IP)
nh.type = NEXTHOP_TYPE_IPV4; nh.type = NEXTHOP_TYPE_IPV4;
else
nh.type = NEXTHOP_TYPE_IPV6;
nh.vrf_id = api->actions[i].u.redirect_vrf;
bgp_pbr_policyroute_add_to_zebra(bgp, path, &bpf, &bpof, bgp_pbr_policyroute_add_to_zebra(bgp, path, &bpf, &bpof,
&nh, &rate); &nh, &rate);
continue_loop = 0; continue_loop = 0;
@ -2580,7 +2897,7 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, struct bgp_path_info *path,
case ACTION_MARKING: case ACTION_MARKING:
if (BGP_DEBUG(pbr, PBR)) { if (BGP_DEBUG(pbr, PBR)) {
bgp_pbr_print_policy_route(api); 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); api->actions[i].u.marking_dscp);
} }
break; break;
@ -2598,8 +2915,6 @@ void bgp_pbr_update_entry(struct bgp *bgp, const struct prefix *p,
{ {
struct bgp_pbr_entry_main api; struct bgp_pbr_entry_main api;
if (afi == AFI_IP6)
return; /* IPv6 not supported */
if (safi != SAFI_FLOWSPEC) if (safi != SAFI_FLOWSPEC)
return; /* not supported */ return; /* not supported */
/* Make Zebra API structure. */ /* 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_head *head;
struct bgp_pbr_interface *pbr_if; struct bgp_pbr_interface *pbr_if;
if (!bgp_pbr_cfg || afi != AFI_IP) if (!bgp_pbr_cfg)
return; return;
if (afi == AFI_IP)
head = &(bgp_pbr_cfg->ifaces_by_name_ipv4); 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)) { while (!RB_EMPTY(bgp_pbr_interface_head, head)) {
pbr_if = RB_ROOT(bgp_pbr_interface_head, head); pbr_if = RB_ROOT(bgp_pbr_interface_head, head);
RB_REMOVE(bgp_pbr_interface_head, head, pbr_if); RB_REMOVE(bgp_pbr_interface_head, head, pbr_if);

View File

@ -79,6 +79,7 @@ struct bgp_pbr_entry_action {
vrf_id_t redirect_vrf; vrf_id_t redirect_vrf;
struct _pbr_redirect_ip { struct _pbr_redirect_ip {
struct in_addr redirect_ip_v4; struct in_addr redirect_ip_v4;
struct in6_addr redirect_ip_v6;
uint8_t duplicate; uint8_t duplicate;
} zr; } zr;
uint8_t marking_dscp; uint8_t marking_dscp;
@ -114,13 +115,17 @@ struct bgp_pbr_entry_main {
uint8_t match_dscp_num; uint8_t match_dscp_num;
uint8_t match_tcpflags_num; uint8_t match_tcpflags_num;
uint8_t match_fragment_num; uint8_t match_fragment_num;
uint8_t match_flowlabel_num;
struct prefix src_prefix; struct prefix src_prefix;
struct prefix dst_prefix; struct prefix dst_prefix;
uint8_t src_prefix_offset;
uint8_t dst_prefix_offset;
#define PROTOCOL_UDP 17 #define PROTOCOL_UDP 17
#define PROTOCOL_TCP 6 #define PROTOCOL_TCP 6
#define PROTOCOL_ICMP 1 #define PROTOCOL_ICMP 1
#define PROTOCOL_ICMPV6 58
struct bgp_pbr_match_val protocol[BGP_PBR_MATCH_VAL_MAX]; struct bgp_pbr_match_val protocol[BGP_PBR_MATCH_VAL_MAX];
struct bgp_pbr_match_val src_port[BGP_PBR_MATCH_VAL_MAX]; struct bgp_pbr_match_val src_port[BGP_PBR_MATCH_VAL_MAX];
struct bgp_pbr_match_val dst_port[BGP_PBR_MATCH_VAL_MAX]; struct bgp_pbr_match_val dst_port[BGP_PBR_MATCH_VAL_MAX];
@ -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 icmp_code[BGP_PBR_MATCH_VAL_MAX];
struct bgp_pbr_match_val packet_length[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 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 tcpflags[BGP_PBR_MATCH_VAL_MAX];
struct bgp_pbr_match_val fragment[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_config {
struct bgp_pbr_interface_head ifaces_by_name_ipv4; struct bgp_pbr_interface_head ifaces_by_name_ipv4;
bool pbr_interface_any_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; extern struct bgp_pbr_config *bgp_pbr_cfg;
@ -179,6 +187,7 @@ struct bgp_pbr_match {
uint32_t type; uint32_t type;
uint32_t flags; uint32_t flags;
uint8_t family;
uint16_t pkt_len_min; uint16_t pkt_len_min;
uint16_t pkt_len_max; uint16_t pkt_len_max;
@ -187,6 +196,7 @@ struct bgp_pbr_match {
uint8_t dscp_value; uint8_t dscp_value;
uint8_t fragment; uint8_t fragment;
uint8_t protocol; uint8_t protocol;
uint16_t flow_label;
vrf_id_t vrf_id; vrf_id_t vrf_id;
@ -254,6 +264,7 @@ struct bgp_pbr_action {
bool install_in_progress; bool install_in_progress;
uint32_t refcnt; uint32_t refcnt;
struct bgp *bgp; struct bgp *bgp;
afi_t afi;
}; };
extern struct bgp_pbr_rule *bgp_pbr_rule_lookup(vrf_id_t vrf_id, extern struct bgp_pbr_rule *bgp_pbr_rule_lookup(vrf_id_t vrf_id,

View File

@ -10021,11 +10021,14 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi,
if (dest_p->family == AF_FLOWSPEC) { if (dest_p->family == AF_FLOWSPEC) {
char retstr[BGP_FLOWSPEC_STRING_DISPLAY_MAX]; char retstr[BGP_FLOWSPEC_STRING_DISPLAY_MAX];
bgp_fs_nlri_get_string( bgp_fs_nlri_get_string(
(unsigned char *) (unsigned char *)
dest_p->u.prefix_flowspec.ptr, dest_p->u.prefix_flowspec.ptr,
dest_p->u.prefix_flowspec.prefixlen, 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) if (first)
vty_out(vty, "\"%s/%d\": ", retstr, vty_out(vty, "\"%s/%d\": ", retstr,
dest_p->u.prefix_flowspec dest_p->u.prefix_flowspec

View File

@ -594,10 +594,14 @@ route_match_prefix_list_flowspec(afi_t afi, struct prefix_list *plist,
memset(&api, 0, sizeof(api)); memset(&api, 0, sizeof(api));
if (family2afi(p->u.prefix_flowspec.family) != afi)
return RMAP_NOMATCH;
/* extract match from flowspec entries */ /* extract match from flowspec entries */
ret = bgp_flowspec_match_rules_fill( ret = bgp_flowspec_match_rules_fill(
(uint8_t *)p->u.prefix_flowspec.ptr, (uint8_t *)p->u.prefix_flowspec.ptr,
p->u.prefix_flowspec.prefixlen, &api); p->u.prefix_flowspec.prefixlen, &api,
afi);
if (ret < 0) if (ret < 0)
return RMAP_NOMATCH; return RMAP_NOMATCH;
if (api.match_bitmask & PREFIX_DST_PRESENT || 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); ecommunity_free(&old_ecom);
} else { } else {
ecom_lb.size = 1; ecom_lb.size = 1;
ecom_lb.unit_size = ECOMMUNITY_SIZE;
ecom_lb.val = (uint8_t *)lb_eval.val; ecom_lb.val = (uint8_t *)lb_eval.val;
new_ecom = ecommunity_dup(&ecom_lb); new_ecom = ecommunity_dup(&ecom_lb);
} }

View File

@ -7376,15 +7376,20 @@ DEFPY(
} }
static int set_ecom_list(struct vty *vty, int argc, struct cmd_token **argv, 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 *ecom = NULL;
struct ecommunity *ecomadd; struct ecommunity *ecomadd;
for (; argc; --argc, ++argv) { for (; argc; --argc, ++argv) {
if (is_rt6)
ecomadd = ecommunity_str2com_ipv6(argv[0]->arg,
ECOMMUNITY_ROUTE_TARGET,
0);
else
ecomadd = ecommunity_str2com(argv[0]->arg, ecomadd = ecommunity_str2com(argv[0]->arg,
ECOMMUNITY_ROUTE_TARGET, 0); ECOMMUNITY_ROUTE_TARGET,
0);
if (!ecomadd) { if (!ecomadd) {
vty_out(vty, "Malformed community-list value\n"); vty_out(vty, "Malformed community-list value\n");
if (ecom) if (ecom)
@ -7464,10 +7469,10 @@ DEFPY (af_rd_vpn_export,
int ret; int ret;
afi_t afi; afi_t afi;
int idx = 0; int idx = 0;
int yes = 1; bool yes = true;
if (argv_find(argv, argc, "no", &idx)) if (argv_find(argv, argc, "no", &idx))
yes = 0; yes = false;
if (yes) { if (yes) {
ret = str2prefix_rd(rd_str, &prd); ret = str2prefix_rd(rd_str, &prd);
@ -7525,10 +7530,10 @@ DEFPY (af_label_vpn_export,
mpls_label_t label = MPLS_LABEL_NONE; mpls_label_t label = MPLS_LABEL_NONE;
afi_t afi; afi_t afi;
int idx = 0; int idx = 0;
int yes = 1; bool yes = true;
if (argv_find(argv, argc, "no", &idx)) if (argv_find(argv, argc, "no", &idx))
yes = 0; yes = false;
/* If "no ...", squash trailing parameter */ /* If "no ...", squash trailing parameter */
if (!yes) if (!yes)
@ -7620,7 +7625,6 @@ DEFPY (af_nexthop_vpn_export,
vty_out(vty, "%% Nexthop required\n"); vty_out(vty, "%% Nexthop required\n");
return CMD_WARNING_CONFIG_FAILED; return CMD_WARNING_CONFIG_FAILED;
} }
if (!sockunion2hostprefix(nexthop_su, &p)) if (!sockunion2hostprefix(nexthop_su, &p))
return CMD_WARNING_CONFIG_FAILED; return CMD_WARNING_CONFIG_FAILED;
} }
@ -7686,10 +7690,10 @@ DEFPY (af_rt_vpn_imexport,
vpn_policy_direction_t dir; vpn_policy_direction_t dir;
afi_t afi; afi_t afi;
int idx = 0; int idx = 0;
int yes = 1; bool yes = true;
if (argv_find(argv, argc, "no", &idx)) if (argv_find(argv, argc, "no", &idx))
yes = 0; yes = false;
afi = vpn_policy_getafi(vty, bgp, false); afi = vpn_policy_getafi(vty, bgp, false);
if (afi == AFI_MAX) if (afi == AFI_MAX)
@ -7704,7 +7708,7 @@ DEFPY (af_rt_vpn_imexport,
vty_out(vty, "%% Missing RTLIST\n"); vty_out(vty, "%% Missing RTLIST\n");
return CMD_WARNING_CONFIG_FAILED; 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) { if (ret != CMD_SUCCESS) {
return ret; return ret;
} }
@ -7766,10 +7770,10 @@ DEFPY (af_route_map_vpn_imexport,
vpn_policy_direction_t dir; vpn_policy_direction_t dir;
afi_t afi; afi_t afi;
int idx = 0; int idx = 0;
int yes = 1; bool yes = true;
if (argv_find(argv, argc, "no", &idx)) if (argv_find(argv, argc, "no", &idx))
yes = 0; yes = false;
afi = vpn_policy_getafi(vty, bgp, false); afi = vpn_policy_getafi(vty, bgp, false);
if (afi == AFI_MAX) if (afi == AFI_MAX)
@ -8008,12 +8012,12 @@ DEFPY (bgp_imexport_vpn,
afi_t afi; afi_t afi;
safi_t safi; safi_t safi;
int idx = 0; int idx = 0;
int yes = 1; bool yes = true;
int flag; int flag;
vpn_policy_direction_t dir; vpn_policy_direction_t dir;
if (argv_find(argv, argc, "no", &idx)) if (argv_find(argv, argc, "no", &idx))
yes = 0; yes = false;
if (BGP_INSTANCE_TYPE_VRF != bgp->inst_type && if (BGP_INSTANCE_TYPE_VRF != bgp->inst_type &&
BGP_INSTANCE_TYPE_DEFAULT != bgp->inst_type) { BGP_INSTANCE_TYPE_DEFAULT != bgp->inst_type) {
@ -8061,34 +8065,44 @@ DEFPY (bgp_imexport_vpn,
DEFPY (af_routetarget_import, DEFPY (af_routetarget_import,
af_routetarget_import_cmd, af_routetarget_import_cmd,
"[no] <rt|route-target> redirect import RTLIST...", "[no] <rt|route-target|route-target6|rt6> redirect import RTLIST...",
NO_STR NO_STR
"Specify route target list\n" "Specify route target list\n"
"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" "Flow-spec redirect type route target\n"
"Import routes to this address-family\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); VTY_DECLVAR_CONTEXT(bgp, bgp);
int ret; int ret;
struct ecommunity *ecom = NULL; struct ecommunity *ecom = NULL;
afi_t afi; afi_t afi;
int idx = 0; int idx = 0, idx_unused = 0;
int yes = 1; bool yes = true;
bool rt6 = false;
if (argv_find(argv, argc, "no", &idx)) 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); afi = vpn_policy_getafi(vty, bgp, false);
if (afi == AFI_MAX) if (afi == AFI_MAX)
return CMD_WARNING_CONFIG_FAILED; return CMD_WARNING_CONFIG_FAILED;
if (rt6 && afi != AFI_IP6)
return CMD_WARNING_CONFIG_FAILED;
if (yes) { if (yes) {
if (!argv_find(argv, argc, "RTLIST", &idx)) { if (!argv_find(argv, argc, "RTLIST", &idx)) {
vty_out(vty, "%% Missing RTLIST\n"); vty_out(vty, "%% Missing RTLIST\n");
return CMD_WARNING_CONFIG_FAILED; 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) if (ret != CMD_SUCCESS)
return ret; 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_FORMAT_ROUTE_MAP,
ECOMMUNITY_ROUTE_TARGET); 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); XFREE(MTYPE_ECOMMUNITY_STR, b);
} }
} }

View File

@ -2356,7 +2356,10 @@ static void bgp_encode_pbr_rule_action(struct stream *s,
struct bgp_pbr_rule *pbr) struct bgp_pbr_rule *pbr)
{ {
struct prefix pfx; 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 */ stream_putl(s, 0); /* seqno unused */
if (pbr) if (pbr)
stream_putl(s, pbr->priority); 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)); memcpy(&pfx, &(pbr->src), sizeof(struct prefix));
else { else {
memset(&pfx, 0, sizeof(pfx)); memset(&pfx, 0, sizeof(pfx));
pfx.family = AF_INET; pfx.family = fam;
} }
stream_putc(s, pfx.family); stream_putc(s, pfx.family);
stream_putc(s, pfx.prefixlen); 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)); memcpy(&pfx, &(pbr->dst), sizeof(struct prefix));
else { else {
memset(&pfx, 0, sizeof(pfx)); memset(&pfx, 0, sizeof(pfx));
pfx.family = AF_INET; pfx.family = fam;
} }
stream_putc(s, pfx.family); stream_putc(s, pfx.family);
stream_putc(s, pfx.prefixlen); 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->unique);
stream_putl(s, pbim->type); stream_putl(s, pbim->type);
stream_putc(s, pbim->family);
stream_put(s, pbim->ipset_name, stream_put(s, pbim->ipset_name,
ZEBRA_IPSET_NAME_SIZE); ZEBRA_IPSET_NAME_SIZE);
} }
@ -2461,6 +2464,7 @@ static void bgp_encode_pbr_iptable_match(struct stream *s,
stream_putl(s, bpa->fwmark); stream_putl(s, bpa->fwmark);
stream_put(s, pbm->ipset_name, stream_put(s, pbm->ipset_name,
ZEBRA_IPSET_NAME_SIZE); ZEBRA_IPSET_NAME_SIZE);
stream_putc(s, pbm->family);
stream_putw(s, pbm->pkt_len_min); stream_putw(s, pbm->pkt_len_min);
stream_putw(s, pbm->pkt_len_max); stream_putw(s, pbm->pkt_len_max);
stream_putw(s, pbm->tcp_flags); 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->dscp_value);
stream_putc(s, pbm->fragment); stream_putc(s, pbm->fragment);
stream_putc(s, pbm->protocol); stream_putc(s, pbm->protocol);
stream_putw(s, pbm->flow_label);
} }
/* BGP has established connection with Zebra. */ /* 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; 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_config *bgp_pbr_cfg = bgp->bgp_pbr_cfg;
struct bgp_pbr_interface_head *head; 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) if (!bgp_pbr_cfg)
return; return;
if (family == AF_INET)
head = &(bgp_pbr_cfg->ifaces_by_name_ipv4); 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) { RB_FOREACH (pbr_if, bgp_pbr_interface_head, head) {
ifp = if_lookup_by_name(pbr_if->name, bgp->vrf_id); ifp = if_lookup_by_name(pbr_if->name, bgp->vrf_id);
if (ifp) 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_config *bgp_pbr_cfg = bgp->bgp_pbr_cfg;
struct bgp_pbr_interface_head *head; struct bgp_pbr_interface_head *head;
@ -3006,8 +3014,10 @@ static int bgp_pbr_get_ifnumber(struct bgp *bgp)
if (!bgp_pbr_cfg) if (!bgp_pbr_cfg)
return 0; return 0;
if (family == AF_INET)
head = &(bgp_pbr_cfg->ifaces_by_name_ipv4); 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) { RB_FOREACH (pbr_if, bgp_pbr_interface_head, head) {
if (if_lookup_by_name(pbr_if->name, bgp->vrf_id)) if (if_lookup_by_name(pbr_if->name, bgp->vrf_id))
cnt++; cnt++;
@ -3038,10 +3048,10 @@ void bgp_send_pbr_iptable(struct bgp_pbr_action *pba,
VRF_DEFAULT); VRF_DEFAULT);
bgp_encode_pbr_iptable_match(s, pba, pbm); 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); stream_putl(s, nb_interface);
if (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)); stream_putw_at(s, 0, stream_get_endp(s));
ret = zclient_send_message(zclient); ret = zclient_send_message(zclient);
if (install) { if (install) {
@ -3063,14 +3073,14 @@ void bgp_zebra_announce_default(struct bgp *bgp, struct nexthop *nh,
struct zapi_route api; struct zapi_route api;
struct prefix p; 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) || nh->vrf_id == VRF_UNKNOWN)
return; return;
memset(&p, 0, sizeof(struct prefix)); memset(&p, 0, sizeof(struct prefix));
/* default route */ if (afi != AFI_IP && afi != AFI_IP6)
if (afi != AFI_IP)
return; return;
p.family = AF_INET; p.family = afi2family(afi);
memset(&api, 0, sizeof(api)); memset(&api, 0, sizeof(api));
api.vrf_id = bgp->vrf_id; api.vrf_id = bgp->vrf_id;
api.type = ZEBRA_ROUTE_BGP; 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); SET_FLAG(api.message, ZAPI_MESSAGE_DISTANCE);
/* redirect IP */ /* 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]; char buff[PREFIX_STRLEN];
api_nh->vrf_id = nh->vrf_id; 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); inet_ntop(AF_INET, &(nh->gate.ipv4), buff, INET_ADDRSTRLEN);
if (BGP_DEBUG(zebra, ZEBRA)) 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", announce ? "adding" : "withdrawing",
buff, table_id); buff, table_id);
zclient_route_send(announce ? ZEBRA_ROUTE_ADD zclient_route_send(announce ? ZEBRA_ROUTE_ADD

View File

@ -1534,6 +1534,7 @@ struct bgp_nlri {
#define BGP_ATTR_AS_PATHLIMIT 21 #define BGP_ATTR_AS_PATHLIMIT 21
#define BGP_ATTR_PMSI_TUNNEL 22 #define BGP_ATTR_PMSI_TUNNEL 22
#define BGP_ATTR_ENCAP 23 #define BGP_ATTR_ENCAP 23
#define BGP_ATTR_IPV6_EXT_COMMUNITIES 25
#define BGP_ATTR_LARGE_COMMUNITIES 32 #define BGP_ATTR_LARGE_COMMUNITIES 32
#define BGP_ATTR_PREFIX_SID 40 #define BGP_ATTR_PREFIX_SID 40
#ifdef ENABLE_BGP_VNC_ATTR #ifdef ENABLE_BGP_VNC_ATTR

View File

@ -22,6 +22,9 @@ more or less complex combination of the following:
- Layer 3 information: DSCP value, Protocol type, packet length, fragmentation. - Layer 3 information: DSCP value, Protocol type, packet length, fragmentation.
- Misc layer 4 TCP flags. - 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 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 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 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` - :rfc:`5575`
- [Draft-IETF-IDR-Flowspec-redirect-IP]_ - [Draft-IETF-IDR-Flowspec-redirect-IP]_
- [Draft-IETF-IDR-Flow-Spec-V6]_
.. _design-principles-flowspec: .. _design-principles-flowspec:
@ -108,9 +112,13 @@ As of today, it is only possible to configure Flowspec on the default VRF.
router bgp <AS> router bgp <AS>
neighbor <A.B.C.D> remote-as <remoteAS> neighbor <A.B.C.D> remote-as <remoteAS>
neighbor <A:B::C:D> remote-as <remoteAS2>
address-family ipv4 flowspec address-family ipv4 flowspec
neighbor <A.B.C.D> activate neighbor <A.B.C.D> activate
exit exit
address-family ipv6 flowspec
neighbor <A:B::C:D> activate
exit
exit exit
You can see Flowspec entries, by using one of the following show commands: 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] .. index:: show bgp ipv4 flowspec [detail | A.B.C.D]
.. clicmd:: 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 Per-interface configuration
^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -183,6 +193,28 @@ interfaces are created with private IP addressing scheme.
exit exit
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 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-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-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> .. [Presentation] <https://docs.google.com/presentation/d/1ekQygUAG5yvQ3wWUyrw4Wcag0LgmbW1kV02IWcU4iUg/edit#slide=id.g378f0e1b5e_1_44>

View File

@ -129,6 +129,8 @@ struct pbr_rule {
#define MATCH_FRAGMENT_INVERSE_SET (1 << 9) #define MATCH_FRAGMENT_INVERSE_SET (1 << 9)
#define MATCH_ICMP_SET (1 << 10) #define MATCH_ICMP_SET (1 << 10)
#define MATCH_PROTOCOL_SET (1 << 11) #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, extern int zapi_pbr_rule_encode(uint8_t cmd, struct stream *s,
struct pbr_rule *zrule); struct pbr_rule *zrule);

View File

@ -188,6 +188,10 @@ int prefix_match(const struct prefix *n, const struct prefix *p)
if (n->family == AF_FLOWSPEC) { if (n->family == AF_FLOWSPEC) {
/* prefixlen is unused. look at fs prefix len */ /* 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 > if (n->u.prefix_flowspec.prefixlen >
p->u.prefix_flowspec.prefixlen) p->u.prefix_flowspec.prefixlen)
return 0; return 0;
@ -325,6 +329,8 @@ void prefix_copy(union prefixptr udest, union prefixconstptr usrc)
len = src->u.prefix_flowspec.prefixlen; len = src->u.prefix_flowspec.prefixlen;
dest->u.prefix_flowspec.prefixlen = dest->u.prefix_flowspec.prefixlen =
src->u.prefix_flowspec.prefixlen; src->u.prefix_flowspec.prefixlen;
dest->u.prefix_flowspec.family =
src->u.prefix_flowspec.family;
dest->family = src->family; dest->family = src->family;
temp = XCALLOC(MTYPE_PREFIX_FLOWSPEC, len); temp = XCALLOC(MTYPE_PREFIX_FLOWSPEC, len);
dest->u.prefix_flowspec.ptr = (uintptr_t)temp; 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))) sizeof(struct evpn_addr)))
return 1; return 1;
if (p1->family == AF_FLOWSPEC) { if (p1->family == AF_FLOWSPEC) {
if (p1->u.prefix_flowspec.family !=
p2->u.prefix_flowspec.family)
return 0;
if (p1->u.prefix_flowspec.prefixlen != if (p1->u.prefix_flowspec.prefixlen !=
p2->u.prefix_flowspec.prefixlen) p2->u.prefix_flowspec.prefixlen)
return 0; return 0;
@ -415,6 +424,10 @@ int prefix_cmp(union prefixconstptr up1, union prefixconstptr up2)
pp1 = (const uint8_t *)p1->u.prefix_flowspec.ptr; pp1 = (const uint8_t *)p1->u.prefix_flowspec.ptr;
pp2 = (const uint8_t *)p2->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 != if (p1->u.prefix_flowspec.prefixlen !=
p2->u.prefix_flowspec.prefixlen) p2->u.prefix_flowspec.prefixlen)
return numcmp(p1->u.prefix_flowspec.prefixlen, return numcmp(p1->u.prefix_flowspec.prefixlen,

View File

@ -176,6 +176,7 @@ struct evpn_addr {
#endif #endif
struct flowspec_prefix { struct flowspec_prefix {
uint8_t family;
uint16_t prefixlen; /* length in bytes */ uint16_t prefixlen; /* length in bytes */
uintptr_t ptr; uintptr_t ptr;
}; };

View File

View 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

View 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
}
}

View 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
!
!

View 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
}
}

View File

@ -0,0 +1,8 @@
!
hostname r1
password zebra
interface r1-eth0
ip address 10.0.1.1/24
ipv6 address 1001::1/112
!

View 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)

View File

@ -2841,6 +2841,7 @@ static inline void zread_ipset(ZAPI_HANDLER_ARGS)
zpi.vrf_id = zvrf->vrf->vrf_id; zpi.vrf_id = zvrf->vrf->vrf_id;
STREAM_GETL(s, zpi.unique); STREAM_GETL(s, zpi.unique);
STREAM_GETL(s, zpi.type); STREAM_GETL(s, zpi.type);
STREAM_GETC(s, zpi.family);
STREAM_GET(&zpi.ipset_name, s, ZEBRA_IPSET_NAME_SIZE); STREAM_GET(&zpi.ipset_name, s, ZEBRA_IPSET_NAME_SIZE);
if (hdr->command == ZEBRA_IPSET_CREATE) 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->action);
STREAM_GETL(s, zpi->fwmark); STREAM_GETL(s, zpi->fwmark);
STREAM_GET(&zpi->ipset_name, s, ZEBRA_IPSET_NAME_SIZE); 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_min);
STREAM_GETW(s, zpi->pkt_len_max); STREAM_GETW(s, zpi->pkt_len_max);
STREAM_GETW(s, zpi->tcp_flags); 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->dscp_value);
STREAM_GETC(s, zpi->fragment); STREAM_GETC(s, zpi->fragment);
STREAM_GETC(s, zpi->protocol); STREAM_GETC(s, zpi->protocol);
STREAM_GETW(s, zpi->flow_label);
STREAM_GETL(s, zpi->nb_interface); STREAM_GETL(s, zpi->nb_interface);
zebra_pbr_iptable_update_interfacelist(s, zpi); zebra_pbr_iptable_update_interfacelist(s, zpi);

View File

@ -83,6 +83,27 @@ const struct message icmp_typecode_str[] = {
{0} {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 */ /* definitions */
static const struct message tcp_value_str[] = { static const struct message tcp_value_str[] = {
{TCP_HEADER_FIN, "FIN"}, {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 *pnt = (uint32_t *)&ipset->ipset_name;
uint32_t key = jhash_1word(ipset->vrf_id, 0x63ab42de); uint32_t key = jhash_1word(ipset->vrf_id, 0x63ab42de);
key = jhash_1word(ipset->family, key);
return jhash2(pnt, ZEBRA_IPSET_NAME_HASH_SIZE, 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; return false;
if (r1->vrf_id != r2->vrf_id) if (r1->vrf_id != r2->vrf_id)
return false; return false;
if (r1->family != r2->family)
return false;
if (strncmp(r1->ipset_name, r2->ipset_name, if (strncmp(r1->ipset_name, r2->ipset_name,
ZEBRA_IPSET_NAME_SIZE)) 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, key = jhash2(pnt, ZEBRA_IPSET_NAME_HASH_SIZE,
0x63ab42de); 0x63ab42de);
key = jhash_1word(iptable->fwmark, key); 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_min, key);
key = jhash_1word(iptable->pkt_len_max, key); key = jhash_1word(iptable->pkt_len_max, key);
key = jhash_1word(iptable->tcp_flags, 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, if (strncmp(r1->ipset_name, r2->ipset_name,
ZEBRA_IPSET_NAME_SIZE)) ZEBRA_IPSET_NAME_SIZE))
return false; 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) if (r1->pkt_len_min != r2->pkt_len_min)
return false; return false;
if (r1->pkt_len_max != r2->pkt_len_max) 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; const struct prefix *p = pu.p;
char buf[PREFIX2STR_BUFFER]; 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, snprintf(str, size, "%s", inet_ntop(p->family, &p->u.prefix,
buf, PREFIX2STR_BUFFER)); buf, PREFIX2STR_BUFFER));
return str; return str;
@ -889,6 +921,9 @@ static void zebra_pbr_display_icmp(struct vty *vty,
{ {
char decoded_str[20]; char decoded_str[20];
uint16_t port; uint16_t port;
struct zebra_pbr_ipset *zpi;
zpi = zpie->backpointer;
/* range icmp type */ /* range icmp type */
if (zpie->src_port_max || zpie->dst_port_max) { 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)); memset(decoded_str, 0, sizeof(decoded_str));
snprintf(decoded_str, sizeof(decoded_str), "%u/%u", snprintf(decoded_str, sizeof(decoded_str), "%u/%u",
zpie->src_port_min, zpie->dst_port_min); zpie->src_port_min, zpie->dst_port_min);
vty_out(vty, ":icmp:%s", vty_out(vty, ":%s:%s",
lookup_msg(icmp_typecode_str, zpi->family == AF_INET6 ? "ipv6-icmp" : "icmp",
lookup_msg(zpi->family == AF_INET6 ?
icmpv6_typecode_str : icmp_typecode_str,
port, decoded_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 vty *vty = uniqueipset->vty;
struct zebra_ns *zns = uniqueipset->zns; struct zebra_ns *zns = uniqueipset->zns;
vty_out(vty, "IPset %s type %s\n", zpi->ipset_name, vty_out(vty, "IPset %s type %s family %s\n", zpi->ipset_name,
zebra_pbr_ipset_type2str(zpi->type)); zebra_pbr_ipset_type2str(zpi->type),
family2str(zpi->family));
unique.vty = vty; unique.vty = vty;
unique.zpi = zpi; unique.zpi = zpi;
unique.zns = zns; 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); vty_out(vty, "No IPset %s found\n", ipsetname);
return; return;
} }
vty_out(vty, "IPset %s type %s\n", ipsetname, vty_out(vty, "IPset %s type %s family %s\n", ipsetname,
zebra_pbr_ipset_type2str(zpi->type)); zebra_pbr_ipset_type2str(zpi->type),
family2str(zpi->family));
unique.vty = vty; unique.vty = vty;
unique.zpi = zpi; unique.zpi = zpi;
unique.zns = zns; unique.zns = zns;
@ -1101,7 +1139,9 @@ static void zebra_pbr_show_iptable_unit(struct zebra_pbr_iptable *iptable,
int ret; int ret;
uint64_t pkts = 0, bytes = 0; 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->action == ZEBRA_IPTABLES_DROP ? "drop" : "redirect",
iptable->unique); iptable->unique);
if (iptable->type == IPSET_NET_PORT || 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 ? iptable->filter_bm & MATCH_DSCP_INVERSE_SET ?
"not" : "", iptable->dscp_value); "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) { if (iptable->fragment) {
char val_str[10]; char val_str[10];

View File

@ -79,6 +79,9 @@ struct zebra_pbr_ipset {
* but value is an enum ipset_type * but value is an enum ipset_type
*/ */
uint32_t type; uint32_t type;
uint8_t family;
char ipset_name[ZEBRA_IPSET_NAME_SIZE]; char ipset_name[ZEBRA_IPSET_NAME_SIZE];
}; };
@ -150,6 +153,9 @@ struct zebra_pbr_iptable {
uint8_t protocol; uint8_t protocol;
uint32_t nb_interface; uint32_t nb_interface;
uint16_t flow_label;
uint8_t family;
struct list *interface_name_list; struct list *interface_name_list;
@ -157,6 +163,7 @@ struct zebra_pbr_iptable {
}; };
extern const struct message icmp_typecode_str[]; extern const struct message icmp_typecode_str[];
extern const struct message icmpv6_typecode_str[];
const char *zebra_pbr_ipset_type2str(uint32_t type); const char *zebra_pbr_ipset_type2str(uint32_t type);