From e4552d667aa43a518c5adaf3f06295be05c01d57 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Tue, 15 Oct 2019 15:01:39 +0200 Subject: [PATCH 01/19] lib: add family attribute for flowspec prefix structure to recognize whether a flowspec prefix has been carried out by ipv4 flowspec or ipv6 flowspec ( actually, the hypothesis is that only ipv4 flowspec is supported), then a new attribute should contain the family value: AF_INET or AF_INET6. That value will be further used in the BGP flowspec code. Signed-off-by: Philippe Guibert --- lib/prefix.c | 13 +++++++++++++ lib/prefix.h | 1 + 2 files changed, 14 insertions(+) diff --git a/lib/prefix.c b/lib/prefix.c index 697e1a6239..0a88f9eca6 100644 --- a/lib/prefix.c +++ b/lib/prefix.c @@ -188,6 +188,10 @@ int prefix_match(const struct prefix *n, const struct prefix *p) if (n->family == AF_FLOWSPEC) { /* prefixlen is unused. look at fs prefix len */ + if (n->u.prefix_flowspec.family != + p->u.prefix_flowspec.family) + return 0; + if (n->u.prefix_flowspec.prefixlen > p->u.prefix_flowspec.prefixlen) return 0; @@ -325,6 +329,8 @@ void prefix_copy(union prefixptr udest, union prefixconstptr usrc) len = src->u.prefix_flowspec.prefixlen; dest->u.prefix_flowspec.prefixlen = src->u.prefix_flowspec.prefixlen; + dest->u.prefix_flowspec.family = + src->u.prefix_flowspec.family; dest->family = src->family; temp = XCALLOC(MTYPE_PREFIX_FLOWSPEC, len); dest->u.prefix_flowspec.ptr = (uintptr_t)temp; @@ -374,6 +380,9 @@ int prefix_same(union prefixconstptr up1, union prefixconstptr up2) sizeof(struct evpn_addr))) return 1; if (p1->family == AF_FLOWSPEC) { + if (p1->u.prefix_flowspec.family != + p2->u.prefix_flowspec.family) + return 0; if (p1->u.prefix_flowspec.prefixlen != p2->u.prefix_flowspec.prefixlen) return 0; @@ -415,6 +424,10 @@ int prefix_cmp(union prefixconstptr up1, union prefixconstptr up2) pp1 = (const uint8_t *)p1->u.prefix_flowspec.ptr; pp2 = (const uint8_t *)p2->u.prefix_flowspec.ptr; + if (p1->u.prefix_flowspec.family != + p2->u.prefix_flowspec.family) + return 1; + if (p1->u.prefix_flowspec.prefixlen != p2->u.prefix_flowspec.prefixlen) return numcmp(p1->u.prefix_flowspec.prefixlen, diff --git a/lib/prefix.h b/lib/prefix.h index 400f07386f..2a33d532c8 100644 --- a/lib/prefix.h +++ b/lib/prefix.h @@ -176,6 +176,7 @@ struct evpn_addr { #endif struct flowspec_prefix { + uint8_t family; uint16_t prefixlen; /* length in bytes */ uintptr_t ptr; }; From 1840384bae2a0ab4d79781bda300ce8f54313d3b Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Mon, 14 Oct 2019 18:02:22 +0200 Subject: [PATCH 02/19] bgpd: flowspec code support for ipv6 until now, the assumption was done in bgp flowspec code that the information contained was an ipv4 flowspec prefix. now that it is possible to handle ipv4 or ipv6 flowspec prefixes, that information is stored in prefix_flowspec attribute. Also, some unlocking is done in order to process ipv4 and ipv6 flowspec entries. Signed-off-by: Philippe Guibert --- bgpd/bgp_debug.c | 3 ++- bgpd/bgp_flowspec.c | 17 ++++++++--------- bgpd/bgp_flowspec.h | 3 ++- bgpd/bgp_flowspec_util.c | 28 ++++++++++++++++++++-------- bgpd/bgp_flowspec_util.h | 8 +++++--- bgpd/bgp_flowspec_vty.c | 15 ++++++++++++--- bgpd/bgp_nht.c | 2 +- bgpd/bgp_pbr.c | 7 +++---- bgpd/bgp_pbr.h | 1 + bgpd/bgp_route.c | 4 +++- bgpd/bgp_routemap.c | 6 +++++- 11 files changed, 62 insertions(+), 32 deletions(-) diff --git a/bgpd/bgp_debug.c b/bgpd/bgp_debug.c index 255a7f238b..e9d7c9e8aa 100644 --- a/bgpd/bgp_debug.c +++ b/bgpd/bgp_debug.c @@ -2692,7 +2692,8 @@ const char *bgp_debug_rdpfxpath2str(afi_t afi, safi_t safi, bgp_fs_nlri_get_string((unsigned char *)fs->prefix.ptr, fs->prefix.prefixlen, return_string, - NLRI_STRING_FORMAT_DEBUG, NULL); + NLRI_STRING_FORMAT_DEBUG, NULL, + family2afi(fs->prefix.family)); snprintf(str, size, "FS %s Match{%s}", afi2str(afi), return_string); } else diff --git a/bgpd/bgp_flowspec.c b/bgpd/bgp_flowspec.c index 17c41636de..03fc1f52c3 100644 --- a/bgpd/bgp_flowspec.c +++ b/bgpd/bgp_flowspec.c @@ -33,7 +33,8 @@ #include "bgpd/bgp_debug.h" #include "bgpd/bgp_errors.h" -static int bgp_fs_nlri_validate(uint8_t *nlri_content, uint32_t len) +static int bgp_fs_nlri_validate(uint8_t *nlri_content, uint32_t len, + afi_t afi) { uint32_t offset = 0; int type; @@ -48,7 +49,8 @@ static int bgp_fs_nlri_validate(uint8_t *nlri_content, uint32_t len) ret = bgp_flowspec_ip_address( BGP_FLOWSPEC_VALIDATE_ONLY, nlri_content + offset, - len - offset, NULL, &error); + len - offset, NULL, &error, + afi); break; case FLOWSPEC_IP_PROTOCOL: case FLOWSPEC_PORT: @@ -103,11 +105,6 @@ int bgp_nlri_parse_flowspec(struct peer *peer, struct attr *attr, afi = packet->afi; safi = packet->safi; - if (afi == AFI_IP6) { - flog_err(EC_LIB_DEVELOPMENT, "BGP flowspec IPv6 not supported"); - return BGP_NLRI_PARSE_ERROR_FLOWSPEC_IPV6_NOT_SUPPORTED; - } - if (packet->length >= FLOWSPEC_NLRI_SIZELIMIT_EXTENDED) { flog_err(EC_BGP_FLOWSPEC_PACKET, "BGP flowspec nlri length maximum reached (%u)", @@ -137,7 +134,7 @@ int bgp_nlri_parse_flowspec(struct peer *peer, struct attr *attr, psize); return BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW; } - if (bgp_fs_nlri_validate(pnt, psize) < 0) { + if (bgp_fs_nlri_validate(pnt, psize, afi) < 0) { flog_err( EC_BGP_FLOWSPEC_PACKET, "Bad flowspec format or NLRI options not supported"); @@ -147,6 +144,7 @@ int bgp_nlri_parse_flowspec(struct peer *peer, struct attr *attr, p.prefixlen = 0; /* Flowspec encoding is in bytes */ p.u.prefix_flowspec.prefixlen = psize; + p.u.prefix_flowspec.family = afi2family(afi); temp = XCALLOC(MTYPE_TMP, psize); memcpy(temp, pnt, psize); p.u.prefix_flowspec.ptr = (uintptr_t) temp; @@ -161,7 +159,8 @@ int bgp_nlri_parse_flowspec(struct peer *peer, struct attr *attr, p.u.prefix_flowspec.ptr, p.u.prefix_flowspec.prefixlen, return_string, - NLRI_STRING_FORMAT_MIN, NULL); + NLRI_STRING_FORMAT_MIN, NULL, + afi); snprintf(ec_string, sizeof(ec_string), "EC{none}"); if (attr && attr->ecommunity) { diff --git a/bgpd/bgp_flowspec.h b/bgpd/bgp_flowspec.h index 94c571f2fc..d1a59a672d 100644 --- a/bgpd/bgp_flowspec.h +++ b/bgpd/bgp_flowspec.h @@ -41,7 +41,8 @@ extern int bgp_show_table_flowspec(struct vty *vty, struct bgp *bgp, afi_t afi, extern void bgp_fs_nlri_get_string(unsigned char *nlri_content, size_t len, char *return_string, int format, - json_object *json_path); + json_object *json_path, + afi_t afi); extern void route_vty_out_flowspec(struct vty *vty, const struct prefix *p, struct bgp_path_info *path, int display, diff --git a/bgpd/bgp_flowspec_util.c b/bgpd/bgp_flowspec_util.c index 764d2f87ae..0e879e8168 100644 --- a/bgpd/bgp_flowspec_util.c +++ b/bgpd/bgp_flowspec_util.c @@ -76,6 +76,7 @@ static int bgp_flowspec_call_non_opaque_decode(uint8_t *nlri_content, int len, return ret; } + bool bgp_flowspec_contains_prefix(const struct prefix *pfs, struct prefix *input, int prefix_check) { @@ -84,6 +85,7 @@ bool bgp_flowspec_contains_prefix(const struct prefix *pfs, int ret = 0, error = 0; uint8_t *nlri_content = (uint8_t *)pfs->u.prefix_flowspec.ptr; size_t len = pfs->u.prefix_flowspec.prefixlen; + afi_t afi = family2afi(pfs->u.prefix_flowspec.family); struct prefix compare; error = 0; @@ -98,7 +100,8 @@ bool bgp_flowspec_contains_prefix(const struct prefix *pfs, BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE, nlri_content+offset, len - offset, - &compare, &error); + &compare, &error, + afi); if (ret <= 0) break; if (prefix_check && @@ -158,7 +161,8 @@ bool bgp_flowspec_contains_prefix(const struct prefix *pfs, int bgp_flowspec_ip_address(enum bgp_flowspec_util_nlri_t type, uint8_t *nlri_ptr, uint32_t max_len, - void *result, int *error) + void *result, int *error, + afi_t afi) { char *display = (char *)result; /* for return_string */ struct prefix *prefix = (struct prefix *)result; @@ -417,7 +421,8 @@ int bgp_flowspec_bitmask_decode(enum bgp_flowspec_util_nlri_t type, } int bgp_flowspec_match_rules_fill(uint8_t *nlri_content, int len, - struct bgp_pbr_entry_main *bpem) + struct bgp_pbr_entry_main *bpem, + afi_t afi) { int offset = 0, error = 0; struct prefix *prefix; @@ -444,7 +449,8 @@ int bgp_flowspec_match_rules_fill(uint8_t *nlri_content, int len, BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE, nlri_content + offset, len - offset, - prefix, &error); + prefix, &error, + afi); if (error < 0) flog_err(EC_BGP_FLOWSPEC_PACKET, "%s: flowspec_ip_address error %d", @@ -599,7 +605,7 @@ int bgp_flowspec_match_rules_fill(uint8_t *nlri_content, int len, /* return 1 if FS entry invalid or no NH IP */ bool bgp_flowspec_get_first_nh(struct bgp *bgp, struct bgp_path_info *pi, - struct prefix *p) + struct prefix *p, afi_t afi) { struct bgp_pbr_entry_main api; int i; @@ -615,9 +621,15 @@ bool bgp_flowspec_get_first_nh(struct bgp *bgp, struct bgp_path_info *pi, api_action = &api.actions[i]; if (api_action->action != ACTION_REDIRECT_IP) continue; - p->family = AF_INET; - p->prefixlen = IPV4_MAX_BITLEN; - p->u.prefix4 = api_action->u.zr.redirect_ip_v4; + p->family = afi2family(afi); + if (afi == AFI_IP) { + p->prefixlen = IPV4_MAX_BITLEN; + p->u.prefix4 = api_action->u.zr.redirect_ip_v4; + } else { + p->prefixlen = IPV6_MAX_BITLEN; + memcpy(&p->u.prefix6, &api_action->u.zr.redirect_ip_v6, + sizeof(struct in6_addr)); + } return false; } return true; diff --git a/bgpd/bgp_flowspec_util.h b/bgpd/bgp_flowspec_util.h index 0e78c7a53c..26d919d7d2 100644 --- a/bgpd/bgp_flowspec_util.h +++ b/bgpd/bgp_flowspec_util.h @@ -39,7 +39,8 @@ extern int bgp_flowspec_op_decode(enum bgp_flowspec_util_nlri_t type, extern int bgp_flowspec_ip_address(enum bgp_flowspec_util_nlri_t type, uint8_t *nlri_ptr, uint32_t max_len, - void *result, int *error); + void *result, int *error, + afi_t afi); extern int bgp_flowspec_bitmask_decode(enum bgp_flowspec_util_nlri_t type, uint8_t *nlri_ptr, @@ -48,13 +49,14 @@ extern int bgp_flowspec_bitmask_decode(enum bgp_flowspec_util_nlri_t type, struct bgp_pbr_entry_main; extern int bgp_flowspec_match_rules_fill(uint8_t *nlri_content, int len, - struct bgp_pbr_entry_main *bpem); + struct bgp_pbr_entry_main *bpem, + afi_t afi); extern bool bgp_flowspec_contains_prefix(const struct prefix *pfs, struct prefix *input, int prefix_check); extern bool bgp_flowspec_get_first_nh(struct bgp *bgp, struct bgp_path_info *pi, - struct prefix *nh); + struct prefix *nh, afi_t afi); #endif /* _FRR_BGP_FLOWSPEC_UTIL_H */ diff --git a/bgpd/bgp_flowspec_vty.c b/bgpd/bgp_flowspec_vty.c index e309fa948e..5703d86be3 100644 --- a/bgpd/bgp_flowspec_vty.c +++ b/bgpd/bgp_flowspec_vty.c @@ -93,7 +93,8 @@ static const struct message bgp_flowspec_display_min[] = { */ void bgp_fs_nlri_get_string(unsigned char *nlri_content, size_t len, char *return_string, int format, - json_object *json_path) + json_object *json_path, + afi_t afi) { uint32_t offset = 0; int type; @@ -127,7 +128,8 @@ void bgp_fs_nlri_get_string(unsigned char *nlri_content, size_t len, type_util, nlri_content+offset, len - offset, - local_string, &error); + local_string, &error, + afi); if (ret <= 0) break; if (json_path) { @@ -263,7 +265,12 @@ void route_vty_out_flowspec(struct vty *vty, const struct prefix *p, json_object *json_ecom_path = NULL; json_object *json_time_path = NULL; char timebuf[BGP_UPTIME_LEN]; + struct bgp_dest *dest = NULL; + if (path) + dest = path->net; + if (dest) + bgp_dest_get_bgp_table_info(dest); /* Print prefix */ if (p != NULL) { if (p->family != AF_FLOWSPEC) @@ -282,7 +289,9 @@ void route_vty_out_flowspec(struct vty *vty, const struct prefix *p, p->u.prefix_flowspec.prefixlen, return_string, display, - json_nlri_path); + json_nlri_path, + family2afi(p->u.prefix_flowspec + .family)); if (display == NLRI_STRING_FORMAT_LARGE) vty_out(vty, "%s", return_string); else if (display == NLRI_STRING_FORMAT_DEBUG) diff --git a/bgpd/bgp_nht.c b/bgpd/bgp_nht.c index a74b5f91ac..a780fb7347 100644 --- a/bgpd/bgp_nht.c +++ b/bgpd/bgp_nht.c @@ -544,7 +544,7 @@ static int make_prefix(int afi, struct bgp_path_info *pi, struct prefix *p) if (!pi->peer) return -1; return bgp_flowspec_get_first_nh(pi->peer->bgp, - pi, p); + pi, p, afi); } memset(p, 0, sizeof(struct prefix)); switch (afi) { diff --git a/bgpd/bgp_pbr.c b/bgpd/bgp_pbr.c index 7c3e8cd70e..38b1ae4da9 100644 --- a/bgpd/bgp_pbr.c +++ b/bgpd/bgp_pbr.c @@ -668,6 +668,7 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api) } /* return -1 if build or validation failed */ + int bgp_pbr_build_and_validate_entry(const struct prefix *p, struct bgp_path_info *path, struct bgp_pbr_entry_main *api) @@ -679,13 +680,13 @@ int bgp_pbr_build_and_validate_entry(const struct prefix *p, struct bgp_pbr_entry_action *api_action; struct prefix *src = NULL, *dst = NULL; int valid_prefix = 0; - afi_t afi = AFI_IP; struct bgp_pbr_entry_action *api_action_redirect_ip = NULL; bool discard_action_found = false; + afi_t afi = family2afi(p->u.prefix_flowspec.family); /* extract match from flowspec entries */ ret = bgp_flowspec_match_rules_fill((uint8_t *)p->u.prefix_flowspec.ptr, - p->u.prefix_flowspec.prefixlen, api); + p->u.prefix_flowspec.prefixlen, api, afi); if (ret < 0) return -1; /* extract actiosn from flowspec ecom list */ @@ -2598,8 +2599,6 @@ void bgp_pbr_update_entry(struct bgp *bgp, const struct prefix *p, { struct bgp_pbr_entry_main api; - if (afi == AFI_IP6) - return; /* IPv6 not supported */ if (safi != SAFI_FLOWSPEC) return; /* not supported */ /* Make Zebra API structure. */ diff --git a/bgpd/bgp_pbr.h b/bgpd/bgp_pbr.h index 043853a157..52bb3b5410 100644 --- a/bgpd/bgp_pbr.h +++ b/bgpd/bgp_pbr.h @@ -79,6 +79,7 @@ struct bgp_pbr_entry_action { vrf_id_t redirect_vrf; struct _pbr_redirect_ip { struct in_addr redirect_ip_v4; + struct in6_addr redirect_ip_v6; uint8_t duplicate; } zr; uint8_t marking_dscp; diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 3a627b4486..eefff4bef8 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -10008,11 +10008,13 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi, if (dest_p->family == AF_FLOWSPEC) { char retstr[BGP_FLOWSPEC_STRING_DISPLAY_MAX]; + bgp_fs_nlri_get_string( (unsigned char *) dest_p->u.prefix_flowspec.ptr, dest_p->u.prefix_flowspec.prefixlen, - retstr, NLRI_STRING_FORMAT_MIN, NULL); + retstr, NLRI_STRING_FORMAT_MIN, NULL, + family2afi(dest_p->u.prefix_flowspec.family)); if (first) vty_out(vty, "\"%s/%d\": ", retstr, dest_p->u.prefix_flowspec diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index 97153cfb72..5644daba62 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -594,10 +594,14 @@ route_match_prefix_list_flowspec(afi_t afi, struct prefix_list *plist, memset(&api, 0, sizeof(api)); + if (family2afi(p->u.prefix_flowspec.family) != afi) + return RMAP_NOMATCH; + /* extract match from flowspec entries */ ret = bgp_flowspec_match_rules_fill( (uint8_t *)p->u.prefix_flowspec.ptr, - p->u.prefix_flowspec.prefixlen, &api); + p->u.prefix_flowspec.prefixlen, &api, + afi); if (ret < 0) return RMAP_NOMATCH; if (api.match_bitmask & PREFIX_DST_PRESENT || From 9cec4121623c18eb5d826420e2078e2a78242362 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Wed, 16 Oct 2019 08:42:30 +0200 Subject: [PATCH 03/19] bgpd: ipv6 flowspec address decoding and validation as per [0], ipv6 adress format introduces an ipv6 offset that needs to be extracted too. The change include the validation, decoding for further usage with policy-routing and decoding for dumping. [0] https://tools.ietf.org/html/draft-ietf-idr-flow-spec-v6-09 Signed-off-by: Philippe Guibert --- bgpd/bgp_flowspec.c | 2 +- bgpd/bgp_flowspec_util.c | 48 +++++-- bgpd/bgp_flowspec_util.h | 2 +- bgpd/bgp_flowspec_vty.c | 2 +- bgpd/bgp_pbr.c | 288 ++++++++++++++++++++++++++------------- bgpd/bgp_pbr.h | 2 + 6 files changed, 240 insertions(+), 104 deletions(-) diff --git a/bgpd/bgp_flowspec.c b/bgpd/bgp_flowspec.c index 03fc1f52c3..19db1159c1 100644 --- a/bgpd/bgp_flowspec.c +++ b/bgpd/bgp_flowspec.c @@ -50,7 +50,7 @@ static int bgp_fs_nlri_validate(uint8_t *nlri_content, uint32_t len, BGP_FLOWSPEC_VALIDATE_ONLY, nlri_content + offset, len - offset, NULL, &error, - afi); + afi, NULL); break; case FLOWSPEC_IP_PROTOCOL: case FLOWSPEC_PORT: diff --git a/bgpd/bgp_flowspec_util.c b/bgpd/bgp_flowspec_util.c index 0e879e8168..2dd35696d0 100644 --- a/bgpd/bgp_flowspec_util.c +++ b/bgpd/bgp_flowspec_util.c @@ -101,7 +101,7 @@ bool bgp_flowspec_contains_prefix(const struct prefix *pfs, nlri_content+offset, len - offset, &compare, &error, - afi); + afi, NULL); if (ret <= 0) break; if (prefix_check && @@ -162,13 +162,15 @@ int bgp_flowspec_ip_address(enum bgp_flowspec_util_nlri_t type, uint8_t *nlri_ptr, uint32_t max_len, void *result, int *error, - afi_t afi) + afi_t afi, + uint8_t *ipv6_offset) { char *display = (char *)result; /* for return_string */ struct prefix *prefix = (struct prefix *)result; uint32_t offset = 0; struct prefix prefix_local; int psize; + uint8_t prefix_offset = 0; *error = 0; memset(&prefix_local, 0, sizeof(struct prefix)); @@ -176,8 +178,13 @@ int bgp_flowspec_ip_address(enum bgp_flowspec_util_nlri_t type, prefix_local.prefixlen = nlri_ptr[offset]; psize = PSIZE(prefix_local.prefixlen); offset++; - /* TODO Flowspec IPv6 Support */ - prefix_local.family = AF_INET; + prefix_local.family = afi2family(afi); + if (prefix_local.family == AF_INET6) { + prefix_offset = nlri_ptr[offset]; + if (ipv6_offset) + *ipv6_offset = prefix_offset; + offset++; + } /* Prefix length check. */ if (prefix_local.prefixlen > prefix_blen(&prefix_local) * 8) *error = -1; @@ -193,11 +200,28 @@ int bgp_flowspec_ip_address(enum bgp_flowspec_util_nlri_t type, offset += psize; switch (type) { case BGP_FLOWSPEC_RETURN_STRING: - prefix2str(&prefix_local, display, - BGP_FLOWSPEC_STRING_DISPLAY_MAX); + if (prefix_local.family == AF_INET6) { + char str[BGP_FLOWSPEC_STRING_DISPLAY_MAX]; + int ret; + + prefix2str(&prefix_local, str, + BGP_FLOWSPEC_STRING_DISPLAY_MAX); + ret = snprintf(display, BGP_FLOWSPEC_STRING_DISPLAY_MAX, + "%s/off %u", + str, prefix_offset); + if (ret < 0) { + *error = -1; + break; + } + } else + prefix2str(&prefix_local, display, + BGP_FLOWSPEC_STRING_DISPLAY_MAX); break; case BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE: - PREFIX_COPY_IPV4(prefix, &prefix_local) + if (prefix_local.family == AF_INET) + PREFIX_COPY_IPV4(prefix, &prefix_local) + else + PREFIX_COPY_IPV6(prefix, &prefix_local) break; case BGP_FLOWSPEC_VALIDATE_ONLY: default: @@ -430,6 +454,7 @@ int bgp_flowspec_match_rules_fill(uint8_t *nlri_content, int len, uint8_t *match_num; uint8_t bitmask = 0; int ret = 0, type; + uint8_t *prefix_offset; while (offset < len - 1 && error >= 0) { type = nlri_content[offset]; @@ -441,16 +466,18 @@ int bgp_flowspec_match_rules_fill(uint8_t *nlri_content, int len, if (type == FLOWSPEC_DEST_PREFIX) { bitmask |= PREFIX_DST_PRESENT; prefix = &bpem->dst_prefix; + prefix_offset = &bpem->dst_prefix_offset; } else { bitmask |= PREFIX_SRC_PRESENT; prefix = &bpem->src_prefix; + prefix_offset = &bpem->src_prefix_offset; } ret = bgp_flowspec_ip_address( BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE, nlri_content + offset, len - offset, prefix, &error, - afi); + afi, prefix_offset); if (error < 0) flog_err(EC_BGP_FLOWSPEC_PACKET, "%s: flowspec_ip_address error %d", @@ -462,6 +489,11 @@ int bgp_flowspec_match_rules_fill(uint8_t *nlri_content, int len, if (prefix->family == AF_INET && prefix->u.prefix4.s_addr == INADDR_ANY) bpem->match_bitmask_iprule |= bitmask; + else if (prefix->family == AF_INET6 + && !memcmp(&prefix->u.prefix6, + &in6addr_any, + sizeof(struct in6_addr))) + bpem->match_bitmask_iprule |= bitmask; else bpem->match_bitmask |= bitmask; } diff --git a/bgpd/bgp_flowspec_util.h b/bgpd/bgp_flowspec_util.h index 26d919d7d2..6cc4854d7f 100644 --- a/bgpd/bgp_flowspec_util.h +++ b/bgpd/bgp_flowspec_util.h @@ -40,7 +40,7 @@ extern int bgp_flowspec_ip_address(enum bgp_flowspec_util_nlri_t type, uint8_t *nlri_ptr, uint32_t max_len, void *result, int *error, - afi_t afi); + afi_t afi, uint8_t *ipv6_offset); extern int bgp_flowspec_bitmask_decode(enum bgp_flowspec_util_nlri_t type, uint8_t *nlri_ptr, diff --git a/bgpd/bgp_flowspec_vty.c b/bgpd/bgp_flowspec_vty.c index 5703d86be3..d3e95b123f 100644 --- a/bgpd/bgp_flowspec_vty.c +++ b/bgpd/bgp_flowspec_vty.c @@ -129,7 +129,7 @@ void bgp_fs_nlri_get_string(unsigned char *nlri_content, size_t len, nlri_content+offset, len - offset, local_string, &error, - afi); + afi, NULL); if (ret <= 0) break; if (json_path) { diff --git a/bgpd/bgp_pbr.c b/bgpd/bgp_pbr.c index 38b1ae4da9..c20bade97f 100644 --- a/bgpd/bgp_pbr.c +++ b/bgpd/bgp_pbr.c @@ -168,35 +168,61 @@ static int bgp_pbr_match_walkcb(struct hash_bucket *bucket, void *arg) return HASHWALK_CONTINUE; } -static int sprintf_bgp_pbr_match_val(char *str, struct bgp_pbr_match_val *mval, - const char *prepend) +static int snprintf_bgp_pbr_match_val(char *str, int len, struct bgp_pbr_match_val *mval, + const char *prepend) { char *ptr = str; + int delta; - if (prepend) - ptr += sprintf(ptr, "%s", prepend); - else { - if (mval->unary_operator & OPERATOR_UNARY_OR) - ptr += sprintf(ptr, ", or "); - if (mval->unary_operator & OPERATOR_UNARY_AND) - ptr += sprintf(ptr, ", and "); + if (prepend) { + delta = snprintf(ptr, len, "%s", prepend); + ptr += delta; + len -= delta; + } else { + if (mval->unary_operator & OPERATOR_UNARY_OR) { + delta = snprintf(ptr, len, ", or "); + ptr += delta; + len -= delta; + } + if (mval->unary_operator & OPERATOR_UNARY_AND) { + delta = snprintf(ptr, len, ", and "); + ptr += delta; + len -= delta; + } } - if (mval->compare_operator & OPERATOR_COMPARE_LESS_THAN) - ptr += sprintf(ptr, "<"); - if (mval->compare_operator & OPERATOR_COMPARE_GREATER_THAN) - ptr += sprintf(ptr, ">"); - if (mval->compare_operator & OPERATOR_COMPARE_EQUAL_TO) - ptr += sprintf(ptr, "="); - if (mval->compare_operator & OPERATOR_COMPARE_EXACT_MATCH) - ptr += sprintf(ptr, "match"); - ptr += sprintf(ptr, " %u", mval->value); + if (mval->compare_operator & OPERATOR_COMPARE_LESS_THAN) { + delta = snprintf(ptr, len, "<"); + ptr += delta; + len -= delta; + } + if (mval->compare_operator & OPERATOR_COMPARE_GREATER_THAN) { + delta = snprintf(ptr, len, ">"); + ptr += delta; + len -= delta; + } + if (mval->compare_operator & OPERATOR_COMPARE_EQUAL_TO) { + delta = snprintf(ptr, len, "="); + ptr += delta; + len -= delta; + } + if (mval->compare_operator & OPERATOR_COMPARE_EXACT_MATCH) { + delta = snprintf(ptr, len, "match"); + ptr += delta; + len -= delta; + } + ptr += snprintf(ptr, len, " %u", mval->value); return (int)(ptr - str); } -#define INCREMENT_DISPLAY(_ptr, _cnt) do { \ - if (_cnt) \ - (_ptr) += sprintf((_ptr), "; "); \ - _cnt++; \ +#define INCREMENT_DISPLAY(_ptr, _cnt, _len) do { \ + int sn_delta; \ + \ + if (_cnt) { \ + sn_delta = snprintf((_ptr), (_len), "; ");\ + (_len) -= sn_delta; \ + (_ptr) += sn_delta; \ + } \ + (_cnt)++; \ } while (0) /* this structure can be used for port range, @@ -1275,132 +1301,208 @@ void bgp_pbr_print_policy_route(struct bgp_pbr_entry_main *api) char *ptr = return_string; char buff[64]; int nb_items = 0; + int delta, len = sizeof(return_string); - ptr += sprintf(ptr, "MATCH : "); + delta = snprintf(ptr, sizeof(return_string), "MATCH : "); + len -= delta; + ptr += delta; if (api->match_bitmask & PREFIX_SRC_PRESENT) { struct prefix *p = &(api->src_prefix); - ptr += sprintf(ptr, "@src %s", prefix2str(p, buff, 64)); - INCREMENT_DISPLAY(ptr, nb_items); + if (api->src_prefix_offset) + delta = snprintf(ptr, len, "@src %s/off%u", + prefix2str(p, buff, 64), + api->src_prefix_offset); + else + delta = snprintf(ptr, len, "@src %s", + prefix2str(p, buff, 64)); + len -= delta; + ptr += delta; + INCREMENT_DISPLAY(ptr, nb_items, len); } if (api->match_bitmask & PREFIX_DST_PRESENT) { struct prefix *p = &(api->dst_prefix); - INCREMENT_DISPLAY(ptr, nb_items); - ptr += sprintf(ptr, "@dst %s", prefix2str(p, buff, 64)); + INCREMENT_DISPLAY(ptr, nb_items, len); + if (api->dst_prefix_offset) + delta = snprintf(ptr, len, "@dst %s/off%u", + prefix2str(p, buff, 64), + api->dst_prefix_offset); + else + delta = snprintf(ptr, len, "@dst %s", prefix2str(p, buff, 64)); + len -= delta; + ptr += delta; } if (api->match_protocol_num) - INCREMENT_DISPLAY(ptr, nb_items); - for (i = 0; i < api->match_protocol_num; i++) - ptr += sprintf_bgp_pbr_match_val(ptr, &api->protocol[i], - i > 0 ? NULL : "@proto "); + INCREMENT_DISPLAY(ptr, nb_items, len); + for (i = 0; i < api->match_protocol_num; i++) { + delta = snprintf_bgp_pbr_match_val(ptr, len, &api->protocol[i], + i > 0 ? NULL : "@proto "); + len -= delta; + ptr += delta; + } if (api->match_src_port_num) - INCREMENT_DISPLAY(ptr, nb_items); - for (i = 0; i < api->match_src_port_num; i++) - ptr += sprintf_bgp_pbr_match_val(ptr, &api->src_port[i], - i > 0 ? NULL : "@srcport "); + INCREMENT_DISPLAY(ptr, nb_items, len); + for (i = 0; i < api->match_src_port_num; i++) { + delta = snprintf_bgp_pbr_match_val(ptr, len, &api->src_port[i], + i > 0 ? NULL : "@srcport "); + len -= delta; + ptr += delta; + } if (api->match_dst_port_num) - INCREMENT_DISPLAY(ptr, nb_items); - for (i = 0; i < api->match_dst_port_num; i++) - ptr += sprintf_bgp_pbr_match_val(ptr, &api->dst_port[i], - i > 0 ? NULL : "@dstport "); + INCREMENT_DISPLAY(ptr, nb_items, len); + for (i = 0; i < api->match_dst_port_num; i++) { + delta = snprintf_bgp_pbr_match_val(ptr, len, &api->dst_port[i], + i > 0 ? NULL : "@dstport "); + len -= delta; + ptr += delta; + } if (api->match_port_num) - INCREMENT_DISPLAY(ptr, nb_items); - for (i = 0; i < api->match_port_num; i++) - ptr += sprintf_bgp_pbr_match_val(ptr, &api->port[i], - i > 0 ? NULL : "@port "); + INCREMENT_DISPLAY(ptr, nb_items, len); + for (i = 0; i < api->match_port_num; i++) { + delta = snprintf_bgp_pbr_match_val(ptr, len, &api->port[i], + i > 0 ? NULL : "@port "); + len -= delta; + ptr += delta; + } if (api->match_icmp_type_num) - INCREMENT_DISPLAY(ptr, nb_items); - for (i = 0; i < api->match_icmp_type_num; i++) - ptr += sprintf_bgp_pbr_match_val(ptr, &api->icmp_type[i], - i > 0 ? NULL : "@icmptype "); + INCREMENT_DISPLAY(ptr, nb_items, len); + for (i = 0; i < api->match_icmp_type_num; i++) { + delta = snprintf_bgp_pbr_match_val(ptr, len, &api->icmp_type[i], + i > 0 ? NULL : "@icmptype "); + len -= delta; + ptr += delta; + } if (api->match_icmp_code_num) - INCREMENT_DISPLAY(ptr, nb_items); - for (i = 0; i < api->match_icmp_code_num; i++) - ptr += sprintf_bgp_pbr_match_val(ptr, &api->icmp_code[i], - i > 0 ? NULL : "@icmpcode "); + INCREMENT_DISPLAY(ptr, nb_items, len); + for (i = 0; i < api->match_icmp_code_num; i++) { + delta = snprintf_bgp_pbr_match_val(ptr, len, &api->icmp_code[i], + i > 0 ? NULL : "@icmpcode "); + len -= delta; + ptr += delta; + } if (api->match_packet_length_num) - INCREMENT_DISPLAY(ptr, nb_items); - for (i = 0; i < api->match_packet_length_num; i++) - ptr += sprintf_bgp_pbr_match_val(ptr, &api->packet_length[i], - i > 0 ? NULL : "@plen "); + INCREMENT_DISPLAY(ptr, nb_items, len); + for (i = 0; i < api->match_packet_length_num; i++) { + delta = snprintf_bgp_pbr_match_val(ptr, len, &api->packet_length[i], + i > 0 ? NULL : "@plen "); + len -= delta; + ptr += delta; + } if (api->match_dscp_num) - INCREMENT_DISPLAY(ptr, nb_items); - for (i = 0; i < api->match_dscp_num; i++) - ptr += sprintf_bgp_pbr_match_val(ptr, &api->dscp[i], - i > 0 ? NULL : "@dscp "); + INCREMENT_DISPLAY(ptr, nb_items, len); + for (i = 0; i < api->match_dscp_num; i++) { + delta = snprintf_bgp_pbr_match_val(ptr, len, &api->dscp[i], + i > 0 ? NULL : "@dscp "); + len -= delta; + ptr += delta; + } if (api->match_tcpflags_num) - INCREMENT_DISPLAY(ptr, nb_items); - for (i = 0; i < api->match_tcpflags_num; i++) - ptr += sprintf_bgp_pbr_match_val(ptr, &api->tcpflags[i], - i > 0 ? NULL : "@tcpflags "); + INCREMENT_DISPLAY(ptr, nb_items, len); + for (i = 0; i < api->match_tcpflags_num; i++) { + delta = snprintf_bgp_pbr_match_val(ptr, len, &api->tcpflags[i], + i > 0 ? NULL : "@tcpflags "); + len -= delta; + ptr += delta; + } if (api->match_fragment_num) - INCREMENT_DISPLAY(ptr, nb_items); - for (i = 0; i < api->match_fragment_num; i++) - ptr += sprintf_bgp_pbr_match_val(ptr, &api->fragment[i], - i > 0 ? NULL : "@fragment "); - if (!nb_items) + INCREMENT_DISPLAY(ptr, nb_items, len); + for (i = 0; i < api->match_fragment_num; i++) { + delta = snprintf_bgp_pbr_match_val(ptr, len, &api->fragment[i], + i > 0 ? NULL : "@fragment "); + len -= delta; + ptr += delta; + } + + len = sizeof(return_string); + if (!nb_items) { ptr = return_string; - else - ptr += sprintf(ptr, "; "); - if (api->action_num) - ptr += sprintf(ptr, "SET : "); + } else { + len -= (ptr - return_string); + delta = snprintf(ptr, len, "; "); + len -= delta; + ptr += delta; + } + if (api->action_num) { + delta = snprintf(ptr, len, "SET : "); + len -= delta; + ptr += delta; + } nb_items = 0; for (i = 0; i < api->action_num; i++) { switch (api->actions[i].action) { case ACTION_TRAFFICRATE: - INCREMENT_DISPLAY(ptr, nb_items); - ptr += sprintf(ptr, "@set rate %f", - api->actions[i].u.r.rate); + INCREMENT_DISPLAY(ptr, nb_items, len); + delta = snprintf(ptr, len, "@set rate %f", + api->actions[i].u.r.rate); + len -= delta; + ptr += delta; break; case ACTION_TRAFFIC_ACTION: - INCREMENT_DISPLAY(ptr, nb_items); - ptr += sprintf(ptr, "@action "); + INCREMENT_DISPLAY(ptr, nb_items, len); + delta = snprintf(ptr, len, "@action "); + len -= delta; + ptr += delta; if (api->actions[i].u.za.filter - & TRAFFIC_ACTION_TERMINATE) - ptr += sprintf(ptr, - " terminate (apply filter(s))"); + & TRAFFIC_ACTION_TERMINATE) { + delta = snprintf(ptr, len, + " terminate (apply filter(s))"); + len -= delta; + ptr += delta; + } if (api->actions[i].u.za.filter - & TRAFFIC_ACTION_DISTRIBUTE) - ptr += sprintf(ptr, " distribute"); + & TRAFFIC_ACTION_DISTRIBUTE) { + delta = snprintf(ptr, len, " distribute"); + len -= delta; + ptr += delta; + } if (api->actions[i].u.za.filter - & TRAFFIC_ACTION_SAMPLE) - ptr += sprintf(ptr, " sample"); + & TRAFFIC_ACTION_SAMPLE) { + delta = snprintf(ptr, len, " sample"); + len -= delta; + ptr += delta; + } break; case ACTION_REDIRECT_IP: - INCREMENT_DISPLAY(ptr, nb_items); char local_buff[INET_ADDRSTRLEN]; + INCREMENT_DISPLAY(ptr, nb_items, len); if (inet_ntop(AF_INET, &api->actions[i].u.zr.redirect_ip_v4, local_buff, INET_ADDRSTRLEN) != NULL) - ptr += sprintf(ptr, + delta = snprintf(ptr, len, "@redirect ip nh %s", local_buff); + len -= delta; + ptr += delta; break; case ACTION_REDIRECT: { struct vrf *vrf; vrf = vrf_lookup_by_id(api->actions[i].u.redirect_vrf); - INCREMENT_DISPLAY(ptr, nb_items); - ptr += sprintf(ptr, "@redirect vrf %s(%u)", - VRF_LOGNAME(vrf), - api->actions[i].u.redirect_vrf); + INCREMENT_DISPLAY(ptr, nb_items, len); + delta = snprintf(ptr, len, "@redirect vrf %s(%u)", + VRF_LOGNAME(vrf), + api->actions[i].u.redirect_vrf); + len -= delta; + ptr += delta; break; } case ACTION_MARKING: - INCREMENT_DISPLAY(ptr, nb_items); - ptr += sprintf(ptr, "@set dscp %u", - api->actions[i].u.marking_dscp); + INCREMENT_DISPLAY(ptr, nb_items, len); + delta = snprintf(ptr, len, "@set dscp %u", + api->actions[i].u.marking_dscp); + len -= delta; + ptr += delta; break; default: break; diff --git a/bgpd/bgp_pbr.h b/bgpd/bgp_pbr.h index 52bb3b5410..ff771f88d3 100644 --- a/bgpd/bgp_pbr.h +++ b/bgpd/bgp_pbr.h @@ -118,6 +118,8 @@ struct bgp_pbr_entry_main { struct prefix src_prefix; struct prefix dst_prefix; + uint8_t src_prefix_offset; + uint8_t dst_prefix_offset; #define PROTOCOL_UDP 17 #define PROTOCOL_TCP 6 From 4088180002478f772332aefbac54f9148f20018f Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Wed, 16 Oct 2019 08:44:20 +0200 Subject: [PATCH 04/19] bgpd, lib: support for flow_label flowspec type in ipv6 flowspec, a new type is defined to be able to do filtering rules based on 20 bits flow label field as depicted in [0]. The change include the decoding by flowspec, and the addition of a new attribute in policy routing rule, so that the data is ready to be sent to zebra. The commit also includes a check on fragment option, since dont fragment bit does not exist in ipv6, the value should always be set to 0, otherwise the flowspec rule becomes invalid. [0] https://tools.ietf.org/html/draft-ietf-idr-flow-spec-v6-09 Signed-off-by: Philippe Guibert --- bgpd/bgp_flowspec.c | 7 +++ bgpd/bgp_flowspec_private.h | 1 + bgpd/bgp_flowspec_util.c | 27 +++++++++++- bgpd/bgp_flowspec_vty.c | 3 ++ bgpd/bgp_pbr.c | 85 +++++++++++++++++++++++++++++++++++-- bgpd/bgp_pbr.h | 3 ++ lib/pbr.h | 2 + 7 files changed, 123 insertions(+), 5 deletions(-) diff --git a/bgpd/bgp_flowspec.c b/bgpd/bgp_flowspec.c index 19db1159c1..341cfe9d07 100644 --- a/bgpd/bgp_flowspec.c +++ b/bgpd/bgp_flowspec.c @@ -52,6 +52,13 @@ static int bgp_fs_nlri_validate(uint8_t *nlri_content, uint32_t len, len - offset, NULL, &error, afi, NULL); break; + case FLOWSPEC_FLOW_LABEL: + if (afi == AFI_IP) + return -1; + ret = bgp_flowspec_op_decode(BGP_FLOWSPEC_VALIDATE_ONLY, + nlri_content + offset, + len - offset, NULL, &error); + break; case FLOWSPEC_IP_PROTOCOL: case FLOWSPEC_PORT: case FLOWSPEC_DEST_PORT: diff --git a/bgpd/bgp_flowspec_private.h b/bgpd/bgp_flowspec_private.h index cec244c165..757a8ae2e9 100644 --- a/bgpd/bgp_flowspec_private.h +++ b/bgpd/bgp_flowspec_private.h @@ -41,5 +41,6 @@ #define FLOWSPEC_PKT_LEN 10 #define FLOWSPEC_DSCP 11 #define FLOWSPEC_FRAGMENT 12 +#define FLOWSPEC_FLOW_LABEL 13 /* For IPv6 only */ #endif /* _FRR_BGP_FLOWSPEC_PRIVATE_H */ diff --git a/bgpd/bgp_flowspec_util.c b/bgpd/bgp_flowspec_util.c index 2dd35696d0..0115d7af1e 100644 --- a/bgpd/bgp_flowspec_util.c +++ b/bgpd/bgp_flowspec_util.c @@ -118,6 +118,16 @@ bool bgp_flowspec_contains_prefix(const struct prefix *pfs, &compare.u.prefix6.s6_addr)) return true; break; + case FLOWSPEC_FLOW_LABEL: + if (afi == AFI_IP) { + error = -1; + continue; + } + ret = bgp_flowspec_op_decode(BGP_FLOWSPEC_VALIDATE_ONLY, + nlri_content+offset, + len - offset, + NULL, &error); + break; case FLOWSPEC_IP_PROTOCOL: case FLOWSPEC_PORT: case FLOWSPEC_DEST_PORT: @@ -499,6 +509,20 @@ int bgp_flowspec_match_rules_fill(uint8_t *nlri_content, int len, } offset += ret; break; + case FLOWSPEC_FLOW_LABEL: + if (afi == AFI_IP) { + error = -1; + continue; + } + match_num = &(bpem->match_flowlabel_num); + mval = (struct bgp_pbr_match_val *) + &(bpem->flow_label); + offset += bgp_flowspec_call_non_opaque_decode( + nlri_content + offset, + len - offset, + mval, match_num, + &error); + break; case FLOWSPEC_IP_PROTOCOL: match_num = &(bpem->match_protocol_num); mval = (struct bgp_pbr_match_val *) @@ -621,7 +645,8 @@ int bgp_flowspec_match_rules_fill(uint8_t *nlri_content, int len, bpem->match_packet_length_num || bpem->match_icmp_code_num || bpem->match_icmp_type_num || bpem->match_port_num || bpem->match_src_port_num || bpem->match_dst_port_num || - bpem->match_protocol_num || bpem->match_bitmask) + bpem->match_protocol_num || bpem->match_bitmask || + bpem->match_flowlabel_num) bpem->type = BGP_PBR_IPSET; else if ((bpem->match_bitmask_iprule & PREFIX_SRC_PRESENT) || (bpem->match_bitmask_iprule & PREFIX_DST_PRESENT)) diff --git a/bgpd/bgp_flowspec_vty.c b/bgpd/bgp_flowspec_vty.c index d3e95b123f..4b1d26b2fe 100644 --- a/bgpd/bgp_flowspec_vty.c +++ b/bgpd/bgp_flowspec_vty.c @@ -50,6 +50,7 @@ static const struct message bgp_flowspec_display_large[] = { {FLOWSPEC_PKT_LEN, "Packet Length"}, {FLOWSPEC_DSCP, "DSCP field"}, {FLOWSPEC_FRAGMENT, "Packet Fragment"}, + {FLOWSPEC_FLOW_LABEL, "Packet Flow Label"}, {0} }; @@ -66,6 +67,7 @@ static const struct message bgp_flowspec_display_min[] = { {FLOWSPEC_PKT_LEN, "pktlen"}, {FLOWSPEC_DSCP, "dscp"}, {FLOWSPEC_FRAGMENT, "pktfrag"}, + {FLOWSPEC_FLOW_LABEL, "flwlbl"}, {0} }; @@ -147,6 +149,7 @@ void bgp_fs_nlri_get_string(unsigned char *nlri_content, size_t len, len_string -= len_written; ptr += len_written; break; + case FLOWSPEC_FLOW_LABEL: case FLOWSPEC_IP_PROTOCOL: case FLOWSPEC_PORT: case FLOWSPEC_DEST_PORT: diff --git a/bgpd/bgp_pbr.c b/bgpd/bgp_pbr.c index c20bade97f..715781143f 100644 --- a/bgpd/bgp_pbr.c +++ b/bgpd/bgp_pbr.c @@ -257,6 +257,7 @@ struct bgp_pbr_filter { struct bgp_pbr_range_port *dst_port; struct bgp_pbr_val_mask *tcp_flags; struct bgp_pbr_val_mask *dscp; + struct bgp_pbr_val_mask *flow_label; struct bgp_pbr_val_mask *pkt_len_val; struct bgp_pbr_val_mask *fragment; }; @@ -268,6 +269,7 @@ struct bgp_pbr_filter { struct bgp_pbr_or_filter { struct list *tcpflags; struct list *dscp; + struct list *flowlabel; struct list *pkt_len; struct list *fragment; struct list *icmp_type; @@ -294,6 +296,7 @@ static bool bgp_pbr_extract_enumerate_unary_opposite( TCP_HEADER_ALL_FLAGS & ~(value); } else if (type_entry == FLOWSPEC_DSCP || + type_entry == FLOWSPEC_FLOW_LABEL || type_entry == FLOWSPEC_PKT_LEN || type_entry == FLOWSPEC_FRAGMENT) { and_valmask->val = value; @@ -308,6 +311,7 @@ static bool bgp_pbr_extract_enumerate_unary_opposite( TCP_HEADER_ALL_FLAGS & ~(value); } else if (type_entry == FLOWSPEC_DSCP || + type_entry == FLOWSPEC_FLOW_LABEL || type_entry == FLOWSPEC_FRAGMENT || type_entry == FLOWSPEC_PKT_LEN) { and_valmask->val = value; @@ -322,7 +326,7 @@ static bool bgp_pbr_extract_enumerate_unary_opposite( /* TCP : FIN and SYN -> val = ALL; mask = 3 * TCP : not (FIN and SYN) -> val = ALL; mask = ALL & ~(FIN|RST) - * other variables type: dscp, pkt len, fragment + * other variables type: dscp, pkt len, fragment, flow label * - value is copied in bgp_pbr_val_mask->val value * - if negate form is identifierd, bgp_pbr_val_mask->mask set to 1 */ @@ -377,6 +381,7 @@ static bool bgp_pbr_extract_enumerate_unary(struct bgp_pbr_match_val list[], and_valmask->mask |= TCP_HEADER_ALL_FLAGS & list[i].value; } else if (type_entry == FLOWSPEC_DSCP || + type_entry == FLOWSPEC_FLOW_LABEL || type_entry == FLOWSPEC_ICMP_TYPE || type_entry == FLOWSPEC_ICMP_CODE || type_entry == FLOWSPEC_FRAGMENT || @@ -601,6 +606,23 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api) return 0; } } + if (api->match_flowlabel_num) { + if (api->afi == AFI_IP) { + if (BGP_DEBUG(pbr, PBR)) + zlog_debug("BGP: match Flow Label operations:" + "Not for IPv4."); + return 0; + } + if (!bgp_pbr_extract_enumerate(api->flow_label, + api->match_flowlabel_num, + OPERATOR_UNARY_OR | OPERATOR_UNARY_AND, + NULL, FLOWSPEC_FLOW_LABEL)) { + if (BGP_DEBUG(pbr, PBR)) + zlog_debug("BGP: match FlowLabel operations:" + "too complex. ignoring."); + return 0; + } + } if (api->match_fragment_num) { char fail_str[64]; bool success; @@ -624,6 +646,13 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api) "Value not valid (%d) for this implementation", api->fragment[i].value); } + if (api->afi == AFI_IP6 && + api->fragment[i].value == 1) { + success = false; + snprintf(fail_str, sizeof(fail_str), + "IPv6 dont fragment match invalid (%d)", + api->fragment[i].value); + } } } else snprintf(fail_str, sizeof(fail_str), @@ -669,7 +698,7 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api) if (api->actions[i].action == ACTION_MARKING) { if (BGP_DEBUG(pbr, PBR)) { bgp_pbr_print_policy_route(api); - zlog_warn("PBR: iprule set DSCP %u not supported", + zlog_warn("PBR: iprule set DSCP/Flow Label %u not supported", api->actions[i].u.marking_dscp); } } @@ -1001,6 +1030,7 @@ uint32_t bgp_pbr_match_hash_key(const void *arg) key = jhash(&pbm->tcp_flags, 2, key); key = jhash(&pbm->tcp_mask_flags, 2, key); key = jhash(&pbm->dscp_value, 1, key); + key = jhash(&pbm->flow_label, 2, key); key = jhash(&pbm->fragment, 1, key); key = jhash(&pbm->protocol, 1, key); return jhash_1word(pbm->type, key); @@ -1040,6 +1070,9 @@ bool bgp_pbr_match_hash_equal(const void *arg1, const void *arg2) if (r1->dscp_value != r2->dscp_value) return false; + if (r1->flow_label != r2->flow_label) + return false; + if (r1->fragment != r2->fragment) return false; @@ -1406,6 +1439,15 @@ void bgp_pbr_print_policy_route(struct bgp_pbr_entry_main *api) ptr += delta; } + if (api->match_flowlabel_num) + INCREMENT_DISPLAY(ptr, nb_items, len); + for (i = 0; i < api->match_flowlabel_num; i++) { + delta = snprintf_bgp_pbr_match_val(ptr, len, &api->flow_label[i], + i > 0 ? NULL : "@flowlabel "); + len -= delta; + ptr += delta; + } + if (api->match_tcpflags_num) INCREMENT_DISPLAY(ptr, nb_items, len); for (i = 0; i < api->match_tcpflags_num; i++) { @@ -1499,7 +1541,7 @@ void bgp_pbr_print_policy_route(struct bgp_pbr_entry_main *api) } case ACTION_MARKING: INCREMENT_DISPLAY(ptr, nb_items, len); - delta = snprintf(ptr, len, "@set dscp %u", + delta = snprintf(ptr, len, "@set dscp/flowlabel %u", api->actions[i].u.marking_dscp); len -= delta; ptr += delta; @@ -1676,6 +1718,7 @@ static int bgp_pbr_get_remaining_entry(struct hash_bucket *bucket, void *arg) bpm_temp->pkt_len_min != bpm->pkt_len_min || bpm_temp->pkt_len_max != bpm->pkt_len_max || bpm_temp->dscp_value != bpm->dscp_value || + bpm_temp->flow_label != bpm->flow_label || bpm_temp->fragment != bpm->fragment) return HASHWALK_CONTINUE; @@ -1799,6 +1842,14 @@ static void bgp_pbr_policyroute_remove_from_zebra_unit( temp.flags |= MATCH_DSCP_SET; temp.dscp_value = bpf->dscp->val; } + if (bpf->flow_label) { + if (bpf->flow_label->mask) + temp.flags |= MATCH_FLOW_LABEL_INVERSE_SET; + else + temp.flags |= MATCH_FLOW_LABEL_SET; + temp.flow_label = bpf->flow_label->val; + } + if (bpf->fragment) { if (bpf->fragment->mask) temp.flags |= MATCH_FRAGMENT_INVERSE_SET; @@ -1845,6 +1896,8 @@ static uint8_t bgp_pbr_next_type_entry(uint8_t type_entry) if (type_entry == FLOWSPEC_TCP_FLAGS) return FLOWSPEC_DSCP; if (type_entry == FLOWSPEC_DSCP) + return FLOWSPEC_FLOW_LABEL; + if (type_entry == FLOWSPEC_FLOW_LABEL) return FLOWSPEC_PKT_LEN; if (type_entry == FLOWSPEC_PKT_LEN) return FLOWSPEC_FRAGMENT; @@ -1938,6 +1991,9 @@ static void bgp_pbr_policyroute_remove_from_zebra_recursive( } else if (type_entry == FLOWSPEC_DSCP && bpof->dscp) { orig_list = bpof->dscp; target_val = &bpf->dscp; + } else if (type_entry == FLOWSPEC_FLOW_LABEL && bpof->flowlabel) { + orig_list = bpof->flowlabel; + target_val = &bpf->flow_label; } else if (type_entry == FLOWSPEC_PKT_LEN && bpof->pkt_len) { orig_list = bpof->pkt_len; target_val = &bpf->pkt_len_val; @@ -1975,6 +2031,9 @@ static void bgp_pbr_policyroute_remove_from_zebra( else if (bpof->dscp) bgp_pbr_policyroute_remove_from_zebra_recursive( bgp, path, bpf, bpof, FLOWSPEC_DSCP); + else if (bpof->flowlabel) + bgp_pbr_policyroute_remove_from_zebra_recursive( + bgp, path, bpf, bpof, FLOWSPEC_FLOW_LABEL); else if (bpof->pkt_len) bgp_pbr_policyroute_remove_from_zebra_recursive( bgp, path, bpf, bpof, FLOWSPEC_PKT_LEN); @@ -1991,6 +2050,8 @@ static void bgp_pbr_policyroute_remove_from_zebra( list_delete_all_node(bpof->tcpflags); if (bpof->dscp) list_delete_all_node(bpof->dscp); + if (bpof->flowlabel) + list_delete_all_node(bpof->flowlabel); if (bpof->pkt_len) list_delete_all_node(bpof->pkt_len); if (bpof->fragment) @@ -2082,6 +2143,15 @@ static void bgp_pbr_dump_entry(struct bgp_pbr_filter *bpf, bool add) ? "!" : "", bpf->dscp->val); } + if (bpf->flow_label) { + snprintf(buffer + remaining_len, + sizeof(buffer) + - remaining_len, + "%s flow_label %d", + bpf->flow_label->mask + ? "!" : "", + bpf->flow_label->val); + } zlog_debug("BGP: %s FS PBR from %s to %s, %s %s", add ? "adding" : "removing", bpf->src == NULL ? "" : @@ -2265,6 +2335,13 @@ static void bgp_pbr_policyroute_add_to_zebra_unit(struct bgp *bgp, temp.flags |= MATCH_DSCP_SET; temp.dscp_value = bpf->dscp->val; } + if (bpf->flow_label) { + if (bpf->flow_label->mask) + temp.flags |= MATCH_FLOW_LABEL_INVERSE_SET; + else + temp.flags |= MATCH_FLOW_LABEL_SET; + temp.flow_label = bpf->flow_label->val; + } if (bpf->fragment) { if (bpf->fragment->mask) temp.flags |= MATCH_FRAGMENT_INVERSE_SET; @@ -2683,7 +2760,7 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, struct bgp_path_info *path, case ACTION_MARKING: if (BGP_DEBUG(pbr, PBR)) { bgp_pbr_print_policy_route(api); - zlog_warn("PBR: Set DSCP %u Ignored", + zlog_warn("PBR: Set DSCP/FlowLabel %u Ignored", api->actions[i].u.marking_dscp); } break; diff --git a/bgpd/bgp_pbr.h b/bgpd/bgp_pbr.h index ff771f88d3..403300dc03 100644 --- a/bgpd/bgp_pbr.h +++ b/bgpd/bgp_pbr.h @@ -115,6 +115,7 @@ struct bgp_pbr_entry_main { uint8_t match_dscp_num; uint8_t match_tcpflags_num; uint8_t match_fragment_num; + uint8_t match_flowlabel_num; struct prefix src_prefix; struct prefix dst_prefix; @@ -132,6 +133,7 @@ struct bgp_pbr_entry_main { struct bgp_pbr_match_val icmp_code[BGP_PBR_MATCH_VAL_MAX]; struct bgp_pbr_match_val packet_length[BGP_PBR_MATCH_VAL_MAX]; struct bgp_pbr_match_val dscp[BGP_PBR_MATCH_VAL_MAX]; + struct bgp_pbr_match_val flow_label[BGP_PBR_MATCH_VAL_MAX]; struct bgp_pbr_match_val tcpflags[BGP_PBR_MATCH_VAL_MAX]; struct bgp_pbr_match_val fragment[BGP_PBR_MATCH_VAL_MAX]; @@ -190,6 +192,7 @@ struct bgp_pbr_match { uint8_t dscp_value; uint8_t fragment; uint8_t protocol; + uint16_t flow_label; vrf_id_t vrf_id; diff --git a/lib/pbr.h b/lib/pbr.h index fd183d7115..53a63122cc 100644 --- a/lib/pbr.h +++ b/lib/pbr.h @@ -129,6 +129,8 @@ struct pbr_rule { #define MATCH_FRAGMENT_INVERSE_SET (1 << 9) #define MATCH_ICMP_SET (1 << 10) #define MATCH_PROTOCOL_SET (1 << 11) +#define MATCH_FLOW_LABEL_SET (1 << 12) +#define MATCH_FLOW_LABEL_INVERSE_SET (1 << 13) extern int zapi_pbr_rule_encode(uint8_t cmd, struct stream *s, struct pbr_rule *zrule); From f01e580fc022be15b1f70a3407c898d92b0509ee Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Wed, 16 Oct 2019 10:05:36 +0200 Subject: [PATCH 05/19] bgpd: support for redirect ipv6 simpson method this commit supports [0] where ipv6 address is encoded in nexthop attribute of nlri, and not in bgp redirect ip extended community. the community contains only duplicate information or not. Adding to this, because an action or a rule needs to apply to either ipv4 or ipv6 flow, modify some internal structures so as to be aware of which flow needs to be filtered. This work is needed when an ipv6 flowspec rule without ip addresses is mentioned, we need to know which afi is served. Also, this work will be useful when doing redirect VRF. [0] draft-simpson-idr-flowspec-redirect-02.txt Signed-off-by: Philippe Guibert --- bgpd/bgp_ecommunity.c | 6 ++- bgpd/bgp_ecommunity.h | 3 +- bgpd/bgp_flowspec_vty.c | 26 ++++++++++-- bgpd/bgp_pbr.c | 94 ++++++++++++++++++++++++++++++++--------- bgpd/bgp_pbr.h | 1 + bgpd/bgp_zebra.c | 37 ++++++++++++---- 6 files changed, 132 insertions(+), 35 deletions(-) diff --git a/bgpd/bgp_ecommunity.c b/bgpd/bgp_ecommunity.c index f2aac3646c..c7fabb396a 100644 --- a/bgpd/bgp_ecommunity.c +++ b/bgpd/bgp_ecommunity.c @@ -1052,7 +1052,8 @@ bool ecommunity_del_val(struct ecommunity *ecom, struct ecommunity_val *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) { api->action = ACTION_TRAFFICRATE; @@ -1076,7 +1077,8 @@ int ecommunity_fill_pbr_action(struct ecommunity_val *ecom_eval, } else if (ecom_eval->val[1] == ECOMMUNITY_REDIRECT_VRF) { /* must use external function */ return 0; - } else if (ecom_eval->val[1] == ECOMMUNITY_REDIRECT_IP_NH) { + } else if (ecom_eval->val[1] == ECOMMUNITY_REDIRECT_IP_NH && + afi == AFI_IP) { /* see draft-ietf-idr-flowspec-redirect-ip-02 * Q1: how come a ext. community can host ipv6 address * Q2 : from cisco documentation: diff --git a/bgpd/bgp_ecommunity.h b/bgpd/bgp_ecommunity.h index 812bcc46e7..8ae3b44bab 100644 --- a/bgpd/bgp_ecommunity.h +++ b/bgpd/bgp_ecommunity.h @@ -222,7 +222,8 @@ extern bool ecommunity_del_val(struct ecommunity *ecom, struct ecommunity_val *eval); struct bgp_pbr_entry_action; extern int ecommunity_fill_pbr_action(struct ecommunity_val *ecom_eval, - struct bgp_pbr_entry_action *api); + struct bgp_pbr_entry_action *api, + afi_t afi); extern void bgp_compute_aggregate_ecommunity( struct bgp_aggregate *aggregate, diff --git a/bgpd/bgp_flowspec_vty.c b/bgpd/bgp_flowspec_vty.c index 4b1d26b2fe..3e3366bf8b 100644 --- a/bgpd/bgp_flowspec_vty.c +++ b/bgpd/bgp_flowspec_vty.c @@ -325,10 +325,28 @@ void route_vty_out_flowspec(struct vty *vty, const struct prefix *p, json_object_array_add(json_paths, json_ecom_path); } - if (attr->nexthop.s_addr != 0 && - display == NLRI_STRING_FORMAT_LARGE) - vty_out(vty, "\tNLRI NH %-16s\n", - inet_ntoa(attr->nexthop)); + if (display == NLRI_STRING_FORMAT_LARGE) { + char local_buff[INET6_ADDRSTRLEN]; + + local_buff[0] = '\0'; + if (p->u.prefix_flowspec.family == AF_INET && + attr->nexthop.s_addr != 0) + inet_ntop(AF_INET, + &attr->nexthop.s_addr, + local_buff, + INET6_ADDRSTRLEN); + else if (p->u.prefix_flowspec.family == AF_INET6 && + attr->mp_nexthop_len != 0 && + attr->mp_nexthop_len != BGP_ATTR_NHLEN_IPV4 && + attr->mp_nexthop_len != BGP_ATTR_NHLEN_VPNV4) + inet_ntop(AF_INET6, + &attr->mp_nexthop_global, + local_buff, + INET6_ADDRSTRLEN); + if (local_buff[0] != '\0') + vty_out(vty, "\tNLRI NH %s\n", + local_buff); + } XFREE(MTYPE_ECOMMUNITY_STR, s); } peer_uptime(path->uptime, timebuf, BGP_UPTIME_LEN, 0, NULL); diff --git a/bgpd/bgp_pbr.c b/bgpd/bgp_pbr.c index 715781143f..9446575611 100644 --- a/bgpd/bgp_pbr.c +++ b/bgpd/bgp_pbr.c @@ -248,6 +248,7 @@ struct bgp_pbr_val_mask { struct bgp_pbr_filter { uint8_t type; vrf_id_t vrf_id; + uint8_t family; struct prefix *src; struct prefix *dst; uint8_t bitmask_iprule; @@ -793,9 +794,10 @@ int bgp_pbr_build_and_validate_entry(const struct prefix *p, * do not overwrite * draft-ietf-idr-flowspec-redirect */ - if (api_action_redirect_ip) { - if (api_action_redirect_ip->u.zr - .redirect_ip_v4.s_addr + if (api_action_redirect_ip && + p->u.prefix_flowspec.family == AF_INET) { + if (api_action_redirect_ip->u + .zr.redirect_ip_v4.s_addr != INADDR_ANY) continue; if (path->attr->nexthop.s_addr @@ -807,13 +809,42 @@ int bgp_pbr_build_and_validate_entry(const struct prefix *p, api_action_redirect_ip->u.zr.duplicate = ecom_eval->val[7]; continue; - } else { + } else if (api_action_redirect_ip && + p->u.prefix_flowspec.family == AF_INET6) { + if (memcmp(&api_action_redirect_ip->u + .zr.redirect_ip_v6, + &in6addr_any, + sizeof(struct in6_addr))) + continue; + if (path->attr->mp_nexthop_len == 0 || + path->attr->mp_nexthop_len == + BGP_ATTR_NHLEN_IPV4 || + path->attr->mp_nexthop_len == + BGP_ATTR_NHLEN_VPNV4) + continue; + memcpy(&api_action_redirect_ip->u + .zr.redirect_ip_v6, + &path->attr->mp_nexthop_global, + sizeof(struct in6_addr)); + api_action_redirect_ip->u.zr.duplicate + = ecom_eval->val[7]; + continue; + } else if (p->u.prefix_flowspec.family == AF_INET) { api_action->action = ACTION_REDIRECT_IP; api_action->u.zr.redirect_ip_v4.s_addr = path->attr->nexthop.s_addr; api_action->u.zr.duplicate = ecom_eval->val[7]; api_action_redirect_ip = api_action; + } else if (p->u.prefix_flowspec.family == AF_INET6) { + api_action->action = ACTION_REDIRECT_IP; + memcpy(&api_action->u + .zr.redirect_ip_v6, + &path->attr->mp_nexthop_global, + sizeof(struct in6_addr)); + api_action->u.zr.duplicate + = ecom_eval->val[7]; + api_action_redirect_ip = api_action; } } else if ((ecom_eval->val[0] == (char)ECOMMUNITY_ENCODE_IP) && @@ -845,7 +876,8 @@ int bgp_pbr_build_and_validate_entry(const struct prefix *p, (char)ECOMMUNITY_ENCODE_TRANS_EXP) continue; ret = ecommunity_fill_pbr_action(ecom_eval, - api_action); + api_action, + afi); if (ret != 0) continue; if ((api_action->action == ACTION_TRAFFICRATE) && @@ -1181,6 +1213,7 @@ uint32_t bgp_pbr_action_hash_key(const void *arg) pbra = arg; key = jhash_1word(pbra->table_id, 0x4312abde); key = jhash_1word(pbra->fwmark, key); + key = jhash_1word(pbra->afi, key); return key; } @@ -1198,6 +1231,9 @@ bool bgp_pbr_action_hash_equal(const void *arg1, const void *arg2) if (r1->vrf_id != r2->vrf_id) return false; + if (r1->afi != r2->afi) + return false; + if (memcmp(&r1->nh, &r2->nh, sizeof(struct nexthop))) return false; @@ -1515,18 +1551,25 @@ void bgp_pbr_print_policy_route(struct bgp_pbr_entry_main *api) ptr += delta; } break; - case ACTION_REDIRECT_IP: - char local_buff[INET_ADDRSTRLEN]; + case ACTION_REDIRECT_IP: { + char local_buff[INET6_ADDRSTRLEN]; + void *ptr_ip; INCREMENT_DISPLAY(ptr, nb_items, len); - if (inet_ntop(AF_INET, - &api->actions[i].u.zr.redirect_ip_v4, - local_buff, INET_ADDRSTRLEN) != NULL) + if (api->afi == AF_INET) + ptr_ip = &api->actions[i].u.zr.redirect_ip_v4; + else + ptr_ip = &api->actions[i].u.zr.redirect_ip_v6; + if (inet_ntop(afi2family(api->afi), + ptr_ip, local_buff, + INET6_ADDRSTRLEN) != NULL) { delta = snprintf(ptr, len, "@redirect ip nh %s", local_buff); len -= delta; ptr += delta; + } break; + } case ACTION_REDIRECT: { struct vrf *vrf; @@ -1639,7 +1682,7 @@ static void bgp_pbr_flush_entry(struct bgp *bgp, struct bgp_pbr_action *bpa, if (bpa->installed && bpa->table_id != 0) { bgp_send_pbr_rule_action(bpa, NULL, false); bgp_zebra_announce_default(bpa->bgp, &(bpa->nh), - AFI_IP, + bpa->afi, bpa->table_id, false); bpa->installed = false; @@ -1794,12 +1837,12 @@ static void bgp_pbr_policyroute_remove_from_zebra_unit( temp.flags |= MATCH_IP_SRC_SET; prefix_copy(&temp2.src, bpf->src); } else - temp2.src.family = AF_INET; + temp2.src.family = bpf->family; if (bpf->dst) { temp.flags |= MATCH_IP_DST_SET; prefix_copy(&temp2.dst, bpf->dst); } else - temp2.dst.family = AF_INET; + temp2.dst.family = bpf->family; if (src_port && (src_port->min_port || bpf->protocol == IPPROTO_ICMP)) { if (bpf->protocol == IPPROTO_ICMP) temp.flags |= MATCH_ICMP_SET; @@ -2200,6 +2243,7 @@ static void bgp_pbr_policyroute_add_to_zebra_unit(struct bgp *bgp, if (nh) memcpy(&temp3.nh, nh, sizeof(struct nexthop)); temp3.vrf_id = bpf->vrf_id; + temp3.afi = family2afi(bpf->family); bpa = hash_get(bgp->pbr_action_hash, &temp3, bgp_pbr_action_alloc_intern); @@ -2258,7 +2302,8 @@ static void bgp_pbr_policyroute_add_to_zebra_unit(struct bgp *bgp, if (!bpa->installed && !bpa->install_in_progress) { bgp_send_pbr_rule_action(bpa, NULL, true); bgp_zebra_announce_default(bgp, nh, - AFI_IP, bpa->table_id, true); + bpa->afi, + bpa->table_id, true); } /* ip rule add */ if (bpr && !bpr->installed) @@ -2377,11 +2422,11 @@ static void bgp_pbr_policyroute_add_to_zebra_unit(struct bgp *bgp, if (bpf->src) prefix_copy(&temp2.src, bpf->src); else - temp2.src.family = AF_INET; + temp2.src.family = bpf->family; if (bpf->dst) prefix_copy(&temp2.dst, bpf->dst); else - temp2.dst.family = AF_INET; + temp2.dst.family = bpf->family; temp2.src_port_min = src_port ? src_port->min_port : 0; temp2.dst_port_min = dst_port ? dst_port->min_port : 0; temp2.src_port_max = src_port ? src_port->max_port : 0; @@ -2428,7 +2473,7 @@ static void bgp_pbr_policyroute_add_to_zebra_unit(struct bgp *bgp, if (!bpa->installed && !bpa->install_in_progress) { bgp_send_pbr_rule_action(bpa, NULL, true); bgp_zebra_announce_default(bgp, nh, - AFI_IP, bpa->table_id, true); + bpa->afi, bpa->table_id, true); } /* ipset create */ @@ -2690,6 +2735,7 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, struct bgp_path_info *path, bpf.protocol = proto; bpf.src_port = srcp; bpf.dst_port = dstp; + bpf.family = afi2family(api->afi); if (!add) { bgp_pbr_policyroute_remove_from_zebra(bgp, path, &bpf, &bpof); return; @@ -2739,10 +2785,18 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, struct bgp_path_info *path, */ break; case ACTION_REDIRECT_IP: - nh.type = NEXTHOP_TYPE_IPV4; - nh.gate.ipv4.s_addr = - api->actions[i].u.zr.redirect_ip_v4.s_addr; nh.vrf_id = api->vrf_id; + if (api->afi == AFI_IP) { + nh.type = NEXTHOP_TYPE_IPV4; + nh.gate.ipv4.s_addr = + api->actions[i].u.zr. + redirect_ip_v4.s_addr; + } else { + nh.type = NEXTHOP_TYPE_IPV6; + memcpy(&nh.gate.ipv6, + &api->actions[i].u.zr.redirect_ip_v6, + sizeof(struct in6_addr)); + } bgp_pbr_policyroute_add_to_zebra(bgp, path, &bpf, &bpof, &nh, &rate); /* XXX combination with REDIRECT_VRF diff --git a/bgpd/bgp_pbr.h b/bgpd/bgp_pbr.h index 403300dc03..ff333d2f10 100644 --- a/bgpd/bgp_pbr.h +++ b/bgpd/bgp_pbr.h @@ -260,6 +260,7 @@ struct bgp_pbr_action { bool install_in_progress; uint32_t refcnt; struct bgp *bgp; + afi_t afi; }; extern struct bgp_pbr_rule *bgp_pbr_rule_lookup(vrf_id_t vrf_id, diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index 87936f1dd6..bf32d493bc 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -2356,7 +2356,10 @@ static void bgp_encode_pbr_rule_action(struct stream *s, struct bgp_pbr_rule *pbr) { struct prefix pfx; + uint8_t fam = AF_INET; + if (pbra && pbra->nh.type == NEXTHOP_TYPE_IPV6) + fam = AF_INET6; stream_putl(s, 0); /* seqno unused */ if (pbr) stream_putl(s, pbr->priority); @@ -2376,7 +2379,7 @@ static void bgp_encode_pbr_rule_action(struct stream *s, memcpy(&pfx, &(pbr->src), sizeof(struct prefix)); else { memset(&pfx, 0, sizeof(pfx)); - pfx.family = AF_INET; + pfx.family = fam; } stream_putc(s, pfx.family); stream_putc(s, pfx.prefixlen); @@ -2388,7 +2391,7 @@ static void bgp_encode_pbr_rule_action(struct stream *s, memcpy(&pfx, &(pbr->dst), sizeof(struct prefix)); else { memset(&pfx, 0, sizeof(pfx)); - pfx.family = AF_INET; + pfx.family = fam; } stream_putc(s, pfx.family); stream_putc(s, pfx.prefixlen); @@ -3063,14 +3066,14 @@ void bgp_zebra_announce_default(struct bgp *bgp, struct nexthop *nh, struct zapi_route api; struct prefix p; - if (!nh || nh->type != NEXTHOP_TYPE_IPV4 + if (!nh || (nh->type != NEXTHOP_TYPE_IPV4 + && nh->type != NEXTHOP_TYPE_IPV6) || nh->vrf_id == VRF_UNKNOWN) return; memset(&p, 0, sizeof(struct prefix)); - /* default route */ - if (afi != AFI_IP) + if (afi != AFI_IP && afi != AFI_IP6) return; - p.family = AF_INET; + p.family = afi2family(afi); memset(&api, 0, sizeof(api)); api.vrf_id = bgp->vrf_id; api.type = ZEBRA_ROUTE_BGP; @@ -3086,7 +3089,7 @@ void bgp_zebra_announce_default(struct bgp *bgp, struct nexthop *nh, SET_FLAG(api.message, ZAPI_MESSAGE_DISTANCE); /* redirect IP */ - if (nh->gate.ipv4.s_addr != INADDR_ANY) { + if (afi == AFI_IP && nh->gate.ipv4.s_addr != INADDR_ANY) { char buff[PREFIX_STRLEN]; api_nh->vrf_id = nh->vrf_id; @@ -3095,7 +3098,25 @@ void bgp_zebra_announce_default(struct bgp *bgp, struct nexthop *nh, inet_ntop(AF_INET, &(nh->gate.ipv4), buff, INET_ADDRSTRLEN); if (BGP_DEBUG(zebra, ZEBRA)) - zlog_info("BGP: %s default route to %s table %d (redirect IP)", + zlog_debug("BGP: %s default route to %s table %d (redirect IP)", + announce ? "adding" : "withdrawing", + buff, table_id); + zclient_route_send(announce ? ZEBRA_ROUTE_ADD + : ZEBRA_ROUTE_DELETE, + zclient, &api); + } else if (afi == AFI_IP6 && + memcmp(&nh->gate.ipv6, + &in6addr_any, sizeof(struct in6_addr))) { + char buff[PREFIX_STRLEN]; + + api_nh->vrf_id = nh->vrf_id; + memcpy(&api_nh->gate.ipv6, &nh->gate.ipv6, + sizeof(struct in6_addr)); + api_nh->type = NEXTHOP_TYPE_IPV6; + + inet_ntop(AF_INET6, &(nh->gate.ipv6), buff, INET_ADDRSTRLEN); + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("BGP: %s default route to %s table %d (redirect IP)", announce ? "adding" : "withdrawing", buff, table_id); zclient_route_send(announce ? ZEBRA_ROUTE_ADD From a60b7031f98bd95050905fc1cf1b488f285655ce Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Wed, 16 Oct 2019 11:07:41 +0200 Subject: [PATCH 06/19] bgp, zebra: add family attribute to ipset and iptable context in order to create appropriate policy route, family attribute is stored in ipset and iptable zapi contexts. This commit also adds the flow label attribute in iptables, for further usage. Signed-off-by: Philippe Guibert --- bgpd/bgp_pbr.c | 7 +++++++ bgpd/bgp_pbr.h | 1 + bgpd/bgp_zebra.c | 4 +++- zebra/zapi_msg.c | 3 +++ zebra/zebra_pbr.c | 34 +++++++++++++++++++++++++++------- zebra/zebra_pbr.h | 6 ++++++ 6 files changed, 47 insertions(+), 8 deletions(-) diff --git a/bgpd/bgp_pbr.c b/bgpd/bgp_pbr.c index 9446575611..69c19fa7d8 100644 --- a/bgpd/bgp_pbr.c +++ b/bgpd/bgp_pbr.c @@ -1057,6 +1057,7 @@ uint32_t bgp_pbr_match_hash_key(const void *arg) key = jhash_1word(pbm->vrf_id, 0x4312abde); key = jhash_1word(pbm->flags, key); + key = jhash_1word(pbm->family, key); key = jhash(&pbm->pkt_len_min, 2, key); key = jhash(&pbm->pkt_len_max, 2, key); key = jhash(&pbm->tcp_flags, 2, key); @@ -1078,6 +1079,9 @@ bool bgp_pbr_match_hash_equal(const void *arg1, const void *arg2) if (r1->vrf_id != r2->vrf_id) return false; + if (r1->family != r2->family) + return false; + if (r1->type != r2->type) return false; @@ -1762,6 +1766,7 @@ static int bgp_pbr_get_remaining_entry(struct hash_bucket *bucket, void *arg) bpm_temp->pkt_len_max != bpm->pkt_len_max || bpm_temp->dscp_value != bpm->dscp_value || bpm_temp->flow_label != bpm->flow_label || + bpm_temp->family != bpm->family || bpm_temp->fragment != bpm->fragment) return HASHWALK_CONTINUE; @@ -1833,6 +1838,7 @@ static void bgp_pbr_policyroute_remove_from_zebra_unit( return; } + temp.family = bpf->family; if (bpf->src) { temp.flags |= MATCH_IP_SRC_SET; prefix_copy(&temp2.src, bpf->src); @@ -2329,6 +2335,7 @@ static void bgp_pbr_policyroute_add_to_zebra_unit(struct bgp *bgp, /* then look for bpm */ memset(&temp, 0, sizeof(temp)); temp.vrf_id = bpf->vrf_id; + temp.family = bpf->family; if (bpf->src) temp.flags |= MATCH_IP_SRC_SET; if (bpf->dst) diff --git a/bgpd/bgp_pbr.h b/bgpd/bgp_pbr.h index ff333d2f10..9d6360094c 100644 --- a/bgpd/bgp_pbr.h +++ b/bgpd/bgp_pbr.h @@ -184,6 +184,7 @@ struct bgp_pbr_match { uint32_t type; uint32_t flags; + uint8_t family; uint16_t pkt_len_min; uint16_t pkt_len_max; diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index bf32d493bc..73ce990724 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -2415,7 +2415,7 @@ static void bgp_encode_pbr_ipset_match(struct stream *s, { stream_putl(s, pbim->unique); stream_putl(s, pbim->type); - + stream_putc(s, pbim->family); stream_put(s, pbim->ipset_name, ZEBRA_IPSET_NAME_SIZE); } @@ -2464,6 +2464,7 @@ static void bgp_encode_pbr_iptable_match(struct stream *s, stream_putl(s, bpa->fwmark); stream_put(s, pbm->ipset_name, ZEBRA_IPSET_NAME_SIZE); + stream_putc(s, pbm->family); stream_putw(s, pbm->pkt_len_min); stream_putw(s, pbm->pkt_len_max); stream_putw(s, pbm->tcp_flags); @@ -2471,6 +2472,7 @@ static void bgp_encode_pbr_iptable_match(struct stream *s, stream_putc(s, pbm->dscp_value); stream_putc(s, pbm->fragment); stream_putc(s, pbm->protocol); + stream_putw(s, pbm->flow_label); } /* BGP has established connection with Zebra. */ diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index 2bcb3502d5..5068765d2f 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -2841,6 +2841,7 @@ static inline void zread_ipset(ZAPI_HANDLER_ARGS) zpi.vrf_id = zvrf->vrf->vrf_id; STREAM_GETL(s, zpi.unique); STREAM_GETL(s, zpi.type); + STREAM_GETC(s, zpi.family); STREAM_GET(&zpi.ipset_name, s, ZEBRA_IPSET_NAME_SIZE); if (hdr->command == ZEBRA_IPSET_CREATE) @@ -2951,6 +2952,7 @@ static inline void zread_iptable(ZAPI_HANDLER_ARGS) STREAM_GETL(s, zpi->action); STREAM_GETL(s, zpi->fwmark); STREAM_GET(&zpi->ipset_name, s, ZEBRA_IPSET_NAME_SIZE); + STREAM_GETC(s, zpi->family); STREAM_GETW(s, zpi->pkt_len_min); STREAM_GETW(s, zpi->pkt_len_max); STREAM_GETW(s, zpi->tcp_flags); @@ -2958,6 +2960,7 @@ static inline void zread_iptable(ZAPI_HANDLER_ARGS) STREAM_GETC(s, zpi->dscp_value); STREAM_GETC(s, zpi->fragment); STREAM_GETC(s, zpi->protocol); + STREAM_GETW(s, zpi->flow_label); STREAM_GETL(s, zpi->nb_interface); zebra_pbr_iptable_update_interfacelist(s, zpi); diff --git a/zebra/zebra_pbr.c b/zebra/zebra_pbr.c index 1bc8d893bc..166689385c 100644 --- a/zebra/zebra_pbr.c +++ b/zebra/zebra_pbr.c @@ -251,6 +251,8 @@ uint32_t zebra_pbr_ipset_hash_key(const void *arg) uint32_t *pnt = (uint32_t *)&ipset->ipset_name; uint32_t key = jhash_1word(ipset->vrf_id, 0x63ab42de); + key = jhash_1word(ipset->family, key); + return jhash2(pnt, ZEBRA_IPSET_NAME_HASH_SIZE, key); } @@ -267,6 +269,8 @@ bool zebra_pbr_ipset_hash_equal(const void *arg1, const void *arg2) return false; if (r1->vrf_id != r2->vrf_id) return false; + if (r1->family != r2->family) + return false; if (strncmp(r1->ipset_name, r2->ipset_name, ZEBRA_IPSET_NAME_SIZE)) @@ -376,6 +380,8 @@ uint32_t zebra_pbr_iptable_hash_key(const void *arg) key = jhash2(pnt, ZEBRA_IPSET_NAME_HASH_SIZE, 0x63ab42de); key = jhash_1word(iptable->fwmark, key); + key = jhash_1word(iptable->family, key); + key = jhash_1word(iptable->flow_label, key); key = jhash_1word(iptable->pkt_len_min, key); key = jhash_1word(iptable->pkt_len_max, key); key = jhash_1word(iptable->tcp_flags, key); @@ -411,6 +417,10 @@ bool zebra_pbr_iptable_hash_equal(const void *arg1, const void *arg2) if (strncmp(r1->ipset_name, r2->ipset_name, ZEBRA_IPSET_NAME_SIZE)) return false; + if (r1->family != r2->family) + return false; + if (r1->flow_label != r2->flow_label) + return false; if (r1->pkt_len_min != r2->pkt_len_min) return false; if (r1->pkt_len_max != r2->pkt_len_max) @@ -876,7 +886,8 @@ static const char *zebra_pbr_prefix2str(union prefixconstptr pu, const struct prefix *p = pu.p; char buf[PREFIX2STR_BUFFER]; - if (p->family == AF_INET && p->prefixlen == IPV4_MAX_PREFIXLEN) { + if ((p->family == AF_INET && p->prefixlen == IPV4_MAX_PREFIXLEN) || + (p->family == AF_INET6 && p->prefixlen == IPV6_MAX_PREFIXLEN)) { snprintf(str, size, "%s", inet_ntop(p->family, &p->u.prefix, buf, PREFIX2STR_BUFFER)); return str; @@ -1012,8 +1023,9 @@ static int zebra_pbr_show_ipset_walkcb(struct hash_bucket *bucket, void *arg) struct vty *vty = uniqueipset->vty; struct zebra_ns *zns = uniqueipset->zns; - vty_out(vty, "IPset %s type %s\n", zpi->ipset_name, - zebra_pbr_ipset_type2str(zpi->type)); + vty_out(vty, "IPset %s type %s family %s\n", zpi->ipset_name, + zebra_pbr_ipset_type2str(zpi->type), + zpi->family == AF_INET ? "AF_INET" : "AF_INET6"); unique.vty = vty; unique.zpi = zpi; unique.zns = zns; @@ -1058,9 +1070,9 @@ void zebra_pbr_show_ipset_list(struct vty *vty, char *ipsetname) vty_out(vty, "No IPset %s found\n", ipsetname); return; } - vty_out(vty, "IPset %s type %s\n", ipsetname, - zebra_pbr_ipset_type2str(zpi->type)); - + vty_out(vty, "IPset %s type %s family %s\n", ipsetname, + zebra_pbr_ipset_type2str(zpi->type), + zpi->family == AF_INET ? "AF_INET" : "AF_INET6"); unique.vty = vty; unique.zpi = zpi; unique.zns = zns; @@ -1101,7 +1113,9 @@ static void zebra_pbr_show_iptable_unit(struct zebra_pbr_iptable *iptable, int ret; uint64_t pkts = 0, bytes = 0; - vty_out(vty, "IPtable %s action %s (%u)\n", iptable->ipset_name, + vty_out(vty, "IPtable %s family %s action %s (%u)\n", + iptable->ipset_name, + iptable->family == AF_INET ? "AF_INET" : "AF_INET6", iptable->action == ZEBRA_IPTABLES_DROP ? "drop" : "redirect", iptable->unique); if (iptable->type == IPSET_NET_PORT || @@ -1140,6 +1154,12 @@ static void zebra_pbr_show_iptable_unit(struct zebra_pbr_iptable *iptable, iptable->filter_bm & MATCH_DSCP_INVERSE_SET ? "not" : "", iptable->dscp_value); } + if (iptable->filter_bm & (MATCH_FLOW_LABEL_SET | + MATCH_FLOW_LABEL_INVERSE_SET)) { + vty_out(vty, "\t flowlabel %s %d\n", + iptable->filter_bm & MATCH_FLOW_LABEL_INVERSE_SET ? + "not" : "", iptable->flow_label); + } if (iptable->fragment) { char val_str[10]; diff --git a/zebra/zebra_pbr.h b/zebra/zebra_pbr.h index 888d2fcfa0..c84e603723 100644 --- a/zebra/zebra_pbr.h +++ b/zebra/zebra_pbr.h @@ -79,6 +79,9 @@ struct zebra_pbr_ipset { * but value is an enum ipset_type */ uint32_t type; + + uint8_t family; + char ipset_name[ZEBRA_IPSET_NAME_SIZE]; }; @@ -150,6 +153,9 @@ struct zebra_pbr_iptable { uint8_t protocol; uint32_t nb_interface; + uint16_t flow_label; + + uint8_t family; struct list *interface_name_list; From 9a659715dfcb6c0b1e3ef8004b6c9d14c55f2081 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Thu, 17 Oct 2019 16:08:16 +0200 Subject: [PATCH 07/19] bgpd: support for bgp ipv6 ext community, and flowspec redirect ipv6 rfc 5701 is supported. it is possible to configure in bgp vpn, a list of route target with ipv6 external communities to import. it is to be noted that this ipv6 external community has been developed only for matching a bgp flowspec update with same ipv6 ext commmunity. adding to this, draft-ietf-idr-flow-spec-v6-09 is implemented regarding the redirect ipv6 option. Practically, under bgp vpn, under ipv6 unicast, it is possible to configure : [no] rt6 redirect import : values. An incoming bgp update with fs ipv6 and that option matching a bgp vrf, will be imported in that bgp vrf. Signed-off-by: Philippe Guibert --- bgpd/bgp_attr.c | 54 +++++ bgpd/bgp_attr.h | 3 + bgpd/bgp_ecommunity.c | 482 +++++++++++++++++++++++++++++----------- bgpd/bgp_ecommunity.h | 30 +++ bgpd/bgp_flowspec_vty.c | 28 ++- bgpd/bgp_mplsvpn.c | 16 +- bgpd/bgp_pbr.c | 53 ++++- bgpd/bgp_vty.c | 41 +++- bgpd/bgpd.h | 1 + 9 files changed, 547 insertions(+), 161 deletions(-) diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index 2c7091f2e4..a77d9467cb 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -84,6 +84,7 @@ static const struct message attr_str[] = { #endif {BGP_ATTR_LARGE_COMMUNITIES, "LARGE_COMMUNITY"}, {BGP_ATTR_PREFIX_SID, "PREFIX_SID"}, + {BGP_ATTR_IPV6_EXT_COMMUNITIES, "IPV6_EXT_COMMUNITIES"}, {0}}; static const struct message attr_flag_str[] = { @@ -662,6 +663,8 @@ unsigned int attrhash_key_make(const void *p) MIX(lcommunity_hash_make(attr->lcommunity)); if (attr->ecommunity) MIX(ecommunity_hash_make(attr->ecommunity)); + if (attr->ipv6_ecommunity) + MIX(ecommunity_hash_make(attr->ipv6_ecommunity)); if (attr->cluster) MIX(cluster_hash_key_make(attr->cluster)); if (attr->transit) @@ -700,6 +703,7 @@ bool attrhash_cmp(const void *p1, const void *p2) && attr1->label_index == attr2->label_index && attr1->mp_nexthop_len == attr2->mp_nexthop_len && attr1->ecommunity == attr2->ecommunity + && attr1->ipv6_ecommunity == attr2->ipv6_ecommunity && attr1->lcommunity == attr2->lcommunity && attr1->cluster == attr2->cluster && attr1->transit == attr2->transit @@ -829,6 +833,15 @@ struct attr *bgp_attr_intern(struct attr *attr) else attr->ecommunity->refcnt++; } + + if (attr->ipv6_ecommunity) { + if (!attr->ipv6_ecommunity->refcnt) + attr->ipv6_ecommunity = + ecommunity_intern(attr->ipv6_ecommunity); + else + attr->ipv6_ecommunity->refcnt++; + } + if (attr->lcommunity) { if (!attr->lcommunity->refcnt) attr->lcommunity = lcommunity_intern(attr->lcommunity); @@ -1034,6 +1047,10 @@ void bgp_attr_unintern_sub(struct attr *attr) ecommunity_unintern(&attr->ecommunity); UNSET_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES)); + if (attr->ipv6_ecommunity) + ecommunity_unintern(&attr->ipv6_ecommunity); + UNSET_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_IPV6_EXT_COMMUNITIES)); + if (attr->lcommunity) lcommunity_unintern(&attr->lcommunity); UNSET_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_LARGE_COMMUNITIES)); @@ -1118,6 +1135,8 @@ void bgp_attr_flush(struct attr *attr) community_free(&attr->community); if (attr->ecommunity && !attr->ecommunity->refcnt) ecommunity_free(&attr->ecommunity); + if (attr->ipv6_ecommunity && !attr->ipv6_ecommunity->refcnt) + ecommunity_free(&attr->ipv6_ecommunity); if (attr->lcommunity && !attr->lcommunity->refcnt) lcommunity_free(&attr->lcommunity); if (attr->cluster && !attr->cluster->refcnt) { @@ -1201,6 +1220,7 @@ bgp_attr_malformed(struct bgp_attr_parser_args *args, uint8_t subcode, case BGP_ATTR_LOCAL_PREF: case BGP_ATTR_COMMUNITIES: case BGP_ATTR_EXT_COMMUNITIES: + case BGP_ATTR_IPV6_EXT_COMMUNITIES: case BGP_ATTR_LARGE_COMMUNITIES: case BGP_ATTR_ORIGINATOR_ID: case BGP_ATTR_CLUSTER_LIST: @@ -1286,6 +1306,8 @@ const uint8_t attr_flags_values[] = { [BGP_ATTR_LARGE_COMMUNITIES] = BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS, [BGP_ATTR_PREFIX_SID] = BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS, + [BGP_ATTR_IPV6_EXT_COMMUNITIES] = + BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS, }; static const size_t attr_flags_values_max = array_size(attr_flags_values) - 1; @@ -2264,6 +2286,35 @@ bgp_attr_ext_communities(struct bgp_attr_parser_args *args) return BGP_ATTR_PARSE_PROCEED; } +/* IPv6 Extended Community attribute. */ +static bgp_attr_parse_ret_t +bgp_attr_ipv6_ext_communities(struct bgp_attr_parser_args *args) +{ + struct peer *const peer = args->peer; + struct attr *const attr = args->attr; + const bgp_size_t length = args->length; + + if (length == 0) { + attr->ipv6_ecommunity = NULL; + /* Empty extcomm doesn't seem to be invalid per se */ + return BGP_ATTR_PARSE_PROCEED; + } + + attr->ipv6_ecommunity = + ecommunity_parse_ipv6((uint8_t *)stream_pnt(peer->curr), + length); + /* XXX: fix ecommunity_parse to use stream API */ + stream_forward_getp(peer->curr, length); + + if (!attr->ipv6_ecommunity) + return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_OPT_ATTR_ERR, + args->total); + + attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_IPV6_EXT_COMMUNITIES); + + return BGP_ATTR_PARSE_PROCEED; +} + /* Parse Tunnel Encap attribute in an UPDATE */ static int bgp_attr_encap(uint8_t type, struct peer *peer, /* IN */ bgp_size_t length, /* IN: attr's length field */ @@ -3082,6 +3133,9 @@ bgp_attr_parse_ret_t bgp_attr_parse(struct peer *peer, struct attr *attr, case BGP_ATTR_PMSI_TUNNEL: ret = bgp_attr_pmsi_tunnel(&attr_args); break; + case BGP_ATTR_IPV6_EXT_COMMUNITIES: + ret = bgp_attr_ipv6_ext_communities(&attr_args); + break; default: ret = bgp_attr_unknown(&attr_args); break; diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h index 1b2c75fbef..c57cf81007 100644 --- a/bgpd/bgp_attr.h +++ b/bgpd/bgp_attr.h @@ -177,6 +177,9 @@ struct attr { /* Extended Communities attribute. */ struct ecommunity *ecommunity; + /* Extended Communities attribute. */ + struct ecommunity *ipv6_ecommunity; + /* Large Communities attribute. */ struct lcommunity *lcommunity; diff --git a/bgpd/bgp_ecommunity.c b/bgpd/bgp_ecommunity.c index c7fabb396a..9a987ad1e7 100644 --- a/bgpd/bgp_ecommunity.c +++ b/bgpd/bgp_ecommunity.c @@ -48,7 +48,12 @@ static struct hash *ecomhash; /* Allocate a new ecommunities. */ struct ecommunity *ecommunity_new(void) { - return XCALLOC(MTYPE_ECOMMUNITY, sizeof(struct ecommunity)); + struct ecommunity *ecom; + + ecom = (struct ecommunity *)XCALLOC(MTYPE_ECOMMUNITY, + sizeof(struct ecommunity)); + ecom->unit_size = ECOMMUNITY_SIZE; + return ecom; } void ecommunity_strfree(char **s) @@ -83,36 +88,51 @@ static void ecommunity_hash_free(struct ecommunity *ecom) once and whether the new value should replace what is existing or not. */ -bool ecommunity_add_val(struct ecommunity *ecom, struct ecommunity_val *eval, - bool unique, bool overwrite) +static bool ecommunity_add_val_internal(struct ecommunity *ecom, const void *eval, + bool unique, bool overwrite, uint8_t ecom_size) { int c, ins_idx; + const struct ecommunity_val *eval4 = (struct ecommunity_val *)eval; + const struct ecommunity_val_ipv6 *eval6 = (struct ecommunity_val_ipv6 *)eval; /* When this is fist value, just add it. */ if (ecom->val == NULL) { ecom->size = 1; - ecom->val = XCALLOC(MTYPE_ECOMMUNITY_VAL, ECOMMUNITY_SIZE); - memcpy(ecom->val, eval->val, ECOMMUNITY_SIZE); + ecom->val = XMALLOC(MTYPE_ECOMMUNITY_VAL, + ecom_length_size(ecom, ecom_size)); + memcpy(ecom->val, eval, ecom_size); return true; } /* If the value already exists in the structure return 0. */ /* check also if the extended community itself exists. */ c = 0; + ins_idx = -1; for (uint8_t *p = ecom->val; c < ecom->size; - p += ECOMMUNITY_SIZE, c++) { + p += ecom_size, c++) { if (unique) { - if (p[0] == eval->val[0] && - p[1] == eval->val[1]) { - if (overwrite) { - memcpy(p, eval->val, ECOMMUNITY_SIZE); - return true; + if (ecom_size == ECOMMUNITY_SIZE) { + if (p[0] == eval4->val[0] && + p[1] == eval4->val[1]) { + if (overwrite) { + memcpy(p, eval4->val, ecom_size); + return true; + } + return false; + } + } else { + if (p[0] == eval6->val[0] && + p[1] == eval6->val[1]) { + if (overwrite) { + memcpy(p, eval6->val, ecom_size); + return true; + } + return false; } - return false; } } - int ret = memcmp(p, eval->val, ECOMMUNITY_SIZE); + int ret = memcmp(p, eval, ecom_size); if (ret == 0) return false; if (ret > 0) { @@ -129,61 +149,104 @@ bool ecommunity_add_val(struct ecommunity *ecom, struct ecommunity_val *eval, /* Add the value to the structure with numerical sorting. */ ecom->size++; ecom->val = XREALLOC(MTYPE_ECOMMUNITY_VAL, ecom->val, - ecom->size * ECOMMUNITY_SIZE); + ecom_length_size(ecom, ecom_size)); - memmove(ecom->val + ((ins_idx + 1) * ECOMMUNITY_SIZE), - ecom->val + (ins_idx * ECOMMUNITY_SIZE), - (ecom->size - 1 - ins_idx) * ECOMMUNITY_SIZE); - memcpy(ecom->val + (ins_idx * ECOMMUNITY_SIZE), - eval->val, ECOMMUNITY_SIZE); + + memmove(ecom->val + ((ins_idx + 1) * ecom_size), + ecom->val + (ins_idx * ecom_size), + (ecom->size - 1 - ins_idx) * ecom_size); + memcpy(ecom->val + (ins_idx * ecom_size), + eval, ecom_size); return true; } +/* Add a new Extended Communities value to Extended Communities + Attribute structure. When the value is already exists in the + structure, we don't add the value. Newly added value is sorted by + numerical order. When the value is added to the structure return 1 + else return 0. */ +bool ecommunity_add_val(struct ecommunity *ecom, struct ecommunity_val *eval, + bool unique, bool overwrite) +{ + return ecommunity_add_val_internal(ecom, (const void *)eval, unique, + overwrite, ECOMMUNITY_SIZE); +} + +bool ecommunity_add_val_ipv6(struct ecommunity *ecom, struct ecommunity_val_ipv6 *eval, + bool unique, bool overwrite) +{ + return ecommunity_add_val_internal(ecom, (const void *)eval, unique, + overwrite, IPV6_ECOMMUNITY_SIZE); +} + +static struct ecommunity * +ecommunity_uniq_sort_internal(struct ecommunity *ecom, + unsigned short ecom_size) +{ + int i; + struct ecommunity *new; + const void *eval; + + if (!ecom) + return NULL; + + new = ecommunity_new(); + new->unit_size = ecom_size; + + for (i = 0; i < ecom->size; i++) { + eval = (void *)(ecom->val + + (i * ecom_size)); + ecommunity_add_val_internal(new, eval, false, false, ecom_size); + } + return new; +} + /* This function takes pointer to Extended Communites strucutre then create a new Extended Communities structure by uniq and sort each Extended Communities value. */ struct ecommunity *ecommunity_uniq_sort(struct ecommunity *ecom) { - int i; - struct ecommunity *new; - struct ecommunity_val *eval; - - if (!ecom) - return NULL; - - new = ecommunity_new(); - - for (i = 0; i < ecom->size; i++) { - eval = (struct ecommunity_val *)(ecom->val - + (i * ECOMMUNITY_SIZE)); - ecommunity_add_val(new, eval, false, false); - } - return new; + return ecommunity_uniq_sort_internal(ecom, ECOMMUNITY_SIZE); } /* Parse Extended Communites Attribute in BGP packet. */ -struct ecommunity *ecommunity_parse(uint8_t *pnt, unsigned short length) +static struct ecommunity *ecommunity_parse_internal(uint8_t *pnt, + unsigned short length, + unsigned short size_ecom) { struct ecommunity tmp; struct ecommunity *new; /* Length check. */ - if (length % ECOMMUNITY_SIZE) + if (length % size_ecom) return NULL; /* Prepare tmporary structure for making a new Extended Communities Attribute. */ - tmp.size = length / ECOMMUNITY_SIZE; + tmp.size = length / size_ecom; tmp.val = pnt; /* Create a new Extended Communities Attribute by uniq and sort each Extended Communities value */ - new = ecommunity_uniq_sort(&tmp); + new = ecommunity_uniq_sort_internal(&tmp, size_ecom); return ecommunity_intern(new); } +struct ecommunity *ecommunity_parse(uint8_t *pnt, + unsigned short length) +{ + return ecommunity_parse_internal(pnt, length, ECOMMUNITY_SIZE); +} + +struct ecommunity *ecommunity_parse_ipv6(uint8_t *pnt, + unsigned short length) +{ + return ecommunity_parse_internal(pnt, length, + IPV6_ECOMMUNITY_SIZE); +} + /* Duplicate the Extended Communities Attribute structure. */ struct ecommunity *ecommunity_dup(struct ecommunity *ecom) { @@ -191,10 +254,11 @@ struct ecommunity *ecommunity_dup(struct ecommunity *ecom) new = XCALLOC(MTYPE_ECOMMUNITY, sizeof(struct ecommunity)); new->size = ecom->size; + new->unit_size = ecom->unit_size; if (new->size) { new->val = XMALLOC(MTYPE_ECOMMUNITY_VAL, - ecom->size * ECOMMUNITY_SIZE); - memcpy(new->val, ecom->val, ecom->size * ECOMMUNITY_SIZE); + ecom->size * ecom->unit_size); + memcpy(new->val, ecom->val, ecom->size * ecom->unit_size); } else new->val = NULL; return new; @@ -216,14 +280,16 @@ struct ecommunity *ecommunity_merge(struct ecommunity *ecom1, if (ecom1->val) ecom1->val = XREALLOC(MTYPE_ECOMMUNITY_VAL, ecom1->val, - (ecom1->size + ecom2->size) * ECOMMUNITY_SIZE); + (ecom1->size + ecom2->size) * + ecom1->unit_size); else ecom1->val = XMALLOC(MTYPE_ECOMMUNITY_VAL, - (ecom1->size + ecom2->size) * ECOMMUNITY_SIZE); + (ecom1->size + ecom2->size) * + ecom1->unit_size); - memcpy(ecom1->val + (ecom1->size * ECOMMUNITY_SIZE), ecom2->val, - ecom2->size * ECOMMUNITY_SIZE); + memcpy(ecom1->val + (ecom1->size * ecom1->unit_size), ecom2->val, + ecom2->size * ecom1->unit_size); ecom1->size += ecom2->size; return ecom1; @@ -235,9 +301,7 @@ struct ecommunity *ecommunity_intern(struct ecommunity *ecom) struct ecommunity *find; assert(ecom->refcnt == 0); - find = (struct ecommunity *)hash_get(ecomhash, ecom, hash_alloc_intern); - if (find != ecom) ecommunity_free(&ecom); @@ -272,7 +336,7 @@ void ecommunity_unintern(struct ecommunity **ecom) unsigned int ecommunity_hash_make(const void *arg) { const struct ecommunity *ecom = arg; - int size = ecom->size * ECOMMUNITY_SIZE; + int size = ecom->size * ecom->unit_size; return jhash(ecom->val, size, 0x564321ab); } @@ -289,9 +353,12 @@ bool ecommunity_cmp(const void *arg1, const void *arg2) if (ecom1 == NULL || ecom2 == NULL) return false; + if (ecom1->unit_size != ecom2->unit_size) + return false; + return (ecom1->size == ecom2->size - && memcmp(ecom1->val, ecom2->val, ecom1->size * ECOMMUNITY_SIZE) - == 0); + && memcmp(ecom1->val, ecom2->val, ecom1->size * + ecom1->unit_size) == 0); } /* Initialize Extended Comminities related hash. */ @@ -314,16 +381,21 @@ enum ecommunity_token { ecommunity_token_rt, ecommunity_token_soo, ecommunity_token_val, + ecommunity_token_rt6, + ecommunity_token_val6, }; -/* - * Encode BGP extended community from passed values. Supports types - * defined in RFC 4360 and well-known sub-types. - */ -static int ecommunity_encode(uint8_t type, uint8_t sub_type, int trans, as_t as, - struct in_addr ip, uint32_t val, - struct ecommunity_val *eval) +static int ecommunity_encode_internal(uint8_t type, uint8_t sub_type, + int trans, as_t as, + struct in_addr *ip, + struct in6_addr *ip6, + uint32_t val, + void *eval_ptr) { + struct ecommunity_val *eval = (struct ecommunity_val *)eval_ptr; + struct ecommunity_val_ipv6 *eval6 = + (struct ecommunity_val_ipv6 *)eval_ptr; + assert(eval); if (type == ECOMMUNITY_ENCODE_AS) { if (as > BGP_AS_MAX) @@ -332,6 +404,10 @@ static int ecommunity_encode(uint8_t type, uint8_t sub_type, int trans, as_t as, || type == ECOMMUNITY_ENCODE_AS4) { if (val > UINT16_MAX) return -1; + } else if (type == ECOMMUNITY_ENCODE_TRANS_EXP && + sub_type == ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6 && + (!ip6 || val > UINT16_MAX)) { + return -1; } /* Fill in the values. */ @@ -347,9 +423,14 @@ static int ecommunity_encode(uint8_t type, uint8_t sub_type, int trans, as_t as, eval->val[6] = (val >> 8) & 0xff; eval->val[7] = val & 0xff; } else if (type == ECOMMUNITY_ENCODE_IP) { - memcpy(&eval->val[2], &ip, sizeof(struct in_addr)); + memcpy(&eval->val[2], ip, sizeof(struct in_addr)); eval->val[6] = (val >> 8) & 0xff; eval->val[7] = val & 0xff; + } else if (type == ECOMMUNITY_ENCODE_TRANS_EXP && + sub_type == ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6) { + memcpy(&eval6->val[2], ip6, sizeof(struct in6_addr)); + eval6->val[18] = (val >> 8) & 0xff; + eval6->val[19] = val & 0xff; } else { eval->val[2] = (as >> 24) & 0xff; eval->val[3] = (as >> 16) & 0xff; @@ -362,9 +443,21 @@ static int ecommunity_encode(uint8_t type, uint8_t sub_type, int trans, as_t as, return 0; } +/* + * Encode BGP extended community from passed values. Supports types + * defined in RFC 4360 and well-known sub-types. + */ +static int ecommunity_encode(uint8_t type, uint8_t sub_type, int trans, as_t as, + struct in_addr ip, uint32_t val, + struct ecommunity_val *eval) +{ + return ecommunity_encode_internal(type, sub_type, trans, as, + &ip, NULL, val, (void *)eval); +} + /* Get next Extended Communities token from the string. */ static const char *ecommunity_gettoken(const char *str, - struct ecommunity_val *eval, + void *eval_ptr, enum ecommunity_token *token) { int ret; @@ -374,11 +467,12 @@ static const char *ecommunity_gettoken(const char *str, const char *p = str; char *endptr; struct in_addr ip; + struct in6_addr ip6; as_t as = 0; uint32_t val = 0; uint8_t ecomm_type; char buf[INET_ADDRSTRLEN + 1]; - + struct ecommunity_val *eval = (struct ecommunity_val *)eval_ptr; /* Skip white space. */ while (isspace((unsigned char)*p)) { p++; @@ -396,7 +490,10 @@ static const char *ecommunity_gettoken(const char *str, p++; if (tolower((unsigned char)*p) == 't') { p++; - *token = ecommunity_token_rt; + if (*p != '\0' && tolower((int)*p) == '6') + *token = ecommunity_token_rt6; + else + *token = ecommunity_token_rt; return p; } if (isspace((unsigned char)*p) || *p == '\0') { @@ -435,6 +532,7 @@ static const char *ecommunity_gettoken(const char *str, * a) A.B.C.D:MN * b) EF:OPQR * c) GHJK:MN + * d) :MN (only with rt6) * * A.B.C.D: Four Byte IP * EF: Two byte ASN @@ -443,6 +541,34 @@ static const char *ecommunity_gettoken(const char *str, * OPQR: Four byte value * */ + /* IPv6 case : look for last ':' */ + if (*token == ecommunity_token_rt6 || + *token == ecommunity_token_val6) { + char *limit; + + limit = endptr = strrchr(p, ':'); + if (!endptr) + goto error; + endptr++; + as = strtoul(endptr, &endptr, 10); + if (*endptr != '\0' || as == BGP_AS4_MAX) + goto error; + memcpy(buf, p, (limit - p)); + buf[limit - p] = '\0'; + ret = inet_pton(AF_INET6, buf, &ip6); + if (ret == 0) + goto error; + ecomm_type = ECOMMUNITY_ENCODE_TRANS_EXP; + if (ecommunity_encode_internal(ecomm_type, + ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6, + 1, 0, NULL, &ip6, as, eval_ptr)) + goto error; + *token = ecommunity_token_val6; + while (isdigit((int)*p) || *p == ':' || *p == '.') { + p++; + } + return p; + } while (isdigit((unsigned char)*p) || *p == ':' || *p == '.') { if (*p == ':') { if (separator) @@ -508,6 +634,77 @@ error: return p; } +static struct ecommunity *ecommunity_str2com_internal(const char *str, int type, + int keyword_included, + bool is_ipv6_extcomm) +{ + struct ecommunity *ecom = NULL; + enum ecommunity_token token = ecommunity_token_unknown; + struct ecommunity_val_ipv6 eval; + int keyword = 0; + + if (is_ipv6_extcomm) + token = ecommunity_token_rt6; + while ((str = ecommunity_gettoken(str, (void *)&eval, &token))) { + switch (token) { + case ecommunity_token_rt: + case ecommunity_token_soo: + if (!keyword_included || keyword) { + if (ecom) + ecommunity_free(&ecom); + return NULL; + } + keyword = 1; + + if (token == ecommunity_token_rt || + token == ecommunity_token_rt6) { + type = ECOMMUNITY_ROUTE_TARGET; + } + if (token == ecommunity_token_soo) { + type = ECOMMUNITY_SITE_ORIGIN; + } + break; + case ecommunity_token_val: + if (keyword_included) { + if (!keyword) { + if (ecom) + ecommunity_free(&ecom); + return NULL; + } + keyword = 0; + } + if (ecom == NULL) + ecom = ecommunity_new(); + eval.val[1] = type; + ecommunity_add_val_internal(ecom, (void *)&eval, false, false, + ecom->unit_size); + break; + case ecommunity_token_val6: + if (keyword_included) { + if (!keyword) { + if (ecom) + ecommunity_free(&ecom); + return NULL; + } + keyword = 0; + } + if (ecom == NULL) + ecom = ecommunity_new(); + ecom->unit_size = IPV6_ECOMMUNITY_SIZE; + eval.val[1] = type; + ecommunity_add_val_internal(ecom, (void *)&eval, false, false, + ecom->unit_size); + break; + case ecommunity_token_unknown: + default: + if (ecom) + ecommunity_free(&ecom); + return NULL; + } + } + return ecom; +} + /* Convert string to extended community attribute. When type is already known, please specify both str and type. str @@ -537,63 +734,30 @@ error: struct ecommunity *ecommunity_str2com(const char *str, int type, int keyword_included) { - struct ecommunity *ecom = NULL; - enum ecommunity_token token = ecommunity_token_unknown; - struct ecommunity_val eval; - int keyword = 0; - - while ((str = ecommunity_gettoken(str, &eval, &token))) { - switch (token) { - case ecommunity_token_rt: - case ecommunity_token_soo: - if (!keyword_included || keyword) { - if (ecom) - ecommunity_free(&ecom); - return NULL; - } - keyword = 1; - - if (token == ecommunity_token_rt) { - type = ECOMMUNITY_ROUTE_TARGET; - } - if (token == ecommunity_token_soo) { - type = ECOMMUNITY_SITE_ORIGIN; - } - break; - case ecommunity_token_val: - if (keyword_included) { - if (!keyword) { - if (ecom) - ecommunity_free(&ecom); - return NULL; - } - keyword = 0; - } - if (ecom == NULL) - ecom = ecommunity_new(); - eval.val[1] = type; - ecommunity_add_val(ecom, &eval, false, false); - break; - case ecommunity_token_unknown: - default: - if (ecom) - ecommunity_free(&ecom); - return NULL; - } - } - return ecom; + return ecommunity_str2com_internal(str, type, + keyword_included, false); } -static int ecommunity_rt_soo_str(char *buf, size_t bufsz, const uint8_t *pnt, - int type, int sub_type, int format) +struct ecommunity *ecommunity_str2com_ipv6(const char *str, int type, + int keyword_included) +{ + return ecommunity_str2com_internal(str, type, + keyword_included, true); +} + +static int ecommunity_rt_soo_str_internal(char *buf, size_t bufsz, + const uint8_t *pnt, int type, + int sub_type, int format, + unsigned short ecom_size) { int len = 0; const char *prefix; + char buf_local[INET6_ADDRSTRLEN]; /* For parse Extended Community attribute tupple. */ struct ecommunity_as eas; struct ecommunity_ip eip; - + struct ecommunity_ip6 eip6; /* Determine prefix for string, if any. */ switch (format) { @@ -619,11 +783,24 @@ static int ecommunity_rt_soo_str(char *buf, size_t bufsz, const uint8_t *pnt, len = snprintf(buf, bufsz, "%s%u:%u", prefix, eas.as, eas.val); } else if (type == ECOMMUNITY_ENCODE_AS) { - eas.as = (*pnt++ << 8); - eas.as |= (*pnt++); - pnt = ptr_get_be32(pnt, &eas.val); + if (ecom_size == ECOMMUNITY_SIZE) { + eas.as = (*pnt++ << 8); + eas.as |= (*pnt++); + pnt = ptr_get_be32(pnt, &eas.val); - len = snprintf(buf, bufsz, "%s%u:%u", prefix, eas.as, eas.val); + len = snprintf(buf, bufsz, "%s%u:%u", prefix, eas.as, eas.val); + } else { + /* this is an IPv6 ext community + */ + memcpy(&eip6.ip, pnt, 16); + pnt += 16; + eip6.val = (*pnt++ << 8); + eip6.val |= (*pnt++); + + inet_ntop(AF_INET6, &eip6.ip, buf_local, sizeof(buf_local)); + len = snprintf(buf, bufsz, "%s%s:%u", prefix, + buf_local, eip6.val); + } } else if (type == ECOMMUNITY_ENCODE_IP) { memcpy(&eip.ip, pnt, 4); pnt += 4; @@ -640,6 +817,14 @@ static int ecommunity_rt_soo_str(char *buf, size_t bufsz, const uint8_t *pnt, return len; } +static int ecommunity_rt_soo_str(char *buf, size_t bufsz, const uint8_t *pnt, + int type, int sub_type, int format) +{ + return ecommunity_rt_soo_str_internal(buf, bufsz, pnt, type, + sub_type, format, + ECOMMUNITY_SIZE); +} + static int ecommunity_lb_str(char *buf, size_t bufsz, const uint8_t *pnt) { int len = 0; @@ -852,15 +1037,43 @@ char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter) type == ECOMMUNITY_EXTENDED_COMMUNITY_PART_2 || type == ECOMMUNITY_EXTENDED_COMMUNITY_PART_3) { sub_type = *pnt++; - if (sub_type == ECOMMUNITY_REDIRECT_VRF) { - char buf[16] = {}; - ecommunity_rt_soo_str( - buf, sizeof(buf), pnt, - type & ~ECOMMUNITY_ENCODE_TRANS_EXP, - ECOMMUNITY_ROUTE_TARGET, - ECOMMUNITY_FORMAT_DISPLAY); + + if (sub_type == ECOMMUNITY_ROUTE_TARGET) { + char buf[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, + format, + ecom->unit_size); + snprintf(encbuf, sizeof(encbuf), "%s", buf); + } else if (sub_type == ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6) { + char buf[64]; + + memset(buf, 0, sizeof(buf)); + ecommunity_rt_soo_str_internal(buf, sizeof(buf), + (const uint8_t *)pnt, + type & + ~ECOMMUNITY_ENCODE_TRANS_EXP, + ECOMMUNITY_ROUTE_TARGET, + ECOMMUNITY_FORMAT_DISPLAY, + ecom->unit_size); + snprintf(encbuf, sizeof(encbuf), "FS:redirect VRF %s", buf); + } else if (sub_type == ECOMMUNITY_REDIRECT_VRF) { + char buf[16]; + + memset(buf, 0, sizeof(buf)); + ecommunity_rt_soo_str(buf, sizeof(buf), (const uint8_t *)pnt, + type & + ~ECOMMUNITY_ENCODE_TRANS_EXP, + ECOMMUNITY_ROUTE_TARGET, + ECOMMUNITY_FORMAT_DISPLAY); snprintf(encbuf, sizeof(encbuf), "FS:redirect VRF %s", buf); + snprintf(encbuf, sizeof(encbuf), "FS:redirect VRF %s", buf); } else if (type != ECOMMUNITY_ENCODE_TRANS_EXP) unk_ecom = 1; else if (sub_type == ECOMMUNITY_TRAFFIC_ACTION) { @@ -935,8 +1148,9 @@ bool ecommunity_match(const struct ecommunity *ecom1, /* Every community on com2 needs to be on com1 for this to match */ while (i < ecom1->size && j < ecom2->size) { - if (memcmp(ecom1->val + i * ECOMMUNITY_SIZE, - ecom2->val + j * ECOMMUNITY_SIZE, ECOMMUNITY_SIZE) + if (memcmp(ecom1->val + i * ecom1->unit_size, + ecom2->val + j * ecom2->unit_size, + ecom2->unit_size) == 0) j++; i++; @@ -957,7 +1171,7 @@ extern struct ecommunity_val *ecommunity_lookup(const struct ecommunity *ecom, /* If the value already exists in the structure return 0. */ c = 0; - for (p = ecom->val; c < ecom->size; p += ECOMMUNITY_SIZE, c++) { + for (p = ecom->val; c < ecom->size; p += ecom->unit_size, c++) { if (p == NULL) { continue; } @@ -984,7 +1198,7 @@ bool ecommunity_strip(struct ecommunity *ecom, uint8_t type, * multiple times, handle that. */ c = 0; - for (p = ecom->val; c < ecom->size; p += ECOMMUNITY_SIZE, c++) { + for (p = ecom->val; c < ecom->size; p += ecom->unit_size, c++) { if (p[0] == type && p[1] == subtype) found++; } @@ -1001,12 +1215,12 @@ bool ecommunity_strip(struct ecommunity *ecom, uint8_t type, /* Strip matching ext community(ies). */ new = XMALLOC(MTYPE_ECOMMUNITY_VAL, - (ecom->size - found) * ECOMMUNITY_SIZE); + (ecom->size - found) * ecom->unit_size); q = new; - for (c = 0, p = ecom->val; c < ecom->size; c++, p += ECOMMUNITY_SIZE) { + for (c = 0, p = ecom->val; c < ecom->size; c++, p += ecom->unit_size) { if (!(p[0] == type && p[1] == subtype)) { - memcpy(q, p, ECOMMUNITY_SIZE); - q += ECOMMUNITY_SIZE; + memcpy(q, p, ecom->unit_size); + q += ecom->unit_size; } } XFREE(MTYPE_ECOMMUNITY_VAL, ecom->val); @@ -1028,8 +1242,8 @@ bool ecommunity_del_val(struct ecommunity *ecom, struct ecommunity_val *eval) if (ecom == NULL || ecom->val == NULL) return false; c = 0; - for (p = ecom->val; c < ecom->size; p += ECOMMUNITY_SIZE, c++) { - if (!memcmp(p, eval->val, ECOMMUNITY_SIZE)) { + for (p = ecom->val; c < ecom->size; p += ecom->unit_size, c++) { + if (!memcmp(p, eval->val, ecom->unit_size)) { found = 1; break; } @@ -1039,13 +1253,13 @@ bool ecommunity_del_val(struct ecommunity *ecom, struct ecommunity_val *eval) /* Delete the selected value */ ecom->size--; - p = XMALLOC(MTYPE_ECOMMUNITY_VAL, ecom->size * ECOMMUNITY_SIZE); + p = XMALLOC(MTYPE_ECOMMUNITY_VAL, ecom->size * ecom->unit_size); if (c != 0) - memcpy(p, ecom->val, c * ECOMMUNITY_SIZE); + memcpy(p, ecom->val, c * ecom->unit_size); if ((ecom->size - c) != 0) - memcpy(p + (c)*ECOMMUNITY_SIZE, - ecom->val + (c + 1) * ECOMMUNITY_SIZE, - (ecom->size - c) * ECOMMUNITY_SIZE); + memcpy(p + (c)*ecom->unit_size, + ecom->val + (c + 1) * ecom->unit_size, + (ecom->size - c) * ecom->unit_size); XFREE(MTYPE_ECOMMUNITY_VAL, ecom->val); ecom->val = p; return true; diff --git a/bgpd/bgp_ecommunity.h b/bgpd/bgp_ecommunity.h index 8ae3b44bab..4a1af0d203 100644 --- a/bgpd/bgp_ecommunity.h +++ b/bgpd/bgp_ecommunity.h @@ -63,6 +63,10 @@ * 0x0c Flow-spec Redirect to IPv4 - draft-ietf-idr-flowspec-redirect */ #define ECOMMUNITY_FLOWSPEC_REDIRECT_IPV4 0x0c +/* from draft-ietf-idr-flow-spec-v6-09 + * 0x0b Flow-spec Redirect to IPv6 + */ +#define ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6 0x0b /* Low-order octet of the Extended Communities type field for EVPN types */ #define ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY 0x00 @@ -90,6 +94,7 @@ /* Extended Communities value is eight octet long. */ #define ECOMMUNITY_SIZE 8 +#define IPV6_ECOMMUNITY_SIZE 20 /* Extended Communities type flag. */ #define ECOMMUNITY_FLAG_NON_TRANSITIVE 0x40 @@ -99,6 +104,11 @@ struct ecommunity { /* Reference counter. */ unsigned long refcnt; + /* Size of Each Unit of Extended Communities attribute. + * to differentiate between IPv6 ext comm and ext comm + */ + int unit_size; + /* Size of Extended Communities attribute. */ int size; @@ -119,11 +129,23 @@ struct ecommunity_ip { uint16_t val; }; +struct ecommunity_ip6 { + struct in6_addr ip; + uint16_t val; +}; + /* Extended community value is eight octet. */ struct ecommunity_val { char val[ECOMMUNITY_SIZE]; }; +/* IPv6 Extended community value is eight octet. */ +struct ecommunity_val_ipv6 { + char val[IPV6_ECOMMUNITY_SIZE]; +}; + +#define ecom_length_size(X, Y) ((X)->size * (Y)) + /* * Encode BGP Route Target AS:nn. */ @@ -193,6 +215,8 @@ extern void ecommunity_init(void); extern void ecommunity_finish(void); extern void ecommunity_free(struct ecommunity **); extern struct ecommunity *ecommunity_parse(uint8_t *, unsigned short); +extern struct ecommunity *ecommunity_parse_ipv6(uint8_t *pnt, + unsigned short length); extern struct ecommunity *ecommunity_dup(struct ecommunity *); extern struct ecommunity *ecommunity_merge(struct ecommunity *, struct ecommunity *); @@ -202,6 +226,8 @@ extern bool ecommunity_cmp(const void *arg1, const void *arg2); extern void ecommunity_unintern(struct ecommunity **); extern unsigned int ecommunity_hash_make(const void *); extern struct ecommunity *ecommunity_str2com(const char *, int, int); +extern struct ecommunity *ecommunity_str2com_ipv6(const char *str, int type, + int keyword_included); extern char *ecommunity_ecom2str(struct ecommunity *, int, int); extern void ecommunity_strfree(char **s); extern bool ecommunity_match(const struct ecommunity *, @@ -209,9 +235,13 @@ extern bool ecommunity_match(const struct ecommunity *, extern char *ecommunity_str(struct ecommunity *); extern struct ecommunity_val *ecommunity_lookup(const struct ecommunity *, uint8_t, uint8_t); + extern bool ecommunity_add_val(struct ecommunity *ecom, struct ecommunity_val *eval, bool unique, bool overwrite); +extern bool ecommunity_add_val_ipv6(struct ecommunity *ecom, + struct ecommunity_val_ipv6 *eval, + bool unique, bool overwrite); /* for vpn */ extern struct ecommunity *ecommunity_new(void); diff --git a/bgpd/bgp_flowspec_vty.c b/bgpd/bgp_flowspec_vty.c index 3e3366bf8b..b4bec75f0a 100644 --- a/bgpd/bgp_flowspec_vty.c +++ b/bgpd/bgp_flowspec_vty.c @@ -263,7 +263,7 @@ void route_vty_out_flowspec(struct vty *vty, const struct prefix *p, { struct attr *attr; char return_string[BGP_FLOWSPEC_STRING_DISPLAY_MAX]; - char *s; + char *s = NULL, *s2 = NULL; json_object *json_nlri_path = NULL; json_object *json_ecom_path = NULL; json_object *json_time_path = NULL; @@ -306,21 +306,31 @@ void route_vty_out_flowspec(struct vty *vty, const struct prefix *p, } if (!path) return; - if (path->attr->ecommunity) { + if (path->attr && + (path->attr->ecommunity || path->attr->ipv6_ecommunity)) { /* Print attribute */ attr = path->attr; - s = ecommunity_ecom2str(attr->ecommunity, - ECOMMUNITY_FORMAT_ROUTE_MAP, 0); - if (!s) + if (attr->ecommunity) + s = ecommunity_ecom2str(attr->ecommunity, + ECOMMUNITY_FORMAT_ROUTE_MAP, 0); + if (attr->ipv6_ecommunity) + s2 = ecommunity_ecom2str(attr->ipv6_ecommunity, + ECOMMUNITY_FORMAT_ROUTE_MAP, 0); + if (!s && !s2) return; if (display == NLRI_STRING_FORMAT_LARGE) - vty_out(vty, "\t%s\n", s); + vty_out(vty, "\t%s%s%s\n", s ? s : "", + s2 && s ? " " : "", s2 ? s2 : ""); else if (display == NLRI_STRING_FORMAT_MIN) - vty_out(vty, "%s", s); + vty_out(vty, "%s%s", s ? s : "", s2 ? s2 : ""); else if (json_paths) { json_ecom_path = json_object_new_object(); - json_object_string_add(json_ecom_path, - "ecomlist", s); + if (s) + json_object_string_add(json_ecom_path, + "ecomlist", s); + if (s2) + json_object_string_add(json_ecom_path, + "ecom6list", s2); if (display == NLRI_STRING_FORMAT_JSON) json_object_array_add(json_paths, json_ecom_path); diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c index 314337d194..86fc4bc0a3 100644 --- a/bgpd/bgp_mplsvpn.c +++ b/bgpd/bgp_mplsvpn.c @@ -391,12 +391,11 @@ static bool ecom_intersect(struct ecommunity *e1, struct ecommunity *e2) if (!e1 || !e2) return false; - for (i = 0; i < e1->size; ++i) { for (j = 0; j < e2->size; ++j) { - if (!memcmp(e1->val + (i * ECOMMUNITY_SIZE), - e2->val + (j * ECOMMUNITY_SIZE), - ECOMMUNITY_SIZE)) { + if (!memcmp(e1->val + (i * e1->unit_size), + e2->val + (j * e2->unit_size), + e1->unit_size)) { return true; } @@ -2456,6 +2455,10 @@ vrf_id_t get_first_vrf_for_redirect_with_rt(struct ecommunity *eckey) { struct listnode *mnode, *mnnode; struct bgp *bgp; + afi_t afi = AFI_IP; + + if (eckey->unit_size == IPV6_ECOMMUNITY_SIZE) + afi = AFI_IP6; for (ALL_LIST_ELEMENTS(bm->bgp, mnode, mnnode, bgp)) { struct ecommunity *ec; @@ -2463,7 +2466,10 @@ vrf_id_t get_first_vrf_for_redirect_with_rt(struct ecommunity *eckey) if (bgp->inst_type != BGP_INSTANCE_TYPE_VRF) continue; - ec = bgp->vpn_policy[AFI_IP].import_redirect_rtlist; + ec = bgp->vpn_policy[afi].import_redirect_rtlist; + + if (ec && eckey->unit_size != ec->unit_size) + continue; if (ecom_intersect(ec, eckey)) return bgp->vrf_id; diff --git a/bgpd/bgp_pbr.c b/bgpd/bgp_pbr.c index 69c19fa7d8..dd01ba3e35 100644 --- a/bgpd/bgp_pbr.c +++ b/bgpd/bgp_pbr.c @@ -42,6 +42,9 @@ DEFINE_MTYPE_STATIC(BGPD, PBR_RULE, "PBR rule") DEFINE_MTYPE_STATIC(BGPD, PBR, "BGP PBR Context") DEFINE_MTYPE_STATIC(BGPD, PBR_VALMASK, "BGP PBR Val Mask Value") +/* chain strings too long to fit in one line */ +#define FSPEC_ACTION_EXCEED_LIMIT "flowspec actions exceeds limit" + RB_GENERATE(bgp_pbr_interface_head, bgp_pbr_interface, id_entry, bgp_pbr_interface_compare); struct bgp_pbr_interface_head ifaces_by_name_ipv4 = @@ -756,8 +759,10 @@ int bgp_pbr_build_and_validate_entry(const struct prefix *p, if (BGP_DEBUG(pbr, PBR_ERROR)) flog_err( EC_BGP_FLOWSPEC_PACKET, - "%s: flowspec actions exceeds limit (max %u)", - __func__, action_count); + "%s: %s (max %u)", + __func__, + FSPEC_ACTION_EXCEED_LIMIT, + action_count); break; } api_action = &api->actions[action_count - 1]; @@ -887,6 +892,45 @@ int bgp_pbr_build_and_validate_entry(const struct prefix *p, api->action_num++; } } + if (path && path->attr && path->attr->ipv6_ecommunity) { + struct ecommunity_val_ipv6 *ipv6_ecom_eval; + + ecom = path->attr->ipv6_ecommunity; + for (i = 0; i < ecom->size; i++) { + ipv6_ecom_eval = (struct ecommunity_val_ipv6 *) + (ecom->val + (i * ecom->unit_size)); + action_count++; + if (action_count > ACTIONS_MAX_NUM) { + if (BGP_DEBUG(pbr, PBR_ERROR)) + flog_err( + EC_BGP_FLOWSPEC_PACKET, + "%s: flowspec actions exceeds limit (max %u)", + __func__, action_count); + break; + } + api_action = &api->actions[action_count - 1]; + if ((ipv6_ecom_eval->val[1] == + (char)ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6) && + (ipv6_ecom_eval->val[0] == + (char)ECOMMUNITY_ENCODE_TRANS_EXP)) { + struct ecommunity *eckey = ecommunity_new(); + struct ecommunity_val_ipv6 ecom_copy; + + eckey->unit_size = IPV6_ECOMMUNITY_SIZE; + memcpy(&ecom_copy, ipv6_ecom_eval, + sizeof(struct ecommunity_val_ipv6)); + ecom_copy.val[1] = ECOMMUNITY_ROUTE_TARGET; + ecommunity_add_val_ipv6(eckey, &ecom_copy, + false, false); + api_action->action = ACTION_REDIRECT; + api_action->u.redirect_vrf = + get_first_vrf_for_redirect_with_rt( + eckey); + ecommunity_free(&eckey); + api->action_num++; + } + } + } /* if ECOMMUNITY_TRAFFIC_RATE = 0 as action * then reduce the API action list to that action */ @@ -2812,8 +2856,11 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, struct bgp_path_info *path, continue_loop = 0; break; case ACTION_REDIRECT: + if (api->afi == AFI_IP) + nh.type = NEXTHOP_TYPE_IPV4; + else + nh.type = NEXTHOP_TYPE_IPV6; nh.vrf_id = api->actions[i].u.redirect_vrf; - nh.type = NEXTHOP_TYPE_IPV4; bgp_pbr_policyroute_add_to_zebra(bgp, path, &bpf, &bpof, &nh, &rate); continue_loop = 0; diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index af02e13340..191c1a561c 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -7332,15 +7332,20 @@ DEFPY( } static int set_ecom_list(struct vty *vty, int argc, struct cmd_token **argv, - struct ecommunity **list) + struct ecommunity **list, int is_rt6) { struct ecommunity *ecom = NULL; struct ecommunity *ecomadd; for (; argc; --argc, ++argv) { - - ecomadd = ecommunity_str2com(argv[0]->arg, - ECOMMUNITY_ROUTE_TARGET, 0); + if (is_rt6) + ecomadd = ecommunity_str2com_ipv6(argv[0]->arg, + ECOMMUNITY_ROUTE_TARGET, + 0); + else + ecomadd = ecommunity_str2com(argv[0]->arg, + ECOMMUNITY_ROUTE_TARGET, + 0); if (!ecomadd) { vty_out(vty, "Malformed community-list value\n"); if (ecom) @@ -7660,7 +7665,7 @@ DEFPY (af_rt_vpn_imexport, vty_out(vty, "%% Missing RTLIST\n"); return CMD_WARNING_CONFIG_FAILED; } - ret = set_ecom_list(vty, argc - idx, argv + idx, &ecom); + ret = set_ecom_list(vty, argc - idx, argv + idx, &ecom, 0); if (ret != CMD_SUCCESS) { return ret; } @@ -8017,34 +8022,44 @@ DEFPY (bgp_imexport_vpn, DEFPY (af_routetarget_import, af_routetarget_import_cmd, - "[no] redirect import RTLIST...", + "[no] redirect import RTLIST...", NO_STR "Specify route target list\n" "Specify route target list\n" + "Specify route target list\n" + "Specify route target list\n" "Flow-spec redirect type route target\n" "Import routes to this address-family\n" - "Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN)\n") + "Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN|IPV6:MN)\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); int ret; struct ecommunity *ecom = NULL; afi_t afi; - int idx = 0; + int idx = 0, idx_unused = 0; int yes = 1; + int rt6 = 0; if (argv_find(argv, argc, "no", &idx)) yes = 0; + if (argv_find(argv, argc, "rt6", &idx_unused) || + argv_find(argv, argc, "route-target6", &idx_unused)) + rt6 = 1; + afi = vpn_policy_getafi(vty, bgp, false); if (afi == AFI_MAX) return CMD_WARNING_CONFIG_FAILED; + if (rt6 && afi != AFI_IP6) + return CMD_WARNING_CONFIG_FAILED; + if (yes) { if (!argv_find(argv, argc, "RTLIST", &idx)) { vty_out(vty, "%% Missing RTLIST\n"); return CMD_WARNING_CONFIG_FAILED; } - ret = set_ecom_list(vty, argc - idx, argv + idx, &ecom); + ret = set_ecom_list(vty, argc - idx, argv + idx, &ecom, rt6); if (ret != CMD_SUCCESS) return ret; } @@ -14577,7 +14592,13 @@ static void bgp_vpn_policy_config_write_afi(struct vty *vty, struct bgp *bgp, ECOMMUNITY_FORMAT_ROUTE_MAP, ECOMMUNITY_ROUTE_TARGET); - vty_out(vty, "%*srt redirect import %s\n", indent, "", b); + if (bgp->vpn_policy[afi].import_redirect_rtlist->unit_size + != ECOMMUNITY_SIZE) + vty_out(vty, "%*srt redirect import %s\n", + indent, "", b); + else + vty_out(vty, "%*srt redirect import %s\n", + indent, "", b); XFREE(MTYPE_ECOMMUNITY_STR, b); } } diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index e84b0e43b9..c5af6c2161 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -1530,6 +1530,7 @@ struct bgp_nlri { #define BGP_ATTR_AS_PATHLIMIT 21 #define BGP_ATTR_PMSI_TUNNEL 22 #define BGP_ATTR_ENCAP 23 +#define BGP_ATTR_IPV6_EXT_COMMUNITIES 25 #define BGP_ATTR_LARGE_COMMUNITIES 32 #define BGP_ATTR_PREFIX_SID 40 #ifdef ENABLE_BGP_VNC_ATTR From 8f2421871071160ad5c856d6c389a3f7d17a2325 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Thu, 17 Oct 2019 16:11:57 +0200 Subject: [PATCH 08/19] bgpd: support for flowspec interface list per address-family in addition to ipv4 flowspec, ipv6 flowspec address family can configure its own list of interfaces to monitor. this permits filtering the policy routing only on some interfaces. Signed-off-by: Philippe Guibert --- bgpd/bgp_ecommunity.c | 19 ++++++++++--------- bgpd/bgp_flowspec_vty.c | 33 ++++++++++++++++++++++++--------- bgpd/bgp_pbr.c | 9 ++++++--- bgpd/bgp_pbr.h | 2 ++ bgpd/bgp_zebra.c | 21 +++++++++++++-------- 5 files changed, 55 insertions(+), 29 deletions(-) diff --git a/bgpd/bgp_ecommunity.c b/bgpd/bgp_ecommunity.c index 9a987ad1e7..f45fa2bed5 100644 --- a/bgpd/bgp_ecommunity.c +++ b/bgpd/bgp_ecommunity.c @@ -195,8 +195,7 @@ ecommunity_uniq_sort_internal(struct ecommunity *ecom, new->unit_size = ecom_size; for (i = 0; i < ecom->size; i++) { - eval = (void *)(ecom->val - + (i * ecom_size)); + eval = (void *)(ecom->val + (i * ecom_size)); ecommunity_add_val_internal(new, eval, false, false, ecom_size); } return new; @@ -549,20 +548,24 @@ static const char *ecommunity_gettoken(const char *str, 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)) + ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6, + 1, 0, NULL, &ip6, as, eval_ptr)) goto error; + *token = ecommunity_token_val6; while (isdigit((int)*p) || *p == ':' || *p == '.') { p++; @@ -667,8 +670,7 @@ static struct ecommunity *ecommunity_str2com_internal(const char *str, int type, case ecommunity_token_val: if (keyword_included) { if (!keyword) { - if (ecom) - ecommunity_free(&ecom); + ecommunity_free(&ecom); return NULL; } keyword = 0; @@ -682,8 +684,7 @@ static struct ecommunity *ecommunity_str2com_internal(const char *str, int type, case ecommunity_token_val6: if (keyword_included) { if (!keyword) { - if (ecom) - ecommunity_free(&ecom); + ecommunity_free(&ecom); return NULL; } keyword = 0; @@ -1039,7 +1040,7 @@ char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter) sub_type = *pnt++; if (sub_type == ECOMMUNITY_ROUTE_TARGET) { - char buf[64]; + char buf[ECOMMUNITY_STRLEN]; memset(buf, 0, sizeof(buf)); ecommunity_rt_soo_str_internal(buf, sizeof(buf), diff --git a/bgpd/bgp_flowspec_vty.c b/bgpd/bgp_flowspec_vty.c index b4bec75f0a..cc6d1ed457 100644 --- a/bgpd/bgp_flowspec_vty.c +++ b/bgpd/bgp_flowspec_vty.c @@ -509,10 +509,17 @@ int bgp_fs_config_write_pbr(struct vty *vty, struct bgp *bgp, struct bgp_pbr_interface_head *head; bool bgp_pbr_interface_any; - if (!bgp_pbr_cfg || safi != SAFI_FLOWSPEC || afi != AFI_IP) + if (!bgp_pbr_cfg || safi != SAFI_FLOWSPEC) return 0; - head = &(bgp_pbr_cfg->ifaces_by_name_ipv4); - bgp_pbr_interface_any = bgp_pbr_cfg->pbr_interface_any_ipv4; + if (afi == AFI_IP) { + head = &(bgp_pbr_cfg->ifaces_by_name_ipv4); + bgp_pbr_interface_any = bgp_pbr_cfg->pbr_interface_any_ipv4; + } else if (afi == AFI_IP6) { + head = &(bgp_pbr_cfg->ifaces_by_name_ipv6); + bgp_pbr_interface_any = bgp_pbr_cfg->pbr_interface_any_ipv6; + } else { + return 0; + } if (!RB_EMPTY(bgp_pbr_interface_head, head) || !bgp_pbr_interface_any) declare_node = true; @@ -523,7 +530,8 @@ int bgp_fs_config_write_pbr(struct vty *vty, struct bgp *bgp, } static int bgp_fs_local_install_interface(struct bgp *bgp, - const char *no, const char *ifname) + const char *no, const char *ifname, + afi_t afi) { struct bgp_pbr_interface *pbr_if; struct bgp_pbr_config *bgp_pbr_cfg = bgp->bgp_pbr_cfg; @@ -532,14 +540,19 @@ static int bgp_fs_local_install_interface(struct bgp *bgp, if (!bgp_pbr_cfg) return CMD_SUCCESS; - head = &(bgp_pbr_cfg->ifaces_by_name_ipv4); - bgp_pbr_interface_any = &(bgp_pbr_cfg->pbr_interface_any_ipv4); + if (afi == AFI_IP) { + head = &(bgp_pbr_cfg->ifaces_by_name_ipv4); + bgp_pbr_interface_any = &(bgp_pbr_cfg->pbr_interface_any_ipv4); + } else { + head = &(bgp_pbr_cfg->ifaces_by_name_ipv6); + bgp_pbr_interface_any = &(bgp_pbr_cfg->pbr_interface_any_ipv6); + } if (no) { if (!ifname) { if (*bgp_pbr_interface_any) { *bgp_pbr_interface_any = false; /* remove all other interface list */ - bgp_pbr_reset(bgp, AFI_IP); + bgp_pbr_reset(bgp, afi); } return CMD_SUCCESS; } @@ -563,7 +576,7 @@ static int bgp_fs_local_install_interface(struct bgp *bgp, if (!*bgp_pbr_interface_any) { /* remove all other interface list */ - bgp_pbr_reset(bgp, AFI_IP); + bgp_pbr_reset(bgp, afi); *bgp_pbr_interface_any = true; } } @@ -583,7 +596,8 @@ DEFUN (bgp_fs_local_install_ifname, char *ifname = argv_find(argv, argc, "INTERFACE", &idx) ? argv[idx]->arg : NULL; - return bgp_fs_local_install_interface(bgp, no, ifname); + return bgp_fs_local_install_interface(bgp, no, ifname, + bgp_node_afi(vty)); } extern int bgp_flowspec_display_match_per_ip(afi_t afi, struct bgp_table *rib, @@ -621,4 +635,5 @@ void bgp_flowspec_vty_init(void) install_element(ENABLE_NODE, &no_debug_bgp_flowspec_cmd); install_element(CONFIG_NODE, &no_debug_bgp_flowspec_cmd); install_element(BGP_FLOWSPECV4_NODE, &bgp_fs_local_install_ifname_cmd); + install_element(BGP_FLOWSPECV6_NODE, &bgp_fs_local_install_ifname_cmd); } diff --git a/bgpd/bgp_pbr.c b/bgpd/bgp_pbr.c index dd01ba3e35..552f6a51fe 100644 --- a/bgpd/bgp_pbr.c +++ b/bgpd/bgp_pbr.c @@ -1388,6 +1388,7 @@ void bgp_pbr_cleanup(struct bgp *bgp) if (bgp->bgp_pbr_cfg == NULL) return; bgp_pbr_reset(bgp, AFI_IP); + bgp_pbr_reset(bgp, AFI_IP6); XFREE(MTYPE_PBR, bgp->bgp_pbr_cfg); } @@ -2935,10 +2936,12 @@ void bgp_pbr_reset(struct bgp *bgp, afi_t afi) struct bgp_pbr_interface_head *head; struct bgp_pbr_interface *pbr_if; - if (!bgp_pbr_cfg || afi != AFI_IP) + if (!bgp_pbr_cfg) return; - head = &(bgp_pbr_cfg->ifaces_by_name_ipv4); - + if (afi == AFI_IP) + head = &(bgp_pbr_cfg->ifaces_by_name_ipv4); + else + head = &(bgp_pbr_cfg->ifaces_by_name_ipv6); while (!RB_EMPTY(bgp_pbr_interface_head, head)) { pbr_if = RB_ROOT(bgp_pbr_interface_head, head); RB_REMOVE(bgp_pbr_interface_head, head, pbr_if); diff --git a/bgpd/bgp_pbr.h b/bgpd/bgp_pbr.h index 9d6360094c..dc2bb7260e 100644 --- a/bgpd/bgp_pbr.h +++ b/bgpd/bgp_pbr.h @@ -159,6 +159,8 @@ extern int bgp_pbr_interface_compare(const struct bgp_pbr_interface *a, struct bgp_pbr_config { struct bgp_pbr_interface_head ifaces_by_name_ipv4; bool pbr_interface_any_ipv4; + struct bgp_pbr_interface_head ifaces_by_name_ipv6; + bool pbr_interface_any_ipv6; }; extern struct bgp_pbr_config *bgp_pbr_cfg; diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index 73ce990724..dba114f86a 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -2984,7 +2984,8 @@ void bgp_send_pbr_ipset_entry_match(struct bgp_pbr_match_entry *pbrime, pbrime->install_in_progress = true; } -static void bgp_encode_pbr_interface_list(struct bgp *bgp, struct stream *s) +static void bgp_encode_pbr_interface_list(struct bgp *bgp, struct stream *s, + uint8_t family) { struct bgp_pbr_config *bgp_pbr_cfg = bgp->bgp_pbr_cfg; struct bgp_pbr_interface_head *head; @@ -2993,8 +2994,10 @@ static void bgp_encode_pbr_interface_list(struct bgp *bgp, struct stream *s) if (!bgp_pbr_cfg) return; - head = &(bgp_pbr_cfg->ifaces_by_name_ipv4); - + if (family == AF_INET) + head = &(bgp_pbr_cfg->ifaces_by_name_ipv4); + else + head = &(bgp_pbr_cfg->ifaces_by_name_ipv6); RB_FOREACH (pbr_if, bgp_pbr_interface_head, head) { ifp = if_lookup_by_name(pbr_if->name, bgp->vrf_id); if (ifp) @@ -3002,7 +3005,7 @@ static void bgp_encode_pbr_interface_list(struct bgp *bgp, struct stream *s) } } -static int bgp_pbr_get_ifnumber(struct bgp *bgp) +static int bgp_pbr_get_ifnumber(struct bgp *bgp, uint8_t family) { struct bgp_pbr_config *bgp_pbr_cfg = bgp->bgp_pbr_cfg; struct bgp_pbr_interface_head *head; @@ -3011,8 +3014,10 @@ static int bgp_pbr_get_ifnumber(struct bgp *bgp) if (!bgp_pbr_cfg) return 0; - head = &(bgp_pbr_cfg->ifaces_by_name_ipv4); - + if (family == AF_INET) + head = &(bgp_pbr_cfg->ifaces_by_name_ipv4); + else + head = &(bgp_pbr_cfg->ifaces_by_name_ipv6); RB_FOREACH (pbr_if, bgp_pbr_interface_head, head) { if (if_lookup_by_name(pbr_if->name, bgp->vrf_id)) cnt++; @@ -3043,10 +3048,10 @@ void bgp_send_pbr_iptable(struct bgp_pbr_action *pba, VRF_DEFAULT); bgp_encode_pbr_iptable_match(s, pba, pbm); - nb_interface = bgp_pbr_get_ifnumber(pba->bgp); + nb_interface = bgp_pbr_get_ifnumber(pba->bgp, pbm->family); stream_putl(s, nb_interface); if (nb_interface) - bgp_encode_pbr_interface_list(pba->bgp, s); + bgp_encode_pbr_interface_list(pba->bgp, s, pbm->family); stream_putw_at(s, 0, stream_get_endp(s)); ret = zclient_send_message(zclient); if (install) { From c9b1139ace19fd9c3bc255eddd4d5b9f31cf3c1c Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Fri, 18 Oct 2019 22:30:24 +0200 Subject: [PATCH 09/19] zebra: add icmpv6 table of type / code this table contains the list of icmp type/code for icmpv6. Signed-off-by: Philippe Guibert --- zebra/zebra_pbr.c | 32 +++++++++++++++++++++++++++++--- zebra/zebra_pbr.h | 1 + 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/zebra/zebra_pbr.c b/zebra/zebra_pbr.c index 166689385c..1bed0b48bb 100644 --- a/zebra/zebra_pbr.c +++ b/zebra/zebra_pbr.c @@ -83,6 +83,27 @@ const struct message icmp_typecode_str[] = { {0} }; +const struct message icmpv6_typecode_str[] = { + { 128 << 8, "echo-request"}, + { 129 << 8, "echo-reply"}, + { 1 << 8, "no-route"}, + { (1 << 8) + 1, "communication-prohibited"}, + { (1 << 8) + 3, "address-unreachable"}, + { (1 << 8) + 4, "port-unreachable"}, + { (2 << 8), "packet-too-big"}, + { 3 << 0, "ttl-zero-during-transit"}, + { (3 << 8) + 1, "ttl-zero-during-reassembly"}, + { 4 << 0, "bad-header"}, + { (4 << 0) + 1, "unknown-header-type"}, + { (4 << 0) + 2, "unknown-option"}, + { 133 << 8, "router-solicitation"}, + { 134 << 8, "router-advertisement"}, + { 135 << 8, "neighbor-solicitation"}, + { 136 << 8, "neighbor-advertisement"}, + { 137 << 8, "redirect"}, + {0} +}; + /* definitions */ static const struct message tcp_value_str[] = { {TCP_HEADER_FIN, "FIN"}, @@ -900,6 +921,9 @@ static void zebra_pbr_display_icmp(struct vty *vty, { char decoded_str[20]; uint16_t port; + struct zebra_pbr_ipset *zpi; + + zpi = zpie->backpointer; /* range icmp type */ if (zpie->src_port_max || zpie->dst_port_max) { @@ -912,8 +936,10 @@ static void zebra_pbr_display_icmp(struct vty *vty, memset(decoded_str, 0, sizeof(decoded_str)); snprintf(decoded_str, sizeof(decoded_str), "%u/%u", zpie->src_port_min, zpie->dst_port_min); - vty_out(vty, ":icmp:%s", - lookup_msg(icmp_typecode_str, + vty_out(vty, ":%s:%s", + zpi->family == AF_INET6 ? "ipv6-icmp" : "icmp", + lookup_msg(zpi->family == AF_INET6 ? + icmpv6_typecode_str : icmp_typecode_str, port, decoded_str)); } } @@ -1115,7 +1141,7 @@ static void zebra_pbr_show_iptable_unit(struct zebra_pbr_iptable *iptable, vty_out(vty, "IPtable %s family %s action %s (%u)\n", iptable->ipset_name, - iptable->family == AF_INET ? "AF_INET" : "AF_INET6", + family2str(iptable->family), iptable->action == ZEBRA_IPTABLES_DROP ? "drop" : "redirect", iptable->unique); if (iptable->type == IPSET_NET_PORT || diff --git a/zebra/zebra_pbr.h b/zebra/zebra_pbr.h index c84e603723..e7504a3547 100644 --- a/zebra/zebra_pbr.h +++ b/zebra/zebra_pbr.h @@ -163,6 +163,7 @@ struct zebra_pbr_iptable { }; extern const struct message icmp_typecode_str[]; +extern const struct message icmpv6_typecode_str[]; const char *zebra_pbr_ipset_type2str(uint32_t type); From 173ebf4784a50bc86c5148cfa5b69d740ee4a14d Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Mon, 21 Oct 2019 11:05:44 +0200 Subject: [PATCH 10/19] bgpd: limit policy routing with flowlabel, fragment, and prefix offset the following 3 options are not supported in current implementation of policy routing. for that, inform the user that the flowspec entry is invalid when attempting to use : - prefix offset with src, or dst ipv6 address ( see [1]) - flowlabel value - limitation due to [0] - fragment ( implementation not done today). [0] https://bugzilla.netfilter.org/show_bug.cgi?id=1375 [1] https://bugzilla.netfilter.org/show_bug.cgi?id=1373 Signed-off-by: Philippe Guibert --- bgpd/bgp_pbr.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/bgpd/bgp_pbr.c b/bgpd/bgp_pbr.c index 552f6a51fe..945a2e494a 100644 --- a/bgpd/bgp_pbr.c +++ b/bgpd/bgp_pbr.c @@ -44,6 +44,7 @@ DEFINE_MTYPE_STATIC(BGPD, PBR_VALMASK, "BGP PBR Val Mask Value") /* chain strings too long to fit in one line */ #define FSPEC_ACTION_EXCEED_LIMIT "flowspec actions exceeds limit" +#define IPV6_FRAGMENT_INVALID "fragment not valid for IPv6 for this implementation" RB_GENERATE(bgp_pbr_interface_head, bgp_pbr_interface, id_entry, bgp_pbr_interface_compare); @@ -525,6 +526,13 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api) api->match_protocol_num); return 0; } + if (api->src_prefix_offset > 0 || + api->dst_prefix_offset > 0) { + if (BGP_DEBUG(pbr, PBR)) + zlog_debug("BGP: match prefix offset:" + "implementation does not support it."); + return 0; + } if (api->match_protocol_num == 1 && api->protocol[0].value != PROTOCOL_UDP && api->protocol[0].value != PROTOCOL_ICMP && @@ -626,6 +634,10 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api) "too complex. ignoring."); return 0; } + if (BGP_DEBUG(pbr, PBR)) + zlog_debug("BGP: match FlowLabel operations " + "not supported. ignoring."); + return 0; } if (api->match_fragment_num) { char fail_str[64]; @@ -658,6 +670,11 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api) api->fragment[i].value); } } + if (api->afi == AFI_IP6) { + success = false; + snprintf(fail_str, sizeof(fail_str), + "%s", IPV6_FRAGMENT_INVALID); + } } else snprintf(fail_str, sizeof(fail_str), "too complex. ignoring"); From f2ead0a540abdd2eaacd7ced544f173937dccd69 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Mon, 21 Oct 2019 11:12:25 +0200 Subject: [PATCH 11/19] bgpd: fallback proto icmp/v6 to appropriate l3 filter if match protocol is icmp, then this protocol will be filtered with afi = ipv4. however, if afi = ipv6, then the icmp protocol will fall back to icmpv6. note that this patch has also been done to simplify the policy routing, as BGP will only handle TCP/UDP/ICMP(v4 or v6) protocols. Signed-off-by: Philippe Guibert --- bgpd/bgp_pbr.c | 6 +++++- bgpd/bgp_pbr.h | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/bgpd/bgp_pbr.c b/bgpd/bgp_pbr.c index 945a2e494a..32b57aa96d 100644 --- a/bgpd/bgp_pbr.c +++ b/bgpd/bgp_pbr.c @@ -536,6 +536,7 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api) if (api->match_protocol_num == 1 && api->protocol[0].value != PROTOCOL_UDP && api->protocol[0].value != PROTOCOL_ICMP && + api->protocol[0].value != PROTOCOL_ICMPV6 && api->protocol[0].value != PROTOCOL_TCP) { if (BGP_DEBUG(pbr, PBR)) zlog_debug("BGP: match protocol operations:protocol (%d) not supported. ignoring", @@ -2694,8 +2695,11 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, struct bgp_path_info *path, bpf.type = api->type; memset(&nh, 0, sizeof(struct nexthop)); nh.vrf_id = VRF_UNKNOWN; - if (api->match_protocol_num) + if (api->match_protocol_num) { proto = (uint8_t)api->protocol[0].value; + if (api->afi == AF_INET6 && proto == IPPROTO_ICMPV6) + proto = IPPROTO_ICMP; + } /* if match_port is selected, then either src or dst port will be parsed * but not both at the same time */ diff --git a/bgpd/bgp_pbr.h b/bgpd/bgp_pbr.h index dc2bb7260e..379ac40672 100644 --- a/bgpd/bgp_pbr.h +++ b/bgpd/bgp_pbr.h @@ -125,6 +125,7 @@ struct bgp_pbr_entry_main { #define PROTOCOL_UDP 17 #define PROTOCOL_TCP 6 #define PROTOCOL_ICMP 1 +#define PROTOCOL_ICMPV6 58 struct bgp_pbr_match_val protocol[BGP_PBR_MATCH_VAL_MAX]; struct bgp_pbr_match_val src_port[BGP_PBR_MATCH_VAL_MAX]; struct bgp_pbr_match_val dst_port[BGP_PBR_MATCH_VAL_MAX]; From 34540b0d7f6fa5e18e553d10dda0ab4aa01687bd Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Tue, 22 Oct 2019 09:21:28 +0200 Subject: [PATCH 12/19] bgpd: fill in local ecommunity context with ecom unit length because the same extended community can be used for storing ipv6 and ipv4 et communities, the unit length must be stored. do not forget to set the standard value in bgp evpn. Signed-off-by: Philippe Guibert --- bgpd/bgp_evpn.c | 30 ++++++++++++++++++------------ bgpd/bgp_evpn_mh.c | 4 ++++ 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index 3bcef1a555..e552866f38 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -726,6 +726,7 @@ static void build_evpn_type5_route_extcomm(struct bgp *bgp_vrf, memset(&ecom_encap, 0, sizeof(ecom_encap)); encode_encap_extcomm(tnl_type, &eval); ecom_encap.size = 1; + ecom_encap.unit_size = ECOMMUNITY_SIZE; ecom_encap.val = (uint8_t *)eval.val; /* Add Encap */ @@ -788,6 +789,7 @@ static void build_evpn_route_extcomm(struct bgpevpn *vpn, struct attr *attr, memset(&ecom_encap, 0, sizeof(ecom_encap)); encode_encap_extcomm(tnl_type, &eval); ecom_encap.size = 1; + ecom_encap.unit_size = ECOMMUNITY_SIZE; ecom_encap.val = (uint8_t *)eval.val; /* Add Encap */ @@ -816,6 +818,7 @@ static void build_evpn_route_extcomm(struct bgpevpn *vpn, struct attr *attr, memset(&ecom_sticky, 0, sizeof(ecom_sticky)); encode_mac_mobility_extcomm(1, seqnum, &eval_sticky); ecom_sticky.size = 1; + ecom_sticky.unit_size = ECOMMUNITY_SIZE; ecom_sticky.val = (uint8_t *)eval_sticky.val; attr->ecommunity = ecommunity_merge(attr->ecommunity, &ecom_sticky); @@ -832,6 +835,7 @@ static void build_evpn_route_extcomm(struct bgpevpn *vpn, struct attr *attr, memset(&ecom_default_gw, 0, sizeof(ecom_default_gw)); encode_default_gw_extcomm(&eval_default_gw); ecom_default_gw.size = 1; + ecom_default_gw.unit_size = ECOMMUNITY_SIZE; ecom_default_gw.val = (uint8_t *)eval_default_gw.val; attr->ecommunity = ecommunity_merge(attr->ecommunity, &ecom_default_gw); @@ -842,6 +846,7 @@ static void build_evpn_route_extcomm(struct bgpevpn *vpn, struct attr *attr, memset(&ecom_na, 0, sizeof(ecom_na)); encode_na_flag_extcomm(&eval_na, attr->router_flag, proxy); ecom_na.size = 1; + ecom_na.unit_size = ECOMMUNITY_SIZE; ecom_na.val = (uint8_t *)eval_na.val; attr->ecommunity = ecommunity_merge(attr->ecommunity, &ecom_na); @@ -871,7 +876,7 @@ static void add_mac_mobility_to_attr(uint32_t seq_num, struct attr *attr) if (attr->ecommunity) { for (i = 0; i < attr->ecommunity->size; i++) { - pnt = attr->ecommunity->val + (i * 8); + pnt = attr->ecommunity->val + (i * attr->ecommunity->unit_size); type = *pnt++; sub_type = *pnt++; @@ -879,7 +884,7 @@ static void add_mac_mobility_to_attr(uint32_t seq_num, struct attr *attr) && sub_type == ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY) { ecom_val_ptr = - (attr->ecommunity->val + (i * 8)); + (attr->ecommunity->val + (i * attr->ecommunity->unit_size)); break; } } @@ -887,12 +892,13 @@ static void add_mac_mobility_to_attr(uint32_t seq_num, struct attr *attr) /* Update the existing MM ecommunity */ if (ecom_val_ptr) { - memcpy(ecom_val_ptr, eval.val, sizeof(char) * ECOMMUNITY_SIZE); + memcpy(ecom_val_ptr, eval.val, sizeof(char) * attr->ecommunity->unit_size); } /* Add MM to existing */ else { memset(&ecom_tmp, 0, sizeof(ecom_tmp)); ecom_tmp.size = 1; + ecom_tmp.unit_size = ECOMMUNITY_SIZE; ecom_tmp.val = (uint8_t *)eval.val; if (attr->ecommunity) @@ -2713,9 +2719,9 @@ static int is_route_matching_for_vrf(struct bgp *bgp_vrf, struct vrf_irt_node *irt; /* Only deal with RTs */ - pnt = (ecom->val + (i * ECOMMUNITY_SIZE)); + pnt = (ecom->val + (i * ecom->unit_size)); eval = (struct ecommunity_val *)(ecom->val - + (i * ECOMMUNITY_SIZE)); + + (i * ecom->unit_size)); type = *pnt++; sub_type = *pnt++; if (sub_type != ECOMMUNITY_ROUTE_TARGET) @@ -2737,7 +2743,7 @@ static int is_route_matching_for_vrf(struct bgp *bgp_vrf, if (type == ECOMMUNITY_ENCODE_AS || type == ECOMMUNITY_ENCODE_AS4 || type == ECOMMUNITY_ENCODE_IP) { - memcpy(&eval_tmp, eval, ECOMMUNITY_SIZE); + memcpy(&eval_tmp, eval, ecom->unit_size); mask_ecom_global_admin(&eval_tmp, eval); irt = lookup_vrf_import_rt(&eval_tmp); } @@ -2780,9 +2786,9 @@ static int is_route_matching_for_vni(struct bgp *bgp, struct bgpevpn *vpn, struct irt_node *irt; /* Only deal with RTs */ - pnt = (ecom->val + (i * ECOMMUNITY_SIZE)); + pnt = (ecom->val + (i * ecom->unit_size)); eval = (struct ecommunity_val *)(ecom->val - + (i * ECOMMUNITY_SIZE)); + + (i * ecom->unit_size)); type = *pnt++; sub_type = *pnt++; if (sub_type != ECOMMUNITY_ROUTE_TARGET) @@ -2804,7 +2810,7 @@ static int is_route_matching_for_vni(struct bgp *bgp, struct bgpevpn *vpn, if (type == ECOMMUNITY_ENCODE_AS || type == ECOMMUNITY_ENCODE_AS4 || type == ECOMMUNITY_ENCODE_IP) { - memcpy(&eval_tmp, eval, ECOMMUNITY_SIZE); + memcpy(&eval_tmp, eval, ecom->unit_size); mask_ecom_global_admin(&eval_tmp, eval); irt = lookup_import_rt(bgp, &eval_tmp); } @@ -3220,9 +3226,9 @@ static int install_uninstall_evpn_route(struct bgp *bgp, afi_t afi, safi_t safi, struct bgp_evpn_es *es; /* Only deal with RTs */ - pnt = (ecom->val + (i * ECOMMUNITY_SIZE)); + pnt = (ecom->val + (i * ecom->unit_size)); eval = (struct ecommunity_val *)(ecom->val - + (i * ECOMMUNITY_SIZE)); + + (i * ecom->unit_size)); type = *pnt++; sub_type = *pnt++; if (sub_type != ECOMMUNITY_ROUTE_TARGET) @@ -3261,7 +3267,7 @@ static int install_uninstall_evpn_route(struct bgp *bgp, afi_t afi, safi_t safi, if (type == ECOMMUNITY_ENCODE_AS || type == ECOMMUNITY_ENCODE_AS4 || type == ECOMMUNITY_ENCODE_IP) { - memcpy(&eval_tmp, eval, ECOMMUNITY_SIZE); + memcpy(&eval_tmp, eval, ecom->unit_size); mask_ecom_global_admin(&eval_tmp, eval); irt = lookup_import_rt(bgp, &eval_tmp); vrf_irt = lookup_vrf_import_rt(&eval_tmp); diff --git a/bgpd/bgp_evpn_mh.c b/bgpd/bgp_evpn_mh.c index eb65c43bb9..8dfb7a305e 100644 --- a/bgpd/bgp_evpn_mh.c +++ b/bgpd/bgp_evpn_mh.c @@ -523,6 +523,7 @@ static void bgp_evpn_type4_route_extcomm_build(struct bgp_evpn_es *es, memset(&ecom_encap, 0, sizeof(ecom_encap)); encode_encap_extcomm(tnl_type, &eval); ecom_encap.size = 1; + ecom_encap.unit_size = ECOMMUNITY_SIZE; ecom_encap.val = (uint8_t *)eval.val; attr->ecommunity = ecommunity_dup(&ecom_encap); @@ -532,6 +533,7 @@ static void bgp_evpn_type4_route_extcomm_build(struct bgp_evpn_es *es, es_get_system_mac(&es->esi, &mac); encode_es_rt_extcomm(&eval_es_rt, &mac); ecom_es_rt.size = 1; + ecom_es_rt.unit_size = ECOMMUNITY_SIZE; ecom_es_rt.val = (uint8_t *)eval_es_rt.val; attr->ecommunity = ecommunity_merge(attr->ecommunity, &ecom_es_rt); @@ -775,6 +777,7 @@ static void bgp_evpn_type1_es_route_extcomm_build(struct bgp_evpn_es *es, memset(&ecom_encap, 0, sizeof(ecom_encap)); encode_encap_extcomm(tnl_type, &eval); ecom_encap.size = 1; + ecom_encap.unit_size = ECOMMUNITY_SIZE; ecom_encap.val = (uint8_t *)eval.val; attr->ecommunity = ecommunity_dup(&ecom_encap); @@ -782,6 +785,7 @@ static void bgp_evpn_type1_es_route_extcomm_build(struct bgp_evpn_es *es, encode_esi_label_extcomm(&eval_esi_label, false /*single_active*/); ecom_esi_label.size = 1; + ecom_esi_label.unit_size = ECOMMUNITY_SIZE; ecom_esi_label.val = (uint8_t *)eval_esi_label.val; attr->ecommunity = ecommunity_merge(attr->ecommunity, &ecom_esi_label); From 8bc3c9ba755895468ac4bbcdb70416503a5710a6 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Mon, 20 Apr 2020 09:18:20 +0200 Subject: [PATCH 13/19] doc: add bgp flowspec ipv6 documentation add bgp flowspdec ipv6 documentation. Signed-off-by: Philippe Guibert --- doc/user/flowspec.rst | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/doc/user/flowspec.rst b/doc/user/flowspec.rst index b274afe8a2..d3eb25a7c7 100644 --- a/doc/user/flowspec.rst +++ b/doc/user/flowspec.rst @@ -22,6 +22,9 @@ more or less complex combination of the following: - Layer 3 information: DSCP value, Protocol type, packet length, fragmentation. - Misc layer 4 TCP flags. +Note that if originally Flowspec defined IPv4 rules, this is also possible to use +IPv6 address-family. The same set of combinations as defined for IPv4 can be used. + A combination of the above rules is applied for traffic filtering. This is encoded as part of specific BGP extended communities and the action can range from the obvious rerouting (to nexthop or to separate VRF) to shaping, or @@ -31,6 +34,7 @@ The following IETF drafts and RFCs have been used to implement FRR Flowspec: - :rfc:`5575` - [Draft-IETF-IDR-Flowspec-redirect-IP]_ +- [Draft-IETF-IDR-Flow-Spec-V6]_ .. _design-principles-flowspec: @@ -108,9 +112,13 @@ As of today, it is only possible to configure Flowspec on the default VRF. router bgp neighbor remote-as + neighbor remote-as address-family ipv4 flowspec neighbor activate - exit + exit + address-family ipv6 flowspec + neighbor activate + exit exit You can see Flowspec entries, by using one of the following show commands: @@ -118,6 +126,8 @@ You can see Flowspec entries, by using one of the following show commands: .. index:: show bgp ipv4 flowspec [detail | A.B.C.D] .. clicmd:: show bgp ipv4 flowspec [detail | A.B.C.D] +.. index:: show bgp ipv6 flowspec [detail | A:B::C:D] +.. clicmd:: show bgp ipv6 flowspec [detail | A:B::C:D] Per-interface configuration ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -183,6 +193,28 @@ interfaces are created with private IP addressing scheme. exit exit +Similarly, it is possible to do the same for IPv6 flowspec rules, by using +an IPv6 extended community. The format is defined on :rfc:`5701`, and that +community contains an IPv6 address encoded in the attribute, and matches the +locally configured imported route target IPv6 defined under the appropriate +BGP VRF instance. Below example defines an IPv6 extended community containing +`E:F::G:H` address followed by 2 bytes chosen by admin ( here `JJ`). + +.. code-block:: frr + + router bgp + neighbor remote-as + address-family ipv6 flowspec + neighbor A:B::C:D activate + exit + exit + router bgp vrf vrf2 + address-family ipv6 unicast + rt6 redirect import + exit + exit + + Flowspec monitoring & troubleshooting ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -348,4 +380,5 @@ inside FRRouting. .. [Draft-IETF-IDR-Flowspec-redirect-IP] .. [Draft-IETF-IDR-Flowspec-Interface-Set] +.. [Draft-IETF-IDR-Flow-Spec-V6] .. [Presentation] From e764d5faab498f75a10b1fa6b4b89a1bfc908c79 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Mon, 20 Apr 2020 10:52:57 +0200 Subject: [PATCH 14/19] topotest: add bgp flowspec ipv4/ipv6 test this test ensures that an incoming bgp ipv4 and ipv6 flowspec entry is received with a nexthop IP associated. Signed-off-by: Philippe Guibert --- tests/topotests/bgp_flowspec/__init__.py | 0 tests/topotests/bgp_flowspec/exabgp.env | 54 +++++ tests/topotests/bgp_flowspec/peer1/exabgp.cfg | 32 +++ tests/topotests/bgp_flowspec/r1/bgpd.conf | 18 ++ tests/topotests/bgp_flowspec/r1/summary.txt | 53 +++++ tests/topotests/bgp_flowspec/r1/zebra.conf | 8 + .../bgp_flowspec/test_bgp_flowspec_topo.py | 208 ++++++++++++++++++ 7 files changed, 373 insertions(+) create mode 100644 tests/topotests/bgp_flowspec/__init__.py create mode 100644 tests/topotests/bgp_flowspec/exabgp.env create mode 100644 tests/topotests/bgp_flowspec/peer1/exabgp.cfg create mode 100644 tests/topotests/bgp_flowspec/r1/bgpd.conf create mode 100644 tests/topotests/bgp_flowspec/r1/summary.txt create mode 100644 tests/topotests/bgp_flowspec/r1/zebra.conf create mode 100755 tests/topotests/bgp_flowspec/test_bgp_flowspec_topo.py diff --git a/tests/topotests/bgp_flowspec/__init__.py b/tests/topotests/bgp_flowspec/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/bgp_flowspec/exabgp.env b/tests/topotests/bgp_flowspec/exabgp.env new file mode 100644 index 0000000000..a328e04962 --- /dev/null +++ b/tests/topotests/bgp_flowspec/exabgp.env @@ -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 diff --git a/tests/topotests/bgp_flowspec/peer1/exabgp.cfg b/tests/topotests/bgp_flowspec/peer1/exabgp.cfg new file mode 100644 index 0000000000..cd1fae5aba --- /dev/null +++ b/tests/topotests/bgp_flowspec/peer1/exabgp.cfg @@ -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 +} +} diff --git a/tests/topotests/bgp_flowspec/r1/bgpd.conf b/tests/topotests/bgp_flowspec/r1/bgpd.conf new file mode 100644 index 0000000000..6dc91b24a9 --- /dev/null +++ b/tests/topotests/bgp_flowspec/r1/bgpd.conf @@ -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 + ! +! diff --git a/tests/topotests/bgp_flowspec/r1/summary.txt b/tests/topotests/bgp_flowspec/r1/summary.txt new file mode 100644 index 0000000000..d5dfe0b6c2 --- /dev/null +++ b/tests/topotests/bgp_flowspec/r1/summary.txt @@ -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 +} +} diff --git a/tests/topotests/bgp_flowspec/r1/zebra.conf b/tests/topotests/bgp_flowspec/r1/zebra.conf new file mode 100644 index 0000000000..e4d5a21194 --- /dev/null +++ b/tests/topotests/bgp_flowspec/r1/zebra.conf @@ -0,0 +1,8 @@ +! +hostname r1 +password zebra +interface r1-eth0 + ip address 10.0.1.1/24 + ipv6 address 1001::1/112 +! + diff --git a/tests/topotests/bgp_flowspec/test_bgp_flowspec_topo.py b/tests/topotests/bgp_flowspec/test_bgp_flowspec_topo.py new file mode 100755 index 0000000000..a7e2c31cde --- /dev/null +++ b/tests/topotests/bgp_flowspec/test_bgp_flowspec_topo.py @@ -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) From c6423c31530db0e54ad90fc330c13bca81c9a80a Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Mon, 20 Apr 2020 18:02:46 +0200 Subject: [PATCH 15/19] bgp, zebra: add some alignments with remarks from community align the code to remarks from community. Signed-off-by: Philippe Guibert --- bgpd/bgp_attr.c | 7 +++---- bgpd/bgp_ecommunity.c | 1 + bgpd/bgp_ecommunity.h | 4 ++-- bgpd/bgp_flowspec_vty.c | 19 ++++++++++--------- bgpd/bgp_vty.c | 35 +++++++++++++++++------------------ zebra/zebra_pbr.c | 4 ++-- 6 files changed, 35 insertions(+), 35 deletions(-) diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index a77d9467cb..cac3ab1ca7 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -2296,13 +2296,12 @@ bgp_attr_ipv6_ext_communities(struct bgp_attr_parser_args *args) if (length == 0) { attr->ipv6_ecommunity = NULL; - /* Empty extcomm doesn't seem to be invalid per se */ - return BGP_ATTR_PARSE_PROCEED; + return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_OPT_ATTR_ERR, + args->total); } attr->ipv6_ecommunity = - ecommunity_parse_ipv6((uint8_t *)stream_pnt(peer->curr), - length); + ecommunity_parse_ipv6(stream_pnt(peer->curr), length); /* XXX: fix ecommunity_parse to use stream API */ stream_forward_getp(peer->curr, length); diff --git a/bgpd/bgp_ecommunity.c b/bgpd/bgp_ecommunity.c index f45fa2bed5..ce67abd364 100644 --- a/bgpd/bgp_ecommunity.c +++ b/bgpd/bgp_ecommunity.c @@ -792,6 +792,7 @@ static int ecommunity_rt_soo_str_internal(char *buf, size_t bufsz, 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; diff --git a/bgpd/bgp_ecommunity.h b/bgpd/bgp_ecommunity.h index 4a1af0d203..fe90efe1f7 100644 --- a/bgpd/bgp_ecommunity.h +++ b/bgpd/bgp_ecommunity.h @@ -94,7 +94,7 @@ /* Extended Communities value is eight octet long. */ #define ECOMMUNITY_SIZE 8 -#define IPV6_ECOMMUNITY_SIZE 20 +#define IPV6_ECOMMUNITY_SIZE 20 /* Extended Communities type flag. */ #define ECOMMUNITY_FLAG_NON_TRANSITIVE 0x40 @@ -107,7 +107,7 @@ struct ecommunity { /* Size of Each Unit of Extended Communities attribute. * to differentiate between IPv6 ext comm and ext comm */ - int unit_size; + uint8_t unit_size; /* Size of Extended Communities attribute. */ int size; diff --git a/bgpd/bgp_flowspec_vty.c b/bgpd/bgp_flowspec_vty.c index cc6d1ed457..92bec6f88a 100644 --- a/bgpd/bgp_flowspec_vty.c +++ b/bgpd/bgp_flowspec_vty.c @@ -263,7 +263,7 @@ void route_vty_out_flowspec(struct vty *vty, const struct prefix *p, { struct attr *attr; char return_string[BGP_FLOWSPEC_STRING_DISPLAY_MAX]; - char *s = NULL, *s2 = NULL; + char *s1 = NULL, *s2 = NULL; json_object *json_nlri_path = NULL; json_object *json_ecom_path = NULL; json_object *json_time_path = NULL; @@ -311,23 +311,23 @@ void route_vty_out_flowspec(struct vty *vty, const struct prefix *p, /* Print attribute */ attr = path->attr; if (attr->ecommunity) - s = ecommunity_ecom2str(attr->ecommunity, + s1 = ecommunity_ecom2str(attr->ecommunity, ECOMMUNITY_FORMAT_ROUTE_MAP, 0); if (attr->ipv6_ecommunity) s2 = ecommunity_ecom2str(attr->ipv6_ecommunity, ECOMMUNITY_FORMAT_ROUTE_MAP, 0); - if (!s && !s2) + if (!s1 && !s2) return; if (display == NLRI_STRING_FORMAT_LARGE) - vty_out(vty, "\t%s%s%s\n", s ? s : "", - s2 && s ? " " : "", s2 ? s2 : ""); + vty_out(vty, "\t%s%s%s\n", s1 ? s1 : "", + s2 && s1 ? " " : "", s2 ? s2 : ""); else if (display == NLRI_STRING_FORMAT_MIN) - vty_out(vty, "%s%s", s ? s : "", s2 ? s2 : ""); + vty_out(vty, "%s%s", s1 ? s1 : "", s2 ? s2 : ""); else if (json_paths) { json_ecom_path = json_object_new_object(); - if (s) + if (s1) json_object_string_add(json_ecom_path, - "ecomlist", s); + "ecomlist", s1); if (s2) json_object_string_add(json_ecom_path, "ecom6list", s2); @@ -357,7 +357,8 @@ void route_vty_out_flowspec(struct vty *vty, const struct prefix *p, vty_out(vty, "\tNLRI NH %s\n", local_buff); } - XFREE(MTYPE_ECOMMUNITY_STR, s); + XFREE(MTYPE_ECOMMUNITY_STR, s1); + XFREE(MTYPE_ECOMMUNITY_STR, s2); } peer_uptime(path->uptime, timebuf, BGP_UPTIME_LEN, 0, NULL); if (display == NLRI_STRING_FORMAT_LARGE) { diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 191c1a561c..1201b488fd 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -7332,7 +7332,7 @@ DEFPY( } static int set_ecom_list(struct vty *vty, int argc, struct cmd_token **argv, - struct ecommunity **list, int is_rt6) + struct ecommunity **list, bool is_rt6) { struct ecommunity *ecom = NULL; struct ecommunity *ecomadd; @@ -7425,10 +7425,10 @@ DEFPY (af_rd_vpn_export, int ret; afi_t afi; int idx = 0; - int yes = 1; + bool yes = true; if (argv_find(argv, argc, "no", &idx)) - yes = 0; + yes = false; if (yes) { ret = str2prefix_rd(rd_str, &prd); @@ -7486,10 +7486,10 @@ DEFPY (af_label_vpn_export, mpls_label_t label = MPLS_LABEL_NONE; afi_t afi; int idx = 0; - int yes = 1; + bool yes = true; if (argv_find(argv, argc, "no", &idx)) - yes = 0; + yes = false; /* If "no ...", squash trailing parameter */ if (!yes) @@ -7581,7 +7581,6 @@ DEFPY (af_nexthop_vpn_export, vty_out(vty, "%% Nexthop required\n"); return CMD_WARNING_CONFIG_FAILED; } - if (!sockunion2hostprefix(nexthop_su, &p)) return CMD_WARNING_CONFIG_FAILED; } @@ -7647,10 +7646,10 @@ DEFPY (af_rt_vpn_imexport, vpn_policy_direction_t dir; afi_t afi; int idx = 0; - int yes = 1; + bool yes = true; if (argv_find(argv, argc, "no", &idx)) - yes = 0; + yes = false; afi = vpn_policy_getafi(vty, bgp, false); if (afi == AFI_MAX) @@ -7665,7 +7664,7 @@ DEFPY (af_rt_vpn_imexport, vty_out(vty, "%% Missing RTLIST\n"); return CMD_WARNING_CONFIG_FAILED; } - ret = set_ecom_list(vty, argc - idx, argv + idx, &ecom, 0); + ret = set_ecom_list(vty, argc - idx, argv + idx, &ecom, false); if (ret != CMD_SUCCESS) { return ret; } @@ -7727,10 +7726,10 @@ DEFPY (af_route_map_vpn_imexport, vpn_policy_direction_t dir; afi_t afi; int idx = 0; - int yes = 1; + bool yes = true; if (argv_find(argv, argc, "no", &idx)) - yes = 0; + yes = false; afi = vpn_policy_getafi(vty, bgp, false); if (afi == AFI_MAX) @@ -7969,12 +7968,12 @@ DEFPY (bgp_imexport_vpn, afi_t afi; safi_t safi; int idx = 0; - int yes = 1; + bool yes = true; int flag; vpn_policy_direction_t dir; if (argv_find(argv, argc, "no", &idx)) - yes = 0; + yes = false; if (BGP_INSTANCE_TYPE_VRF != bgp->inst_type && BGP_INSTANCE_TYPE_DEFAULT != bgp->inst_type) { @@ -8037,15 +8036,15 @@ DEFPY (af_routetarget_import, struct ecommunity *ecom = NULL; afi_t afi; int idx = 0, idx_unused = 0; - int yes = 1; - int rt6 = 0; + bool yes = true; + bool rt6 = false; if (argv_find(argv, argc, "no", &idx)) - yes = 0; + yes = false; if (argv_find(argv, argc, "rt6", &idx_unused) || argv_find(argv, argc, "route-target6", &idx_unused)) - rt6 = 1; + rt6 = true; afi = vpn_policy_getafi(vty, bgp, false); if (afi == AFI_MAX) @@ -14594,7 +14593,7 @@ static void bgp_vpn_policy_config_write_afi(struct vty *vty, struct bgp *bgp, if (bgp->vpn_policy[afi].import_redirect_rtlist->unit_size != ECOMMUNITY_SIZE) - vty_out(vty, "%*srt redirect import %s\n", + vty_out(vty, "%*srt6 redirect import %s\n", indent, "", b); else vty_out(vty, "%*srt redirect import %s\n", diff --git a/zebra/zebra_pbr.c b/zebra/zebra_pbr.c index 1bed0b48bb..95d241c59f 100644 --- a/zebra/zebra_pbr.c +++ b/zebra/zebra_pbr.c @@ -1051,7 +1051,7 @@ static int zebra_pbr_show_ipset_walkcb(struct hash_bucket *bucket, void *arg) vty_out(vty, "IPset %s type %s family %s\n", zpi->ipset_name, zebra_pbr_ipset_type2str(zpi->type), - zpi->family == AF_INET ? "AF_INET" : "AF_INET6"); + family2str(zpi->family)); unique.vty = vty; unique.zpi = zpi; unique.zns = zns; @@ -1098,7 +1098,7 @@ void zebra_pbr_show_ipset_list(struct vty *vty, char *ipsetname) } vty_out(vty, "IPset %s type %s family %s\n", ipsetname, zebra_pbr_ipset_type2str(zpi->type), - zpi->family == AF_INET ? "AF_INET" : "AF_INET6"); + family2str(zpi->family)); unique.vty = vty; unique.zpi = zpi; unique.zns = zns; From a973d4c440b36bcd53254c633d65be84b83790a2 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Tue, 16 Jun 2020 08:42:10 +0200 Subject: [PATCH 16/19] bgpd: remove sprintf() usage on flowspec flowspec is being removed from remaining sprintf() calls. Signed-off-by: Philippe Guibert --- bgpd/bgp_pbr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bgpd/bgp_pbr.c b/bgpd/bgp_pbr.c index 32b57aa96d..0c36f138f1 100644 --- a/bgpd/bgp_pbr.c +++ b/bgpd/bgp_pbr.c @@ -2474,7 +2474,7 @@ static void bgp_pbr_policyroute_add_to_zebra_unit(struct bgp *bgp, if (bpm->unique == 0) { bpm->unique = ++bgp_pbr_match_counter_unique; /* 0 value is forbidden */ - sprintf(bpm->ipset_name, "match%p", bpm); + snprintf(bpm->ipset_name, sizeof(bpm->ipset_name), "match%p", bpm); bpm->entry_hash = hash_create_size(8, bgp_pbr_match_entry_hash_key, bgp_pbr_match_entry_hash_equal, From 7659ad686a5605ea3cdaab23690eb63a99f69c1b Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Wed, 17 Jun 2020 15:10:37 +0200 Subject: [PATCH 17/19] bgpd: do not forget to set the size of community val length because ecommunity structure can host both ext community and ipv6 ext community, do not forget to set the unit_size field. Signed-off-by: Philippe Guibert --- bgpd/bgp_evpn_mh.c | 1 + bgpd/bgp_routemap.c | 1 + 2 files changed, 2 insertions(+) diff --git a/bgpd/bgp_evpn_mh.c b/bgpd/bgp_evpn_mh.c index 8dfb7a305e..934c22d168 100644 --- a/bgpd/bgp_evpn_mh.c +++ b/bgpd/bgp_evpn_mh.c @@ -822,6 +822,7 @@ static void bgp_evpn_type1_evi_route_extcomm_build(struct bgp_evpn_es *es, memset(&ecom_encap, 0, sizeof(ecom_encap)); encode_encap_extcomm(tnl_type, &eval); ecom_encap.size = 1; + ecom_encap.unit_size = ECOMMUNITY_SIZE; ecom_encap.val = (uint8_t *)eval.val; attr->ecommunity = ecommunity_dup(&ecom_encap); diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index 5644daba62..6d81cfaab4 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -2606,6 +2606,7 @@ route_set_ecommunity_lb(void *rule, const struct prefix *prefix, ecommunity_free(&old_ecom); } else { ecom_lb.size = 1; + ecom_lb.unit_size = ECOMMUNITY_SIZE; ecom_lb.val = (uint8_t *)lb_eval.val; new_ecom = ecommunity_dup(&ecom_lb); } From 4371bf9110261f81edfd457a33f86cb0ce12ada7 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Thu, 2 Jul 2020 08:14:41 +0200 Subject: [PATCH 18/19] bgpd: remove warnings related to line too longs in bgp code remove warnings related to line too long in bgp code. Signed-off-by: Philippe Guibert --- bgpd/bgp_ecommunity.c | 113 ++++++++++++++++++++++++------------------ bgpd/bgp_evpn.c | 9 ++-- bgpd/bgp_pbr.c | 25 ++++++---- bgpd/bgp_route.c | 3 +- 4 files changed, 88 insertions(+), 62 deletions(-) diff --git a/bgpd/bgp_ecommunity.c b/bgpd/bgp_ecommunity.c index ce67abd364..79fb7e55e9 100644 --- a/bgpd/bgp_ecommunity.c +++ b/bgpd/bgp_ecommunity.c @@ -88,12 +88,15 @@ static void ecommunity_hash_free(struct ecommunity *ecom) once and whether the new value should replace what is existing or not. */ -static bool ecommunity_add_val_internal(struct ecommunity *ecom, const void *eval, - bool unique, bool overwrite, uint8_t ecom_size) +static bool ecommunity_add_val_internal(struct ecommunity *ecom, + const void *eval, + bool unique, bool overwrite, + uint8_t ecom_size) { int c, ins_idx; const struct ecommunity_val *eval4 = (struct ecommunity_val *)eval; - const struct ecommunity_val_ipv6 *eval6 = (struct ecommunity_val_ipv6 *)eval; + const struct ecommunity_val_ipv6 *eval6 = + (struct ecommunity_val_ipv6 *)eval; /* When this is fist value, just add it. */ if (ecom->val == NULL) { @@ -116,7 +119,8 @@ static bool ecommunity_add_val_internal(struct ecommunity *ecom, const void *eva if (p[0] == eval4->val[0] && p[1] == eval4->val[1]) { if (overwrite) { - memcpy(p, eval4->val, ecom_size); + memcpy(p, eval4->val, + ecom_size); return true; } return false; @@ -125,7 +129,8 @@ static bool ecommunity_add_val_internal(struct ecommunity *ecom, const void *eva if (p[0] == eval6->val[0] && p[1] == eval6->val[1]) { if (overwrite) { - memcpy(p, eval6->val, ecom_size); + memcpy(p, eval6->val, + ecom_size); return true; } return false; @@ -162,10 +167,11 @@ static bool ecommunity_add_val_internal(struct ecommunity *ecom, const void *eva } /* Add a new Extended Communities value to Extended Communities - Attribute structure. When the value is already exists in the - structure, we don't add the value. Newly added value is sorted by - numerical order. When the value is added to the structure return 1 - else return 0. */ + * Attribute structure. When the value is already exists in the + * structure, we don't add the value. Newly added value is sorted by + * numerical order. When the value is added to the structure return 1 + * else return 0. + */ bool ecommunity_add_val(struct ecommunity *ecom, struct ecommunity_val *eval, bool unique, bool overwrite) { @@ -173,8 +179,9 @@ bool ecommunity_add_val(struct ecommunity *ecom, struct ecommunity_val *eval, overwrite, ECOMMUNITY_SIZE); } -bool ecommunity_add_val_ipv6(struct ecommunity *ecom, struct ecommunity_val_ipv6 *eval, - bool unique, bool overwrite) +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); @@ -202,8 +209,9 @@ ecommunity_uniq_sort_internal(struct ecommunity *ecom, } /* This function takes pointer to Extended Communites strucutre then - create a new Extended Communities structure by uniq and sort each - Extended Communities value. */ + * 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); @@ -678,7 +686,8 @@ static struct ecommunity *ecommunity_str2com_internal(const char *str, int type, if (ecom == NULL) ecom = ecommunity_new(); eval.val[1] = type; - ecommunity_add_val_internal(ecom, (void *)&eval, false, false, + ecommunity_add_val_internal(ecom, (void *)&eval, + false, false, ecom->unit_size); break; case ecommunity_token_val6: @@ -707,31 +716,31 @@ static struct ecommunity *ecommunity_str2com_internal(const char *str, int type, } /* Convert string to extended community attribute. - - When type is already known, please specify both str and type. str - should not include keyword such as "rt" and "soo". Type is - ECOMMUNITY_ROUTE_TARGET or ECOMMUNITY_SITE_ORIGIN. - keyword_included should be zero. - - For example route-map's "set extcommunity" command case: - - "rt 100:1 100:2 100:3" -> str = "100:1 100:2 100:3" - type = ECOMMUNITY_ROUTE_TARGET - keyword_included = 0 - - "soo 100:1" -> str = "100:1" - type = ECOMMUNITY_SITE_ORIGIN - keyword_included = 0 - - When string includes keyword for each extended community value. - Please specify keyword_included as non-zero value. - - For example standard extcommunity-list case: - - "rt 100:1 rt 100:2 soo 100:1" -> str = "rt 100:1 rt 100:2 soo 100:1" - type = 0 - keyword_include = 1 -*/ + * + * 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) { @@ -789,7 +798,8 @@ static int ecommunity_rt_soo_str_internal(char *buf, size_t bufsz, eas.as |= (*pnt++); pnt = ptr_get_be32(pnt, &eas.val); - len = snprintf(buf, bufsz, "%s%u:%u", prefix, eas.as, eas.val); + len = snprintf(buf, bufsz, "%s%u:%u", prefix, eas.as, + eas.val); } else { /* this is an IPv6 ext community * first 16 bytes stands for IPv6 addres @@ -799,7 +809,8 @@ static int ecommunity_rt_soo_str_internal(char *buf, size_t bufsz, eip6.val = (*pnt++ << 8); eip6.val |= (*pnt++); - inet_ntop(AF_INET6, &eip6.ip, buf_local, sizeof(buf_local)); + inet_ntop(AF_INET6, &eip6.ip, buf_local, + sizeof(buf_local)); len = snprintf(buf, bufsz, "%s%s:%u", prefix, buf_local, eip6.val); } @@ -1052,7 +1063,8 @@ char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter) format, ecom->unit_size); snprintf(encbuf, sizeof(encbuf), "%s", buf); - } else if (sub_type == ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6) { + } else if (sub_type == + ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6) { char buf[64]; memset(buf, 0, sizeof(buf)); @@ -1063,19 +1075,22 @@ char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter) ECOMMUNITY_ROUTE_TARGET, ECOMMUNITY_FORMAT_DISPLAY, ecom->unit_size); - snprintf(encbuf, sizeof(encbuf), "FS:redirect VRF %s", buf); + snprintf(encbuf, sizeof(encbuf), + "FS:redirect VRF %s", buf); } else if (sub_type == ECOMMUNITY_REDIRECT_VRF) { char buf[16]; memset(buf, 0, sizeof(buf)); - ecommunity_rt_soo_str(buf, sizeof(buf), (const uint8_t *)pnt, - type & - ~ECOMMUNITY_ENCODE_TRANS_EXP, - ECOMMUNITY_ROUTE_TARGET, - ECOMMUNITY_FORMAT_DISPLAY); + ecommunity_rt_soo_str(buf, sizeof(buf), + (const uint8_t *)pnt, + type & + ~ECOMMUNITY_ENCODE_TRANS_EXP, + ECOMMUNITY_ROUTE_TARGET, + ECOMMUNITY_FORMAT_DISPLAY); + snprintf(encbuf, sizeof(encbuf), + "FS:redirect VRF %s", buf); snprintf(encbuf, sizeof(encbuf), "FS:redirect VRF %s", buf); - snprintf(encbuf, sizeof(encbuf), "FS:redirect VRF %s", buf); } else if (type != ECOMMUNITY_ENCODE_TRANS_EXP) unk_ecom = 1; else if (sub_type == ECOMMUNITY_TRAFFIC_ACTION) { diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index e552866f38..8c3e54566e 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -876,7 +876,8 @@ static void add_mac_mobility_to_attr(uint32_t seq_num, struct attr *attr) if (attr->ecommunity) { for (i = 0; i < attr->ecommunity->size; i++) { - pnt = attr->ecommunity->val + (i * attr->ecommunity->unit_size); + pnt = attr->ecommunity->val + + (i * attr->ecommunity->unit_size); type = *pnt++; sub_type = *pnt++; @@ -884,7 +885,8 @@ static void add_mac_mobility_to_attr(uint32_t seq_num, struct attr *attr) && sub_type == ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY) { ecom_val_ptr = - (attr->ecommunity->val + (i * attr->ecommunity->unit_size)); + (attr->ecommunity->val + + (i * attr->ecommunity->unit_size)); break; } } @@ -892,7 +894,8 @@ static void add_mac_mobility_to_attr(uint32_t seq_num, struct attr *attr) /* Update the existing MM ecommunity */ if (ecom_val_ptr) { - memcpy(ecom_val_ptr, eval.val, sizeof(char) * attr->ecommunity->unit_size); + memcpy(ecom_val_ptr, eval.val, sizeof(char) + * attr->ecommunity->unit_size); } /* Add MM to existing */ else { diff --git a/bgpd/bgp_pbr.c b/bgpd/bgp_pbr.c index 0c36f138f1..f6e5c196ca 100644 --- a/bgpd/bgp_pbr.c +++ b/bgpd/bgp_pbr.c @@ -172,7 +172,8 @@ static int bgp_pbr_match_walkcb(struct hash_bucket *bucket, void *arg) return HASHWALK_CONTINUE; } -static int snprintf_bgp_pbr_match_val(char *str, int len, 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) { char *ptr = str; @@ -852,14 +853,16 @@ int bgp_pbr_build_and_validate_entry(const struct prefix *p, api_action_redirect_ip->u.zr.duplicate = ecom_eval->val[7]; continue; - } else if (p->u.prefix_flowspec.family == AF_INET) { + } else if (p->u.prefix_flowspec.family == + AF_INET) { api_action->action = ACTION_REDIRECT_IP; api_action->u.zr.redirect_ip_v4.s_addr = path->attr->nexthop.s_addr; api_action->u.zr.duplicate = ecom_eval->val[7]; api_action_redirect_ip = api_action; - } else if (p->u.prefix_flowspec.family == AF_INET6) { + } else if (p->u.prefix_flowspec.family == + AF_INET6) { api_action->action = ACTION_REDIRECT_IP; memcpy(&api_action->u .zr.redirect_ip_v6, @@ -903,8 +906,8 @@ int bgp_pbr_build_and_validate_entry(const struct prefix *p, afi); if (ret != 0) continue; - if ((api_action->action == ACTION_TRAFFICRATE) && - api->actions[i].u.r.rate == 0) + if ((api_action->action == ACTION_TRAFFICRATE) + && api->actions[i].u.r.rate == 0) discard_action_found = true; } api->action_num++; @@ -1465,7 +1468,8 @@ void bgp_pbr_print_policy_route(struct bgp_pbr_entry_main *api) prefix2str(p, buff, 64), api->dst_prefix_offset); else - delta = snprintf(ptr, len, "@dst %s", prefix2str(p, buff, 64)); + delta = snprintf(ptr, len, "@dst %s", + prefix2str(p, buff, 64)); len -= delta; ptr += delta; } @@ -1527,7 +1531,8 @@ void bgp_pbr_print_policy_route(struct bgp_pbr_entry_main *api) if (api->match_packet_length_num) INCREMENT_DISPLAY(ptr, nb_items, len); for (i = 0; i < api->match_packet_length_num; i++) { - delta = snprintf_bgp_pbr_match_val(ptr, len, &api->packet_length[i], + delta = snprintf_bgp_pbr_match_val(ptr, len, + &api->packet_length[i], i > 0 ? NULL : "@plen "); len -= delta; ptr += delta; @@ -1545,7 +1550,8 @@ void bgp_pbr_print_policy_route(struct bgp_pbr_entry_main *api) 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], + delta = snprintf_bgp_pbr_match_val(ptr, len, + &api->flow_label[i], i > 0 ? NULL : "@flowlabel "); len -= delta; ptr += delta; @@ -2474,7 +2480,8 @@ static void bgp_pbr_policyroute_add_to_zebra_unit(struct bgp *bgp, if (bpm->unique == 0) { bpm->unique = ++bgp_pbr_match_counter_unique; /* 0 value is forbidden */ - snprintf(bpm->ipset_name, sizeof(bpm->ipset_name), "match%p", bpm); + snprintf(bpm->ipset_name, sizeof(bpm->ipset_name), + "match%p", bpm); bpm->entry_hash = hash_create_size(8, bgp_pbr_match_entry_hash_key, bgp_pbr_match_entry_hash_equal, diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index eefff4bef8..e8baac2c32 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -10014,7 +10014,8 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi, dest_p->u.prefix_flowspec.ptr, dest_p->u.prefix_flowspec.prefixlen, retstr, NLRI_STRING_FORMAT_MIN, NULL, - family2afi(dest_p->u.prefix_flowspec.family)); + family2afi(dest_p->u + .prefix_flowspec.family)); if (first) vty_out(vty, "\"%s/%d\": ", retstr, dest_p->u.prefix_flowspec From c24ceb896e9b93abd8a3d03ded9a703324c56a11 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Fri, 21 Aug 2020 08:42:20 +0200 Subject: [PATCH 19/19] bgpd: fix Dereference of null pointer in flowspec a dereference of null pointer exists in current flowspec code, with prefix pointer. check validity of pointer before going ahead. Signed-off-by: Philippe Guibert --- bgpd/bgp_flowspec_util.c | 10 +++++--- bgpd/bgp_flowspec_vty.c | 55 +++++++++++++++++++--------------------- 2 files changed, 32 insertions(+), 33 deletions(-) diff --git a/bgpd/bgp_flowspec_util.c b/bgpd/bgp_flowspec_util.c index 0115d7af1e..90e9236385 100644 --- a/bgpd/bgp_flowspec_util.c +++ b/bgpd/bgp_flowspec_util.c @@ -228,10 +228,12 @@ int bgp_flowspec_ip_address(enum bgp_flowspec_util_nlri_t type, BGP_FLOWSPEC_STRING_DISPLAY_MAX); break; case BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE: - if (prefix_local.family == AF_INET) - PREFIX_COPY_IPV4(prefix, &prefix_local) - else - PREFIX_COPY_IPV6(prefix, &prefix_local) + if (prefix) { + if (prefix_local.family == AF_INET) + PREFIX_COPY_IPV4(prefix, &prefix_local) + else + PREFIX_COPY_IPV6(prefix, &prefix_local) + } break; case BGP_FLOWSPEC_VALIDATE_ONLY: default: diff --git a/bgpd/bgp_flowspec_vty.c b/bgpd/bgp_flowspec_vty.c index 92bec6f88a..798bce8219 100644 --- a/bgpd/bgp_flowspec_vty.c +++ b/bgpd/bgp_flowspec_vty.c @@ -274,36 +274,33 @@ void route_vty_out_flowspec(struct vty *vty, const struct prefix *p, dest = path->net; if (dest) bgp_dest_get_bgp_table_info(dest); - /* Print prefix */ - if (p != NULL) { - if (p->family != AF_FLOWSPEC) - return; - if (json_paths) { - if (display == NLRI_STRING_FORMAT_JSON) - json_nlri_path = json_object_new_object(); - else - json_nlri_path = json_paths; - } - if (display == NLRI_STRING_FORMAT_LARGE && path) - vty_out(vty, "BGP flowspec entry: (flags 0x%x)\n", - path->flags); - bgp_fs_nlri_get_string((unsigned char *) - p->u.prefix_flowspec.ptr, - p->u.prefix_flowspec.prefixlen, - return_string, - display, - json_nlri_path, - family2afi(p->u.prefix_flowspec - .family)); - if (display == NLRI_STRING_FORMAT_LARGE) - vty_out(vty, "%s", return_string); - else if (display == NLRI_STRING_FORMAT_DEBUG) - vty_out(vty, "%s", return_string); - else if (display == NLRI_STRING_FORMAT_MIN) - vty_out(vty, " %-30s", return_string); - else if (json_paths && display == NLRI_STRING_FORMAT_JSON) - json_object_array_add(json_paths, json_nlri_path); + if (p == NULL || p->family != AF_FLOWSPEC) + return; + if (json_paths) { + if (display == NLRI_STRING_FORMAT_JSON) + json_nlri_path = json_object_new_object(); + else + json_nlri_path = json_paths; } + if (display == NLRI_STRING_FORMAT_LARGE && path) + vty_out(vty, "BGP flowspec entry: (flags 0x%x)\n", + path->flags); + bgp_fs_nlri_get_string((unsigned char *) + p->u.prefix_flowspec.ptr, + p->u.prefix_flowspec.prefixlen, + return_string, + display, + json_nlri_path, + family2afi(p->u.prefix_flowspec + .family)); + if (display == NLRI_STRING_FORMAT_LARGE) + vty_out(vty, "%s", return_string); + else if (display == NLRI_STRING_FORMAT_DEBUG) + vty_out(vty, "%s", return_string); + else if (display == NLRI_STRING_FORMAT_MIN) + vty_out(vty, " %-30s", return_string); + else if (json_paths && display == NLRI_STRING_FORMAT_JSON) + json_object_array_add(json_paths, json_nlri_path); if (!path) return; if (path->attr &&