]> git.puffer.fish Git - matthieu/frr.git/commitdiff
bgpd: aspath list format binds on as-notation format
authorPhilippe Guibert <philippe.guibert@6wind.com>
Mon, 14 Nov 2022 14:56:40 +0000 (15:56 +0100)
committerPhilippe Guibert <philippe.guibert@6wind.com>
Fri, 10 Feb 2023 09:27:23 +0000 (10:27 +0100)
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>
13 files changed:
bgpd/bgp_aspath.c
bgpd/bgp_aspath.h
bgpd/bgp_attr.c
bgpd/bgp_btoa.c
bgpd/bgp_route.c
bgpd/bgp_routemap.c
bgpd/bgp_script.c
bgpd/bgpd.c
bgpd/bgpd.h
lib/asn.c
lib/asn.h
tests/bgpd/test_aspath.c
tests/lib/test_printfrr.c

index 9f74aa76d40d8de382edf41965439f9e31f477e1..fdfe494e995042b47d8f4105e51ca3e90e1e41fa 100644 (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))
index a814d73dddc640b091059b1d3527cd1dee9905a7..e0cadef5e0e8fcb42d26f07dd588e88775a44734 100644 (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);
index 392b558805272259d42cb1433ff4a99164bc7577..eb1b208d9b76594aafe13ba19df1ae0862a398c3 100644 (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) {
index aa14d99f18563895721dea776c64da5c10ee6512..af944593b63bb76dafc6c903560b5a11cb30764c 100644 (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;
index 23e6195d3420ef15d567b261197756085ff2958a..32de9e3024675b662e9e612b1cf19a09a81ad35a 100644 (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;
index c9da71c6de249f57ea5306113b6c07c03aa77e0e..87e532efbefbfa75fcdfef63c1c1bf6433264b1d 100644 (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;
index bf3e612bfdb754f2feccd2486aed925342724475..a6004fc72431b3386bf4a6f5ac0c837bb6b261f4 100644 (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);
index 565cbccee4a820d1194d24717541673d1bffe032..6b8c17da8b5c0a96d8c1901363e941f6d69950c8 100644 (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)
index 634c4cf1d1d96bd2d359ba958d25279abe17a52a..aa53e7c30dad3cd406314b4cb2f56cf18707e509 100644 (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);
 
index 502854ec95fb31a6a70ed43c65bec83fd0761017..4c37cca1f68272b0bf5cfdf2895fa8983179753c 100644 (file)
--- a/lib/asn.c
+++ b/lib/asn.c
@@ -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);
+}
index 2884aafd542b9d92795b4aae56d295c7e614385a..902516ef7f3f29aa86c5950f15bd22abf20f5736 100644 (file)
--- a/lib/asn.h
+++ b/lib/asn.h
@@ -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);
 
index 0f6d5b023de4eeb7530f3b6aebb1415c4894ce4d..8c173ea669763927e69a997823832d19a5dd8909 100644 (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);
index 59d08ae82b0407dddabeeb091acba4c08e4a5ae3..f9755512a520b67586c42c55a163e3d3cb66e69f 100644 (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;
 }