bgpd: aspath list format binds on as-notation format

Each BGP prefix may have an as-path list attached. A forged
string is stored in the BGP attribute and shows the as-path
list output.

Before this commit, the as-path list output was expressed as
a list of AS values in plain format. Now, if a given BGP instance
uses a specific asnotation, then the output is changed:

new output:
router bgp 1.1 asnotation dot
!
 address-family ipv4 unicast
  network 10.200.0.0/24 route-map rmap
  network 10.201.0.0/24 route-map rmap
  redistribute connected route-map rmap
 exit-address-family
exit
!
route-map rmap permit 1
 set as-path prepend 1.1 5433.55 264564564
exit

ubuntu2004# do show bgp ipv4
BGP table version is 2, local router ID is 10.0.2.15, vrf id 0
Default local pref 100, local AS 1.1
Status codes:  s suppressed, d damped, h history, * valid, > best, = multipath,
               i internal, r RIB-failure, S Stale, R Removed
Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self
Origin codes:  i - IGP, e - EGP, ? - incomplete
RPKI validation codes: V valid, I invalid, N Not found

    Network          Next Hop            Metric LocPrf Weight Path
 *> 4.4.4.4/32       0.0.0.0                  0         32768 1.1 5433.55 4036.61268 ?
 *> 10.0.2.0/24      0.0.0.0                  0         32768 1.1 5433.55 4036.61268 ?
    10.200.0.0/24    0.0.0.0                  0         32768 1.1 5433.55 4036.61268 i
    10.201.0.0/24    0.0.0.0                  0         32768 1.1 5433.55 4036.61268 i

The changes include:
- the aspath structure has a new field: asnotation type
The ashash list will differentiate 2 aspaths using a different
asnotation.
- 3 new printf extensions display the as number in the wished
format: pASP, pASD, pASE for plain, dot, or dot+ format (extended).

Signed-off-by: Philippe Guibert <philippe.guibert@6wind.com>
This commit is contained in:
Philippe Guibert 2022-11-14 15:56:40 +01:00
parent e55b088399
commit 17571c4ae7
13 changed files with 166 additions and 38 deletions

View File

@ -302,9 +302,13 @@ static struct assegment *assegment_normalise(struct assegment *head)
return head;
}
static struct aspath *aspath_new(void)
static struct aspath *aspath_new(enum asnotation_mode asnotation)
{
return XCALLOC(MTYPE_AS_PATH, sizeof(struct aspath));
struct aspath *as;
as = XCALLOC(MTYPE_AS_PATH, sizeof(struct aspath));
as->asnotation = asnotation;
return as;
}
/* Free AS path structure. */
@ -552,8 +556,10 @@ static void aspath_make_str_count(struct aspath *as, bool make_json)
*
* This was changed to 10 after the well-known BGP assertion, which
* had hit some parts of the Internet in May of 2009.
* plain format : '4294967295 ' : 10 + 1
* astod format : '65535.65535 ': 11 + 1
*/
#define ASN_STR_LEN (10 + 1)
#define ASN_STR_LEN (11 + 1)
str_size = MAX(assegment_count_asns(seg, 0) * ASN_STR_LEN + 2 + 1,
ASPATH_STR_DEFAULT_LEN);
str_buf = XMALLOC(MTYPE_AS_STR, str_size);
@ -584,7 +590,7 @@ static void aspath_make_str_count(struct aspath *as, bool make_json)
/* We might need to increase str_buf, particularly if path has
* differing segments types, our initial guesstimate above will
* have been wrong. Need 10 chars for ASN, a separator each and
* have been wrong. Need 11 chars for ASN, a separator each and
* potentially two segment delimiters, plus a space between each
* segment and trailing zero.
*
@ -610,12 +616,11 @@ static void aspath_make_str_count(struct aspath *as, bool make_json)
/* write out the ASNs, with their separators, bar the last one*/
for (i = 0; i < seg->length; i++) {
if (make_json)
json_object_array_add(
jseg_list,
json_object_new_int64(seg->as[i]));
len += snprintf(str_buf + len, str_size - len, "%u",
seg->as[i]);
asn_asn2json_array(jseg_list, seg->as[i],
as->asnotation);
len += snprintfrr(str_buf + len, str_size - len,
ASN_FORMAT(as->asnotation),
&seg->as[i]);
if (i < (seg->length - 1))
len += snprintf(str_buf + len, str_size - len,
@ -706,6 +711,7 @@ struct aspath *aspath_dup(struct aspath *aspath)
new->str = XMALLOC(MTYPE_AS_STR, buflen);
new->str_len = aspath->str_len;
new->asnotation = aspath->asnotation;
/* copy the string data */
if (aspath->str_len > 0)
@ -733,6 +739,7 @@ static void *aspath_hash_alloc(void *arg)
new->str = aspath->str;
new->str_len = aspath->str_len;
new->json = aspath->json;
new->asnotation = aspath->asnotation;
return new;
}
@ -840,7 +847,8 @@ static int assegments_parse(struct stream *s, size_t length,
On error NULL is returned.
*/
struct aspath *aspath_parse(struct stream *s, size_t length, int use32bit)
struct aspath *aspath_parse(struct stream *s, size_t length, int use32bit,
enum asnotation_mode asnotation)
{
struct aspath as;
struct aspath *find;
@ -855,6 +863,7 @@ struct aspath *aspath_parse(struct stream *s, size_t length, int use32bit)
return NULL;
memset(&as, 0, sizeof(as));
as.asnotation = asnotation;
if (assegments_parse(s, length, &as.segments, use32bit) < 0)
return NULL;
@ -1072,7 +1081,7 @@ struct aspath *aspath_aggregate(struct aspath *as1, struct aspath *as2)
seg = assegment_append_asns(seg, seg1->as, match);
if (!aspath) {
aspath = aspath_new();
aspath = aspath_new(as1->asnotation);
aspath->segments = seg;
} else
prevseg->next = seg;
@ -1092,7 +1101,7 @@ struct aspath *aspath_aggregate(struct aspath *as1, struct aspath *as2)
}
if (!aspath)
aspath = aspath_new();
aspath = aspath_new(as1->asnotation);
/* Make as-set using rest of all information. */
from = match;
@ -1536,7 +1545,7 @@ struct aspath *aspath_filter_exclude(struct aspath *source,
struct assegment *srcseg, *exclseg, *lastseg;
struct aspath *newpath;
newpath = aspath_new();
newpath = aspath_new(source->asnotation);
lastseg = NULL;
for (srcseg = source->segments; srcseg; srcseg = srcseg->next) {
@ -1766,7 +1775,7 @@ struct aspath *aspath_reconcile_as4(struct aspath *aspath,
newseg = assegment_append_asns(newseg, seg->as, cpasns);
if (!newpath) {
newpath = aspath_new();
newpath = aspath_new(aspath->asnotation);
newpath->segments = newseg;
} else
prevseg->next = newseg;
@ -1895,16 +1904,16 @@ static void aspath_segment_add(struct aspath *as, int type)
as->segments = new;
}
struct aspath *aspath_empty(void)
struct aspath *aspath_empty(enum asnotation_mode asnotation)
{
return aspath_parse(NULL, 0, 1); /* 32Bit ;-) */
return aspath_parse(NULL, 0, 1, asnotation); /* 32Bit ;-) */
}
struct aspath *aspath_empty_get(void)
{
struct aspath *aspath;
aspath = aspath_new();
aspath = aspath_new(bgp_get_asnotation(NULL));
aspath_make_str_count(aspath, false);
return aspath;
}
@ -1988,7 +1997,8 @@ static const char *aspath_gettoken(const char *buf, enum as_token *token,
return p;
}
struct aspath *aspath_str2aspath(const char *str)
struct aspath *aspath_str2aspath(const char *str,
enum asnotation_mode asnotation)
{
enum as_token token = as_token_unknown;
unsigned short as_type;
@ -1996,7 +2006,7 @@ struct aspath *aspath_str2aspath(const char *str)
struct aspath *aspath;
int needtype;
aspath = aspath_new();
aspath = aspath_new(asnotation);
/* We start default type as AS_SEQUENCE. */
as_type = AS_SEQUENCE;
@ -2070,6 +2080,10 @@ bool aspath_cmp(const void *arg1, const void *arg2)
const struct assegment *seg1 = ((const struct aspath *)arg1)->segments;
const struct assegment *seg2 = ((const struct aspath *)arg2)->segments;
if (((const struct aspath *)arg1)->asnotation !=
((const struct aspath *)arg2)->asnotation)
return false;
while (seg1 || seg2) {
int i;
if ((!seg1 && seg2) || (seg1 && !seg2))

View File

@ -72,6 +72,9 @@ struct aspath {
and AS path regular expression match. */
char *str;
unsigned short str_len;
/* AS notation used by string expression of AS path */
enum asnotation_mode asnotation;
};
#define ASPATH_STR_DEFAULT_LEN 32
@ -80,7 +83,9 @@ struct aspath {
extern void aspath_init(void);
extern void aspath_finish(void);
extern struct aspath *aspath_parse(struct stream *s, size_t length,
int use32bit);
int use32bit,
enum asnotation_mode asnotation);
extern struct aspath *aspath_dup(struct aspath *aspath);
extern struct aspath *aspath_aggregate(struct aspath *as1, struct aspath *as2);
extern struct aspath *aspath_prepend(struct aspath *as1, struct aspath *as2);
@ -96,9 +101,10 @@ extern bool aspath_cmp_left(const struct aspath *aspath1,
extern bool aspath_cmp_left_confed(const struct aspath *as1,
const struct aspath *as2);
extern struct aspath *aspath_delete_confed_seq(struct aspath *aspath);
extern struct aspath *aspath_empty(void);
extern struct aspath *aspath_empty(enum asnotation_mode asnotation);
extern struct aspath *aspath_empty_get(void);
extern struct aspath *aspath_str2aspath(const char *str);
extern struct aspath *aspath_str2aspath(const char *str,
enum asnotation_mode asnotation);
extern void aspath_str_update(struct aspath *as, bool make_json);
extern void aspath_free(struct aspath *aspath);
extern struct aspath *aspath_intern(struct aspath *aspath);

View File

@ -1069,7 +1069,7 @@ struct attr *bgp_attr_default_set(struct attr *attr, struct bgp *bgp,
attr->origin = origin;
attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_ORIGIN);
attr->aspath = aspath_empty();
attr->aspath = aspath_empty(bgp->asnotation);
attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_AS_PATH);
attr->weight = BGP_ATTR_DEFAULT_WEIGHT;
attr->tag = 0;
@ -1107,7 +1107,7 @@ struct attr *bgp_attr_aggregate_intern(
if (aspath)
attr.aspath = aspath_intern(aspath);
else
attr.aspath = aspath_empty();
attr.aspath = aspath_empty(bgp->asnotation);
attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_AS_PATH);
/* Next hop attribute. */
@ -1605,15 +1605,19 @@ static int bgp_attr_aspath(struct bgp_attr_parser_args *args)
struct attr *const attr = args->attr;
struct peer *const peer = args->peer;
const bgp_size_t length = args->length;
enum asnotation_mode asnotation;
asnotation = bgp_get_asnotation(
args->peer && args->peer->bgp ? args->peer->bgp : NULL);
/*
* peer with AS4 => will get 4Byte ASnums
* otherwise, will get 16 Bit
*/
attr->aspath = aspath_parse(
peer->curr, length,
CHECK_FLAG(peer->cap, PEER_CAP_AS4_RCV)
&& CHECK_FLAG(peer->cap, PEER_CAP_AS4_ADV));
attr->aspath =
aspath_parse(peer->curr, length,
CHECK_FLAG(peer->cap, PEER_CAP_AS4_RCV) &&
CHECK_FLAG(peer->cap, PEER_CAP_AS4_ADV),
asnotation);
/* In case of IBGP, length will be zero. */
if (!attr->aspath) {
@ -1705,8 +1709,11 @@ static int bgp_attr_as4_path(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;
enum asnotation_mode asnotation;
*as4_path = aspath_parse(peer->curr, length, 1);
asnotation = bgp_get_asnotation(peer->bgp);
*as4_path = aspath_parse(peer->curr, length, 1, asnotation);
/* In case of IBGP, length will be zero. */
if (!*as4_path) {

View File

@ -101,7 +101,8 @@ static void attr_parse(struct stream *s, uint16_t len)
case BGP_ATTR_AS_PATH: {
struct aspath *aspath;
aspath = aspath_parse(s, length, 1);
aspath = aspath_parse(s, length, 1,
bgp_get_asnotation(NULL));
printf("ASPATH: %s\n", aspath->str);
aspath_free(aspath);
} break;

View File

@ -7246,7 +7246,7 @@ static bool aggr_suppress_map_test(struct bgp *bgp,
return false;
/* Call route map matching and return result. */
attr.aspath = aspath_empty();
attr.aspath = aspath_empty(bgp->asnotation);
rmap_path.peer = bgp->peer_self;
rmap_path.attr = &attr;
@ -7340,9 +7340,12 @@ static bool bgp_aggregate_info_same(struct bgp_path_info *pi, uint8_t origin,
struct lcommunity *lcomm)
{
static struct aspath *ae = NULL;
enum asnotation_mode asnotation;
asnotation = bgp_get_asnotation(NULL);
if (!ae)
ae = aspath_empty();
ae = aspath_empty(asnotation);
if (!pi)
return false;

View File

@ -228,7 +228,7 @@ static void *route_aspath_compile(const char *arg)
{
struct aspath *aspath;
aspath = aspath_str2aspath(arg);
aspath = aspath_str2aspath(arg, bgp_get_asnotation(NULL));
if (!aspath)
return NULL;
return aspath;

View File

@ -163,7 +163,8 @@ void lua_decode_attr(lua_State *L, int idx, struct attr *attr)
attr->nh_ifindex = lua_tointeger(L, -1);
lua_pop(L, 1);
lua_getfield(L, idx, "aspath");
attr->aspath = aspath_str2aspath(lua_tostring(L, -1));
attr->aspath = aspath_str2aspath(lua_tostring(L, -1),
bgp_get_asnotation(NULL));
lua_pop(L, 1);
lua_getfield(L, idx, "localpref");
attr->local_pref = lua_tointeger(L, -1);

View File

@ -2060,6 +2060,13 @@ const char *bgp_get_name_by_role(uint8_t role)
return "unknown";
}
enum asnotation_mode bgp_get_asnotation(struct bgp *bgp)
{
if (!bgp)
return ASNOTATION_PLAIN;
return bgp->asnotation;
}
static void peer_group2peer_config_copy_af(struct peer_group *group,
struct peer *peer, afi_t afi,
safi_t safi)

View File

@ -2350,6 +2350,7 @@ extern void peer_tx_shutdown_message_unset(struct peer *);
extern void bgp_route_map_update_timer(struct thread *thread);
extern const char *bgp_get_name_by_role(uint8_t role);
extern enum asnotation_mode bgp_get_asnotation(struct bgp *bgp);
extern void bgp_route_map_terminate(void);

View File

@ -118,6 +118,15 @@ static bool asn_str2asn_internal(const char *asstring, as_t *asn,
return ret;
}
static void asn_asn2asdot(as_t asn, char *asstring, size_t len)
{
uint16_t low, high;
high = (asn >> 16) & 0xffff;
low = asn & 0xffff;
snprintf(asstring, len, "%hu.%hu", high, low);
}
bool asn_str2asn(const char *asstring, as_t *asn)
{
return asn_str2asn_internal(asstring, asn, NULL, NULL, NULL);
@ -173,3 +182,58 @@ const char *asn_mode2str(enum asnotation_mode asnotation)
return lookup_msg(asnotation_mode_msg, asnotation,
"Unrecognized AS notation mode");
}
void asn_asn2json_array(json_object *jseg_list, as_t asn,
enum asnotation_mode asnotation)
{
static char as_str[ASN_STRING_MAX_SIZE];
if ((asnotation == ASNOTATION_PLAIN) ||
((asnotation == ASNOTATION_DOT) && asn < UINT16_MAX))
json_object_array_add(jseg_list,
json_object_new_int64(asn));
else {
asn_asn2asdot(asn, as_str, sizeof(as_str));
json_array_string_add(jseg_list, as_str);
}
}
static ssize_t printfrr_asnotation(struct fbuf *buf, struct printfrr_eargs *ea,
const void *ptr,
enum asnotation_mode asnotation)
{
/* for alignemnt up to 33 chars - %33pASD for instance - */
char as_str[ASN_STRING_MAX_SIZE*3];
const as_t *asn;
if (!ptr)
return bputs(buf, "(null)");
asn = ptr;
if ((asnotation == ASNOTATION_PLAIN) ||
((asnotation == ASNOTATION_DOT) && *asn < UINT16_MAX))
snprintf(as_str, sizeof(as_str), "%u", *asn);
else
asn_asn2asdot(*asn, as_str, sizeof(as_str));
return bputs(buf, as_str);
}
printfrr_ext_autoreg_p("ASP", printfrr_asplain);
static ssize_t printfrr_asplain(struct fbuf *buf, struct printfrr_eargs *ea,
const void *ptr)
{
return printfrr_asnotation(buf, ea, ptr, ASNOTATION_PLAIN);
}
printfrr_ext_autoreg_p("ASD", printfrr_asdot);
static ssize_t printfrr_asdot(struct fbuf *buf, struct printfrr_eargs *ea,
const void *ptr)
{
return printfrr_asnotation(buf, ea, ptr, ASNOTATION_DOT);
}
printfrr_ext_autoreg_p("ASE", printfrr_asdotplus);
static ssize_t printfrr_asdotplus(struct fbuf *buf, struct printfrr_eargs *ea,
const void *ptr)
{
return printfrr_asnotation(buf, ea, ptr, ASNOTATION_DOTPLUS);
}

View File

@ -24,6 +24,7 @@
#include "zebra.h"
#include "command_match.h"
#include "json.h"
#ifdef __cplusplus
extern "C" {
@ -48,6 +49,20 @@ extern enum match_type asn_str2asn_match(const char *str);
extern bool asn_str2asn_notation(const char *asstring, as_t *asn,
enum asnotation_mode *asnotation);
extern const char *asn_mode2str(enum asnotation_mode asnotation);
void asn_asn2json_array(json_object *jseg_list, as_t asn,
enum asnotation_mode asnotation);
/* display AS in appropriate format */
#ifdef _FRR_ATTRIBUTE_PRINTFRR
#pragma FRR printfrr_ext "%pASP" (as_t *)
#pragma FRR printfrr_ext "%pASD" (as_t *)
#pragma FRR printfrr_ext "%pASE" (as_t *)
#endif
#define ASN_FORMAT(mode) \
((mode == ASNOTATION_DOT) ? "%pASD" : \
((mode == ASNOTATION_DOTPLUS) ? "%pASE" : \
"%pASP"))
/* for test */
extern void asn_relax_as_zero(bool relax);

View File

@ -880,7 +880,7 @@ static struct aspath *make_aspath(const uint8_t *data, size_t len, int use32bit)
s = stream_new(len);
stream_put(s, data, len);
}
as = aspath_parse(s, len, use32bit);
as = aspath_parse(s, len, use32bit, ASNOTATION_PLAIN);
if (s)
stream_free(s);
@ -925,7 +925,7 @@ static int validate(struct aspath *as, const struct test_spec *sp)
as4 = make_aspath(STREAM_DATA(s), bytes4, 1);
asn_relax_as_zero(true);
asstr = aspath_str2aspath(sp->shouldbe);
asstr = aspath_str2aspath(sp->shouldbe, ASNOTATION_PLAIN);
asn_relax_as_zero(false);
asconfeddel = aspath_delete_confed_seq(aspath_dup(asinout));
@ -1103,7 +1103,7 @@ static void empty_prepend_test(struct test_segment *t)
printf("empty prepend %s: %s\n", t->name, t->desc);
asp1 = make_aspath(t->asdata, t->len, 0);
asp2 = aspath_empty();
asp2 = aspath_empty(ASNOTATION_PLAIN);
ascratch = aspath_dup(asp2);
aspath_unintern(&asp2);

View File

@ -25,6 +25,7 @@
#include "lib/memory.h"
#include "lib/prefix.h"
#include "lib/nexthop.h"
#include "lib/asn.h"
static int errors;
@ -158,6 +159,7 @@ int main(int argc, char **argv)
struct in_addr ip;
char *p;
char buf[256];
as_t asn;
printcmp("%d %u %d %u", 123, 123, -456, -456);
printcmp("%lld %llu %lld %llu", 123LL, 123LL, -456LL, -456LL);
@ -405,6 +407,13 @@ int main(int argc, char **argv)
printchk("-00:09", "%pTSIm", &ts);
printchk("--:--", "%pTVImx", &tv);
printchk("--:--", "%pTTImx", &tt);
/* ASN checks */
asn = 65536;
printchk("1.0", "%pASD", &asn);
asn = 65400;
printchk("65400", "%pASP", &asn);
printchk("0.65400", "%pASE", &asn);
printchk("65400", "%pASD", &asn);
return !!errors;
}