Here is an initial implementation of draft-ietf-idr-large-community for
OpenBGPD.  I can connect and exchange routes with these attributes
against exabgp.

Normal communities are two 16bit numbers.  With the addition of
32bit ASNs, those will not work if you wish to control one of
them.

Large Communities are 32bit:32bit:32bit.  It seems the convention will be
<control ASN>:<verb>:<noun>, with <verb> and <noun> being locally
defined.

RFC status: currently accepted by the IDR-WG, is at version -02, the
wire format is set, the attribute codepoint is assigned by IANA, and it
seems that only trivial details need to be addressed.  Very likely to be
accepted.

This was based on a partial implementation from Job Snijders, many
thanks!

Comments?  OK?



Index: usr.sbin/bgpd.large-communities//bgpd.8
===================================================================
RCS file: /cvs/openbsd/src/usr.sbin/bgpd/bgpd.8,v
retrieving revision 1.48
diff -u -p -u -p -r1.48 bgpd.8
--- usr.sbin/bgpd.large-communities//bgpd.8     14 Aug 2013 06:32:36 -0000      
1.48
+++ usr.sbin/bgpd.large-communities//bgpd.8     2 Oct 2016 13:39:54 -0000
@@ -238,6 +238,17 @@ control socket
 .Re
 .Pp
 .Rs
+.%A J. Snijders
+.%A J. Heitz
+.%A K. Patel
+.%A I. Bagdonas
+.%A A. Simpson
+.%D September 2016
+.%R draft-ietf-idr-large-community
+.%T Large BGP Communities Attribute
+.Re
+.Pp
+.Rs
 .%A A. Heffernan
 .%D August 1998
 .%R RFC 2385
Index: usr.sbin/bgpd.large-communities//bgpd.conf.5
===================================================================
RCS file: /cvs/openbsd/src/usr.sbin/bgpd/bgpd.conf.5,v
retrieving revision 1.147
diff -u -p -u -p -r1.147 bgpd.conf.5
--- usr.sbin/bgpd.large-communities//bgpd.conf.5        5 Oct 2016 07:38:06 
-0000       1.147
+++ usr.sbin/bgpd.large-communities//bgpd.conf.5        10 Oct 2016 20:05:40 
-0000
@@ -1143,6 +1143,39 @@ may be set to
 which is expanded to the current neighbor remote AS number.
 .Pp
 .It Xo
+.Ic large-community
+.Ar as-number : Ns Ar local : Ns Ar local
+.Xc
+.It Ic large-community Ar name
+This rule applies only to
+.Em UPDATES
+where the
+.Ic Large community
+path attribute is present and matches.
+Communities are specified as
+.Ar as-number : Ns Ar local : Ns Ar local ,
+where
+.Ar as-number
+is an AS number and
+.Ar local
+is a locally significant number between zero and
+.Li 4294967295.
+Both
+.Ar as-number
+and
+.Ar local
+may be set to
+.Sq *
+to do wildcard matching.
+Both
+.Ar as-number
+and
+.Ar local
+may be set to
+.Ic neighbor-as ,
+which is expanded to the current neighbor remote AS number.
+.Pp
+.It Xo
 .Ic ext-community
 .Ar subtype Ar as-number : Ns Ar local
 .Xc
@@ -1364,6 +1397,35 @@ Alternately, well-known communities may 
 .Ic NO_EXPORT_SUBCONFED ,
 or
 .Ic NO_PEER .
+For
+.Cm delete ,
+both
+.Ar as-number
+and
+.Ar local
+may be set to
+.Sq *
+to do wildcard matching.
+.Pp
+.It Xo
+.Ic large-community Op Ar delete
+.Ar as-number : Ns Ar local : Ns Ar local
+.Xc
+.It Xo
+.Ic large-community Op Ar delete
+.Ar name
+.Xc
+Set or delete the
+.Em Large Communities
+path attribute.
+Communities are specified as
+.Ar as-number : Ns Ar local : Ns Ar local ,
+where
+.Ar as-number
+is an AS number and
+.Ar local
+is a locally-significant number between zero and
+.Li 4294967295 .
 For
 .Cm delete ,
 both
Index: usr.sbin/bgpd.large-communities//bgpd.h
===================================================================
RCS file: /cvs/openbsd/src/usr.sbin/bgpd/bgpd.h,v
retrieving revision 1.296
diff -u -p -u -p -r1.296 bgpd.h
--- usr.sbin/bgpd.large-communities//bgpd.h     5 Oct 2016 07:38:06 -0000       
1.296
+++ usr.sbin/bgpd.large-communities//bgpd.h     10 Oct 2016 19:14:41 -0000
@@ -377,6 +377,7 @@ enum imsg_type {
        IMSG_CTL_SHOW_RIB_PREFIX,
        IMSG_CTL_SHOW_RIB_ATTR,
        IMSG_CTL_SHOW_RIB_COMMUNITY,
+       IMSG_CTL_SHOW_RIB_LARGECOMMUNITY,
        IMSG_CTL_SHOW_NETWORK,
        IMSG_CTL_SHOW_RIB_MEM,
        IMSG_CTL_SHOW_TERSE,
@@ -648,6 +649,12 @@ struct filter_community {
        int             type;
 };
 
+struct filter_largecommunity {
+       uint32_t        as;
+       uint32_t        ld1;
+       uint32_t        ld2;
+};
+
 struct filter_extcommunity {
        u_int16_t       flags;
        u_int8_t        type;
@@ -675,6 +682,7 @@ struct ctl_show_rib_request {
        struct bgpd_addr        prefix;
        struct filter_as        as;
        struct filter_community community;
+       struct filter_largecommunity large_community;
        u_int32_t               peerid;
        pid_t                   pid;
        u_int16_t               flags;
@@ -793,6 +801,7 @@ struct filter_match {
        struct filter_as                as;
        struct filter_aslen             aslen;
        struct filter_community         community;
+       struct filter_largecommunity    large_community;
        struct filter_extcommunity      ext_community;
 };
 
@@ -834,6 +843,8 @@ enum action_types {
        ACTION_SET_NEXTHOP_SELF,
        ACTION_SET_COMMUNITY,
        ACTION_DEL_COMMUNITY,
+       ACTION_DEL_LARGE_COMMUNITY,
+       ACTION_SET_LARGE_COMMUNITY,
        ACTION_SET_EXT_COMMUNITY,
        ACTION_DEL_EXT_COMMUNITY,
        ACTION_PFTABLE,
@@ -852,6 +863,7 @@ struct filter_set {
                int32_t                 relative;
                struct bgpd_addr        nexthop;
                struct filter_community community;
+               struct filter_largecommunity    large_community;
                struct filter_extcommunity      ext_community;
                char                    pftable[PFTABLE_LEN];
                char                    rtlabel[RTLABEL_LEN];
Index: usr.sbin/bgpd.large-communities//control.c
===================================================================
RCS file: /cvs/openbsd/src/usr.sbin/bgpd/control.c,v
retrieving revision 1.82
diff -u -p -u -p -r1.82 control.c
--- usr.sbin/bgpd.large-communities//control.c  5 Dec 2015 18:28:04 -0000       
1.82
+++ usr.sbin/bgpd.large-communities//control.c  5 Sep 2016 13:41:29 -0000
@@ -244,6 +244,7 @@ control_dispatch_msg(struct pollfd *pfd,
                        case IMSG_CTL_SHOW_RIB_PREFIX:
                        case IMSG_CTL_SHOW_RIB_MEM:
                        case IMSG_CTL_SHOW_RIB_COMMUNITY:
+                       case IMSG_CTL_SHOW_RIB_LARGECOMMUNITY:
                        case IMSG_CTL_SHOW_NETWORK:
                        case IMSG_CTL_SHOW_TERSE:
                        case IMSG_CTL_SHOW_TIMER:
@@ -462,6 +463,7 @@ control_dispatch_msg(struct pollfd *pfd,
                        break;
                case IMSG_CTL_SHOW_RIB_MEM:
                case IMSG_CTL_SHOW_RIB_COMMUNITY:
+               case IMSG_CTL_SHOW_RIB_LARGECOMMUNITY:
                case IMSG_CTL_SHOW_NETWORK:
                        c->ibuf.pid = imsg.hdr.pid;
                        imsg_ctl_rde(imsg.hdr.type, imsg.hdr.pid,
Index: usr.sbin/bgpd.large-communities//parse.y
===================================================================
RCS file: /cvs/openbsd/src/usr.sbin/bgpd/parse.y,v
retrieving revision 1.289
diff -u -p -u -p -r1.289 parse.y
--- usr.sbin/bgpd.large-communities//parse.y    5 Oct 2016 07:38:06 -0000       
1.289
+++ usr.sbin/bgpd.large-communities//parse.y    10 Oct 2016 21:49:58 -0000
@@ -138,6 +138,8 @@ struct filter_rule  *get_rule(enum action
 
 int             getcommunity(char *);
 int             parsecommunity(struct filter_community *, char *);
+int             getlargecommunity(char *);
+int             parselargecommunity(struct filter_largecommunity *, char *);
 int             parsesubtype(char *);
 int             parseextvalue(char *, u_int32_t *);
 int             parseextcommunity(struct filter_extcommunity *, char *,
@@ -185,7 +187,7 @@ typedef struct {
 %token QUICK
 %token FROM TO ANY
 %token CONNECTED STATIC
-%token COMMUNITY EXTCOMMUNITY
+%token COMMUNITY EXTCOMMUNITY LARGECOMMUNITY
 %token PREFIX PREFIXLEN SOURCEAS TRANSITAS PEERAS DELETE MAXASLEN MAXASSEQ
 %token SET LOCALPREF MED METRIC NEXTHOP REJECT BLACKHOLE NOMODIFY SELF
 %token PREPEND_SELF PREPEND_PEER PFTABLE WEIGHT RTLABEL ORIGIN
@@ -1712,10 +1714,12 @@ filter_as       : as4number_any         {
 filter_match_h : /* empty */                   {
                        bzero(&$$, sizeof($$));
                        $$.m.community.as = COMMUNITY_UNSET;
+                       $$.m.large_community.as = (uint32_t)COMMUNITY_UNSET;
                }
                | {
                        bzero(&fmopts, sizeof(fmopts));
                        fmopts.m.community.as = COMMUNITY_UNSET;
+                       fmopts.m.large_community.as = (uint32_t)COMMUNITY_UNSET;
                }
                    filter_match                {
                        memcpy(&$$, &fmopts, sizeof($$));
@@ -1776,6 +1780,18 @@ filter_elm       : filter_prefix_h       {
                        }
                        free($2);
                }
+               | LARGECOMMUNITY STRING {
+                       if (fmopts.m.large_community.as != 
(uint32_t)COMMUNITY_UNSET) {
+                               yyerror("\"large-community\" already 
specified");
+                               free($2);
+                               YYERROR;
+                       }
+                       if (parselargecommunity(&fmopts.m.large_community, $2) 
== -1) {
+                               free($2);
+                               YYERROR;
+                       }
+                       free($2);
+               }
                | EXTCOMMUNITY STRING STRING {
                        if (fmopts.m.ext_community.flags &
                            EXT_COMMUNITY_FLAG_VALID) {
@@ -2131,6 +2147,31 @@ filter_set_opt   : LOCALPREF NUMBER              {
                                YYERROR;
                        }
                }
+               | LARGECOMMUNITY delete STRING  {
+                       if (($$ = calloc(1, sizeof(struct filter_set))) == NULL)
+                               fatal(NULL);
+                       if ($2)
+                               $$->type = ACTION_DEL_LARGE_COMMUNITY;
+                       else
+                               $$->type = ACTION_SET_LARGE_COMMUNITY;
+
+                       if (parselargecommunity(&$$->action.large_community,
+                           $3) == -1) {
+                               free($3);
+                               free($$);
+                               YYERROR;
+                       }
+                       free($3);
+                       /* Don't allow setting of any match */
+                       if (!$2 &&
+                           ($$->action.large_community.as == 
(uint32_t)COMMUNITY_ANY ||
+                           $$->action.large_community.ld1 == 
(uint32_t)COMMUNITY_ANY ||
+                           $$->action.large_community.ld2 == 
(uint32_t)COMMUNITY_ANY)) {
+                               yyerror("'*' is not allowed in set community");
+                               free($$);
+                               YYERROR;
+                       }
+               }
                | EXTCOMMUNITY delete STRING STRING {
                        if (($$ = calloc(1, sizeof(struct filter_set))) == NULL)
                                fatal(NULL);
@@ -2266,6 +2307,7 @@ lookup(char *s)
                { "inet6",              IPV6},
                { "ipsec",              IPSEC},
                { "key",                KEY},
+               { "large-community",    LARGECOMMUNITY},
                { "listen",             LISTEN},
                { "local-address",      LOCALADDR},
                { "localpref",          LOCALPREF},
@@ -2911,6 +2953,59 @@ parsecommunity(struct filter_community *
 }
 
 int
+getlargecommunity(char *s)
+{
+       int              val;
+       const char      *errstr;
+
+       if (strcmp(s, "*") == 0)
+               return (COMMUNITY_ANY);
+       if (strcmp(s, "neighbor-as") == 0)
+               return (COMMUNITY_NEIGHBOR_AS);
+       val = strtonum(s, 0, UINT_MAX, &errstr);
+       if (errstr) {
+               yyerror("Large Community %s is %s (max: %u)",
+                   s, errstr, UINT_MAX);
+               return (COMMUNITY_ERROR);
+       }
+       return (val);
+}
+
+int
+parselargecommunity(struct filter_largecommunity *c, char *s)
+{
+       char *p, *q;
+       int as, ld1, ld2;
+
+       if ((p = strchr(s, ':')) == NULL) {
+               yyerror("Bad community syntax");
+               return (-1);
+       }
+       *p++ = 0;
+
+       if ((q = strchr(p, ':')) == NULL) {
+               yyerror("Bad community syntax");
+               return (-1);
+       }
+       *q++ = 0;
+
+       if ((as = getlargecommunity(s)) == COMMUNITY_ERROR)
+               return (-1);
+
+       if ((ld1 = getlargecommunity(p)) == COMMUNITY_ERROR)
+               return (-1);
+
+       if ((ld2 = getlargecommunity(q)) == COMMUNITY_ERROR)
+               return (-1);
+
+       c->as = as;
+       c->ld1 = ld1;
+       c->ld2 = ld2;
+
+       return (0);
+}
+
+int
 parsesubtype(char *type)
 {
        /* this has to be sorted always */
@@ -3527,6 +3622,10 @@ merge_filterset(struct filter_set_head *
                                yyerror("community is already set");
                        else if (s->type == ACTION_DEL_COMMUNITY)
                                yyerror("community will already be deleted");
+                       else if (s->type == ACTION_SET_LARGE_COMMUNITY)
+                               yyerror("large-community is already set");
+                       else if (s->type == ACTION_DEL_LARGE_COMMUNITY)
+                               yyerror("large-community will already be 
deleted");
                        else if (s->type == ACTION_SET_EXT_COMMUNITY)
                                yyerror("ext-community is already set");
                        else if (s->type == ACTION_DEL_EXT_COMMUNITY)
@@ -3558,6 +3657,18 @@ merge_filterset(struct filter_set_head *
                                        return (0);
                                }
                                break;
+                       case ACTION_SET_LARGE_COMMUNITY:
+                       case ACTION_DEL_LARGE_COMMUNITY:
+                               if (s->action.large_community.as <
+                                   t->action.large_community.as ||
+                                   (s->action.large_community.as ==
+                                   t->action.large_community.as &&
+                                   s->action.large_community.ld1 <
+                                   t->action.large_community.ld2 )) {
+                                       TAILQ_INSERT_BEFORE(t, s, entry);
+                                       return (0);
+                               }
+                               break;
                        case ACTION_SET_EXT_COMMUNITY:
                        case ACTION_DEL_EXT_COMMUNITY:
                                if (memcmp(&s->action.ext_community,
@@ -3634,6 +3745,7 @@ get_rule(enum action_types type)
                r->dir = out ? DIR_OUT : DIR_IN;
                r->action = ACTION_NONE;
                r->match.community.as = COMMUNITY_UNSET;
+               r->match.large_community.as = (uint32_t)COMMUNITY_UNSET;
                TAILQ_INIT(&r->set);
                if (curpeer == curgroup) {
                        /* group */
Index: usr.sbin/bgpd.large-communities//printconf.c
===================================================================
RCS file: /cvs/openbsd/src/usr.sbin/bgpd/printconf.c,v
retrieving revision 1.98
diff -u -p -u -p -r1.98 printconf.c
--- usr.sbin/bgpd.large-communities//printconf.c        5 Oct 2016 07:38:06 
-0000       1.98
+++ usr.sbin/bgpd.large-communities//printconf.c        10 Oct 2016 21:40:47 
-0000
@@ -28,6 +28,7 @@
 
 void            print_op(enum comp_ops);
 void            print_community(int, int);
+void            print_largecommunity(uint32_t , uint32_t, uint32_t);
 void            print_extcommunity(struct filter_extcommunity *);
 void            print_origin(u_int8_t);
 void            print_set(struct filter_set_head *);
@@ -102,6 +103,33 @@ print_community(int as, int type)
 }
 
 void
+print_largecommunity(uint32_t as, uint32_t ld1, uint32_t ld2)
+{
+       if (as == (uint32_t)COMMUNITY_ANY)
+               printf("*:");
+       else if (as == (uint32_t)COMMUNITY_NEIGHBOR_AS)
+               printf("neighbor-as:");
+       else
+               printf("%u:", as);
+
+       if (ld1 == (uint32_t)COMMUNITY_ANY)
+               printf("*:");
+       else if (ld1 == (uint32_t)COMMUNITY_NEIGHBOR_AS)
+               printf("neighbor-as:");
+       else
+               printf("%u:", ld1);
+
+       if (ld2 == (uint32_t)COMMUNITY_ANY)
+               printf("* ");
+       else if (ld2 == (uint32_t)COMMUNITY_NEIGHBOR_AS)
+               printf("neighbor-as ");
+       else
+               printf("%u ", ld2);
+
+}
+
+
+void
 print_extcommunity(struct filter_extcommunity *c)
 {
        switch (c->type & EXT_COMMUNITY_VALUE) {
@@ -200,6 +228,20 @@ print_set(struct filter_set_head *set)
                        printf("community ");
                        print_community(s->action.community.as,
                            s->action.community.type);
+                       printf(" ");
+                       break;
+               case ACTION_DEL_LARGE_COMMUNITY:
+                       printf("large-community delete ");
+                       print_largecommunity(s->action.large_community.as,
+                           s->action.large_community.ld1,
+                           s->action.large_community.ld2);
+                       printf(" ");
+                       break;
+               case ACTION_SET_LARGE_COMMUNITY:
+                       printf("large-community ");
+                       print_largecommunity(s->action.large_community.as,
+                           s->action.large_community.ld1,
+                           s->action.large_community.ld2);
                        printf(" ");
                        break;
                case ACTION_PFTABLE:
Index: usr.sbin/bgpd.large-communities//rde.c
===================================================================
RCS file: /cvs/openbsd/src/usr.sbin/bgpd/rde.c,v
retrieving revision 1.350
diff -u -p -u -p -r1.350 rde.c
--- usr.sbin/bgpd.large-communities//rde.c      3 Sep 2016 16:22:17 -0000       
1.350
+++ usr.sbin/bgpd.large-communities//rde.c      5 Sep 2016 14:44:09 -0000
@@ -559,6 +559,7 @@ badnet:
                case IMSG_CTL_SHOW_RIB:
                case IMSG_CTL_SHOW_RIB_AS:
                case IMSG_CTL_SHOW_RIB_COMMUNITY:
+               case IMSG_CTL_SHOW_RIB_LARGECOMMUNITY:
                case IMSG_CTL_SHOW_RIB_PREFIX:
                        if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(req)) {
                                log_warnx("rde_dispatch: wrong imsg len");
@@ -1577,6 +1578,23 @@ bad_flags:
                    ATTR_PARTIAL))
                        goto bad_flags;
                goto optattr;
+       case ATTR_LARGE_COMMUNITIES:
+               if (attr_len % 12 != 0) {
+                       /*
+                        * mark update as bad and withdraw all routes as per
+                        * draft-ietf-idr-optional-transitive-00.txt
+                        * but only if partial bit is set
+                        */
+                       if ((flags & ATTR_PARTIAL) == 0)
+                               goto bad_len;
+                       a->flags |= F_ATTR_PARSE_ERR;
+                       log_peer_warnx(&peer->conf, "bad LARGE COMMUNITIES, "
+                           "path invalidated and prefix withdrawn");
+               }
+               if (!CHECK_FLAGS(flags, ATTR_OPTIONAL|ATTR_TRANSITIVE,
+                   ATTR_PARTIAL))
+                       goto bad_flags;
+               goto optattr;
        case ATTR_EXT_COMMUNITIES:
                if (attr_len % 8 != 0) {
                        /*
@@ -2266,6 +2284,10 @@ rde_dump_filter(struct prefix *p, struct
                    !community_match(p->aspath, req->community.as,
                    req->community.type))
                        return;
+               if (req->type == IMSG_CTL_SHOW_RIB_LARGECOMMUNITY &&
+                   !community_large_match(p->aspath, req->large_community.as,
+                   req->large_community.ld1, req->large_community.ld2))
+                       return;
                if ((req->flags & F_CTL_ACTIVE) && p->rib->active != p)
                        return;
                rde_dump_rib_as(p, p->aspath, req->pid, req->flags);
@@ -2348,6 +2370,7 @@ rde_dump_ctx_new(struct ctl_show_rib_req
        case IMSG_CTL_SHOW_RIB:
        case IMSG_CTL_SHOW_RIB_AS:
        case IMSG_CTL_SHOW_RIB_COMMUNITY:
+       case IMSG_CTL_SHOW_RIB_LARGECOMMUNITY:
                ctx->ribctx.ctx_upcall = rde_dump_upcall;
                break;
        case IMSG_CTL_SHOW_RIB_PREFIX:
Index: usr.sbin/bgpd.large-communities//rde.h
===================================================================
RCS file: /cvs/openbsd/src/usr.sbin/bgpd/rde.h,v
retrieving revision 1.149
diff -u -p -u -p -r1.149 rde.h
--- usr.sbin/bgpd.large-communities//rde.h      6 Nov 2015 16:23:26 -0000       
1.149
+++ usr.sbin/bgpd.large-communities//rde.h      10 Oct 2016 19:10:01 -0000
@@ -112,7 +112,8 @@ enum attrtypes {
        ATTR_MP_UNREACH_NLRI=15,
        ATTR_EXT_COMMUNITIES=16,
        ATTR_AS4_PATH=17,
-       ATTR_AS4_AGGREGATOR=18
+       ATTR_AS4_AGGREGATOR=18,
+       ATTR_LARGE_COMMUNITIES=30,
 };
 
 /* attribute flags. 4 low order bits reserved */
@@ -367,6 +368,12 @@ int                 aspath_lenmatch(struct aspath *, e
 int             community_match(struct rde_aspath *, int, int);
 int             community_set(struct rde_aspath *, int, int);
 void            community_delete(struct rde_aspath *, int, int);
+int             community_large_match(struct rde_aspath *, uint32_t, uint32_t,
+                   uint32_t);
+int             community_large_set(struct rde_aspath *, uint32_t, uint32_t,
+                   uint32_t);
+void            community_large_delete(struct rde_aspath *, uint32_t,
+                   uint32_t, uint32_t);
 int             community_ext_match(struct rde_aspath *,
                    struct filter_extcommunity *, u_int16_t);
 int             community_ext_set(struct rde_aspath *,
Index: usr.sbin/bgpd.large-communities//rde_attr.c
===================================================================
RCS file: /cvs/openbsd/src/usr.sbin/bgpd/rde_attr.c,v
retrieving revision 1.95
diff -u -p -u -p -r1.95 rde_attr.c
--- usr.sbin/bgpd.large-communities//rde_attr.c 24 Oct 2015 08:00:42 -0000      
1.95
+++ usr.sbin/bgpd.large-communities//rde_attr.c 10 Oct 2016 21:34:08 -0000
@@ -1352,3 +1352,147 @@ community_ext_matchone(struct filter_ext
 
        return (0);
 }
+
+int
+community_large_match(struct rde_aspath *asp, uint32_t as, uint32_t ld1,
+    uint32_t ld2)
+{
+       struct filter_largecommunity    *bar;
+       struct attr     *a;
+       u_int8_t        *p;
+       u_int16_t        len;
+       u_int32_t        eas, eld1, eld2;
+
+       a = attr_optget(asp, ATTR_LARGE_COMMUNITIES);
+       if (a == NULL)
+               /* no communities, no match */
+               return (0);
+
+       p = a->data;
+       for (len = a->len / 12; len > 0; len--) {
+               bar = (struct filter_largecommunity *)p;
+               p += 12;
+               eas = betoh32(bar->as);
+               eld1 = betoh32(bar->ld1);
+               eld2 = betoh32(bar->ld2);
+
+               if ((as == (uint32_t)COMMUNITY_ANY || as == eas) &&
+                   (ld1 == (uint32_t)COMMUNITY_ANY || ld1 == eld1) &&
+                   (ld2 == (uint32_t)COMMUNITY_ANY || ld2 == eld2))
+                       return (1);
+       }
+       return (0);
+}
+
+int
+community_large_set(struct rde_aspath *asp, uint32_t as, uint32_t ld1,
+    uint32_t ld2)
+{
+       struct filter_largecommunity    *bar;
+       struct attr     *attr;
+       u_int8_t        *p = NULL;
+       unsigned int     i, ncommunities = 0;
+       u_int8_t         f = ATTR_OPTIONAL|ATTR_TRANSITIVE;
+
+       attr = attr_optget(asp, ATTR_LARGE_COMMUNITIES);
+       if (attr != NULL) {
+               p = attr->data;
+               ncommunities = attr->len / 12;
+       }
+
+       /* first check if the community is not already set */
+       for (i = 0; i < ncommunities; i++) {
+               bar = (struct filter_largecommunity *)p;
+               if (bar->as == as && bar->ld1 == ld1 && bar->ld2 == ld2)
+                       /* already present, nothing todo */
+                       return (1);
+               p += 12;
+       }
+
+       if (ncommunities++ >= USHRT_MAX / 12)
+               /* overflow */
+               return (0);
+
+       if ((p = reallocarray(NULL, ncommunities, 12)) == NULL)
+               fatal("community_set");
+
+       bar = (struct filter_largecommunity *)p;
+       bar->as = htobe32(as);
+       bar->ld1 = htobe32(ld1);
+       bar->ld2 = htobe32(ld2);
+
+       if (attr != NULL) {
+               memcpy(p + 12, attr->data, attr->len);
+               f = attr->flags;
+               attr_free(asp, attr);
+       }
+
+       attr_optadd(asp, f, ATTR_LARGE_COMMUNITIES, p, ncommunities * 12);
+
+       free(p);
+       return (1);
+}
+
+void
+community_large_delete(struct rde_aspath *asp, uint32_t as, uint32_t ld1,
+    uint32_t ld2)
+{
+       struct filter_largecommunity    *bar;
+       struct attr     *attr;
+       u_int8_t        *p, *n;
+       u_int16_t        l = 0, len = 0;
+       u_int32_t        eas, eld1, eld2;
+       u_int8_t         f;
+
+       attr = attr_optget(asp, ATTR_LARGE_COMMUNITIES);
+       if (attr == NULL)
+               /* no attr nothing to do */
+               return;
+
+       p = attr->data;
+       for (len = 0; l < attr->len; l += 12) {
+               bar = (struct filter_largecommunity *)p;
+               p += 12;
+               eas = betoh32(bar->as);
+               eld1 = betoh32(bar->ld1);
+               eld2 = betoh32(bar->ld2);
+
+               if ((as == (uint32_t)COMMUNITY_ANY || as == eas) &&
+                   (ld1 == (uint32_t)COMMUNITY_ANY || ld1 == eld1) &&
+                   (ld2 == (uint32_t)COMMUNITY_ANY || ld2 == eld2))
+                       /* match */
+                       continue;
+               len += 12;
+       }
+
+       if (len == 0) {
+               attr_free(asp, attr);
+               return;
+       }
+
+       if ((n = malloc(len)) == NULL)
+               fatal("community_delete");
+
+       p = attr->data;
+       for (l = 0; l < len && p < attr->data + attr->len; ) {
+               bar = (struct filter_largecommunity *)p;
+               p += 12;
+               eas = betoh32(bar->as);
+               eld1 = betoh32(bar->ld1);
+               eld2 = betoh32(bar->ld2);
+
+               if ((as == (uint32_t)COMMUNITY_ANY || as == eas) &&
+                   (ld1 == (uint32_t)COMMUNITY_ANY || ld1 == eld1) &&
+                   (ld2 == (uint32_t)COMMUNITY_ANY || ld2 == eld2))
+                       /* match */
+                       continue;
+               memcpy(n + l, bar, sizeof(*bar));
+               l += 12;
+       }
+
+       f = attr->flags;
+
+       attr_free(asp, attr);
+       attr_optadd(asp, f, ATTR_LARGE_COMMUNITIES, n, len);
+       free(n);
+}
Index: usr.sbin/bgpd.large-communities//rde_filter.c
===================================================================
RCS file: /cvs/openbsd/src/usr.sbin/bgpd/rde_filter.c,v
retrieving revision 1.77
diff -u -p -u -p -r1.77 rde_filter.c
--- usr.sbin/bgpd.large-communities//rde_filter.c       3 Jun 2016 17:36:37 
-0000       1.77
+++ usr.sbin/bgpd.large-communities//rde_filter.c       10 Oct 2016 21:53:24 
-0000
@@ -35,7 +35,7 @@ rde_apply_set(struct rde_aspath *asp, st
 {
        struct filter_set       *set;
        u_char                  *np;
-       int                      as, type;
+       int                      as, type, ld1, ld2;
        u_int32_t                prep_as;
        u_int16_t                nl;
        u_int8_t                 prepend;
@@ -181,6 +181,84 @@ rde_apply_set(struct rde_aspath *asp, st
 
                        community_delete(asp, as, type);
                        break;
+               case ACTION_SET_LARGE_COMMUNITY:
+                       switch (set->action.large_community.as) {
+                       case COMMUNITY_ERROR:
+                               fatalx("rde_apply_set bad large community 
string");
+                       case COMMUNITY_NEIGHBOR_AS:
+                               as = peer->conf.remote_as;
+                               break;
+                       case COMMUNITY_ANY:
+                       default:
+                               as = set->action.large_community.as;
+                               break;
+                       }
+
+                       switch (set->action.large_community.ld1) {
+                       case COMMUNITY_ERROR:
+                               fatalx("rde_apply_set bad large community 
string");
+                       case COMMUNITY_NEIGHBOR_AS:
+                               ld1 = peer->conf.remote_as;
+                               break;
+                       case COMMUNITY_ANY:
+                       default:
+                               ld1 = set->action.large_community.ld1;
+                               break;
+                       }
+
+                       switch (set->action.large_community.ld2) {
+                       case COMMUNITY_ERROR:
+                               fatalx("rde_apply_set bad large community 
string");
+                       case COMMUNITY_NEIGHBOR_AS:
+                               ld2 = peer->conf.remote_as;
+                               break;
+                       case COMMUNITY_ANY:
+                       default:
+                               ld2 = set->action.large_community.ld2;
+                               break;
+                       }
+
+                       community_large_set(asp, as, ld1, ld2);
+                       break;
+               case ACTION_DEL_LARGE_COMMUNITY:
+                       switch (set->action.large_community.as) {
+                       case COMMUNITY_ERROR:
+                               fatalx("rde_apply_set bad community string");
+                       case COMMUNITY_NEIGHBOR_AS:
+                               as = peer->conf.remote_as;
+                               break;
+                       case COMMUNITY_ANY:
+                       default:
+                               as = set->action.large_community.as;
+                               break;
+                       }
+
+                       switch (set->action.large_community.ld1) {
+                       case COMMUNITY_ERROR:
+                               fatalx("rde_apply_set bad community string");
+                       case COMMUNITY_NEIGHBOR_AS:
+                               ld1 = peer->conf.remote_as;
+                               break;
+                       case COMMUNITY_ANY:
+                       default:
+                               ld1 = set->action.large_community.ld1;
+                               break;
+                       }
+
+                       switch (set->action.large_community.ld2) {
+                       case COMMUNITY_ERROR:
+                               fatalx("rde_apply_set bad community string");
+                       case COMMUNITY_NEIGHBOR_AS:
+                               ld2 = peer->conf.remote_as;
+                               break;
+                       case COMMUNITY_ANY:
+                       default:
+                               ld2 = set->action.large_community.ld2;
+                               break;
+                       }
+
+                       community_large_delete(asp, as, ld1, ld2);
+                       break;
                case ACTION_PFTABLE:
                        /* convert pftable name to an id */
                        set->action.id = pftable_name2id(set->action.pftable);
@@ -547,6 +625,14 @@ filterset_equal(struct filter_set_head *
                            sizeof(a->action.community)) == 0)
                                continue;
                        break;
+               case ACTION_DEL_LARGE_COMMUNITY:
+               case ACTION_SET_LARGE_COMMUNITY:
+                       if (a->type == b->type &&
+                           memcmp(&a->action.large_community,
+                           &b->action.large_community,
+                           sizeof(a->action.large_community)) == 0)
+                               continue;
+                       break;
                case ACTION_PFTABLE:
                case ACTION_PFTABLE_ID:
                        if (b->type == ACTION_PFTABLE)
@@ -630,6 +716,10 @@ filterset_name(enum action_types type)
                return ("community");
        case ACTION_DEL_COMMUNITY:
                return ("community delete");
+       case ACTION_SET_LARGE_COMMUNITY:
+               return ("large-community");
+       case ACTION_DEL_LARGE_COMMUNITY:
+               return ("large-community delete");
        case ACTION_PFTABLE:
        case ACTION_PFTABLE_ID:
                return ("pftable");
Index: usr.sbin/bgpctl.large-communities//Makefile
===================================================================
RCS file: /cvs/openbsd/src/usr.sbin/bgpctl/Makefile,v
retrieving revision 1.12
diff -u -p -u -p -r1.12 Makefile
--- usr.sbin/bgpctl.large-communities//Makefile 21 Sep 2011 10:37:51 -0000      
1.12
+++ usr.sbin/bgpctl.large-communities//Makefile 1 Oct 2016 20:49:22 -0000
@@ -11,7 +11,7 @@ CFLAGS+= -Wstrict-prototypes -Wmissing-p
 CFLAGS+= -Wmissing-declarations
 CFLAGS+= -Wshadow -Wpointer-arith -Wcast-qual
 CFLAGS+= -Wsign-compare
-CFLAGS+= -I${.CURDIR} -I${.CURDIR}/../bgpd
+CFLAGS+= -I${.CURDIR} -I${.CURDIR}/../bgpd.large-communities
 MAN=   bgpctl.8
 LDADD= -lutil
 DPADD+= ${LIBUTIL}
Index: usr.sbin/bgpctl.large-communities//bgpctl.8
===================================================================
RCS file: /cvs/openbsd/src/usr.sbin/bgpctl/bgpctl.8,v
retrieving revision 1.69
diff -u -p -u -p -r1.69 bgpctl.8
--- usr.sbin/bgpctl.large-communities//bgpctl.8 25 May 2016 14:15:59 -0000      
1.69
+++ usr.sbin/bgpctl.large-communities//bgpctl.8 5 Sep 2016 13:41:29 -0000
@@ -300,6 +300,9 @@ anywhere in the AS path.
 .It Cm community Ar community
 Show all entries with community
 .Ar community .
+.It Cm large-community Ar large-community
+Show all entries with large-community
+.Ar large-community .
 .It Cm empty-as
 Show all entries that are internal routes with no AS's in the AS path.
 .It Cm memory
Index: usr.sbin/bgpctl.large-communities//bgpctl.c
===================================================================
RCS file: /cvs/openbsd/src/usr.sbin/bgpctl/bgpctl.c,v
retrieving revision 1.188
diff -u -p -u -p -r1.188 bgpctl.c
--- usr.sbin/bgpctl.large-communities//bgpctl.c 3 Jun 2016 17:36:37 -0000       
1.188
+++ usr.sbin/bgpctl.large-communities//bgpctl.c 10 Oct 2016 21:03:12 -0000
@@ -82,6 +82,7 @@ void           show_rib_brief(struct ctl_show_ri
 void            show_rib_detail(struct ctl_show_rib *, u_char *, int);
 void            show_attr(void *, u_int16_t);
 void            show_community(u_char *, u_int16_t);
+void            show_large_community(u_char *, u_int16_t);
 void            show_ext_community(u_char *, u_int16_t);
 char           *fmt_mem(int64_t);
 int             show_rib_memory_msg(struct imsg *);
@@ -254,6 +255,13 @@ main(int argc, char *argv[])
                            sizeof(res->community));
                        type = IMSG_CTL_SHOW_RIB_COMMUNITY;
                }
+               if (res->large_community.as != (uint32_t)COMMUNITY_UNSET &&
+                   res->large_community.ld1 != (uint32_t)COMMUNITY_UNSET &&
+                   res->large_community.ld2 != (uint32_t)COMMUNITY_UNSET) {
+                       memcpy(&ribreq.large_community, &res->large_community,
+                           sizeof(res->large_community));
+                       type = IMSG_CTL_SHOW_RIB_LARGECOMMUNITY;
+               }
                memcpy(&ribreq.neighbor, &neighbor, sizeof(ribreq.neighbor));
                strlcpy(ribreq.rib, res->rib, sizeof(ribreq.rib));
                ribreq.aid = res->aid;
@@ -275,6 +283,11 @@ main(int argc, char *argv[])
                    res->community.type != COMMUNITY_UNSET)
                        memcpy(&ribreq.community, &res->community,
                            sizeof(res->community));
+               if (res->large_community.as != (uint32_t)COMMUNITY_UNSET &&
+                   res->large_community.ld1 != (uint32_t)COMMUNITY_UNSET &&
+                   res->large_community.ld2 != (uint32_t)COMMUNITY_UNSET)
+                       memcpy(&ribreq.large_community, &res->large_community,
+                           sizeof(res->large_community));
                memcpy(&ribreq.neighbor, &neighbor, sizeof(ribreq.neighbor));
                ribreq.aid = res->aid;
                ribreq.flags = res->flags;
@@ -377,6 +390,11 @@ main(int argc, char *argv[])
                    res->community.type != COMMUNITY_UNSET)
                        memcpy(&ribreq.community, &res->community,
                            sizeof(res->community));
+               if (res->large_community.as != (uint32_t)COMMUNITY_UNSET &&
+                   res->large_community.ld1 != (uint32_t)COMMUNITY_UNSET &&
+                   res->large_community.ld2 != (uint32_t)COMMUNITY_UNSET)
+                       memcpy(&ribreq.large_community, &res->large_community,
+                           sizeof(res->large_community));
                memcpy(&ribreq.neighbor, &neighbor, sizeof(ribreq.neighbor));
                ribreq.aid = res->aid;
                ribreq.flags = res->flags;
@@ -1424,6 +1442,11 @@ show_attr(void *b, u_int16_t len)
                show_community(data, alen);
                printf("\n");
                break;
+       case ATTR_LARGE_COMMUNITIES:
+               printf("    Large Communities: ");
+               show_large_community(data, alen);
+               printf("\n");
+               break;
        case ATTR_AGGREGATOR:
                memcpy(&as, data, sizeof(as));
                memcpy(&id, data + sizeof(as), sizeof(id));
@@ -1496,6 +1519,30 @@ show_community(u_char *data, u_int16_t l
                        printf(" ");
        }
 }
+
+void
+show_large_community(u_char *data, u_int16_t len)
+{
+       u_int32_t       a, l1, l2;
+       u_int16_t       i;
+
+       if (len % 12)
+               return;
+
+       for (i = 0; i < len; i += 12) {
+               memcpy(&a, data + i, sizeof(a));
+               memcpy(&l1, data + i + 4, sizeof(l1));
+               memcpy(&l2, data + i + 8, sizeof(l2));
+               a = ntohl(a);
+               l1 = ntohl(l1);
+               l2 = ntohl(l2);
+               printf("%u:%u:%u", a, l1, l2);
+
+               if (i + 12 < len)
+                       printf(" ");
+       }
+}
+
 
 void
 show_ext_community(u_char *data, u_int16_t len)
Index: usr.sbin/bgpctl.large-communities//parser.c
===================================================================
RCS file: /cvs/openbsd/src/usr.sbin/bgpctl/parser.c,v
retrieving revision 1.73
diff -u -p -u -p -r1.73 parser.c
--- usr.sbin/bgpctl.large-communities//parser.c 11 Oct 2015 19:53:57 -0000      
1.73
+++ usr.sbin/bgpctl.large-communities//parser.c 10 Oct 2016 21:24:33 -0000
@@ -44,6 +44,7 @@ enum token_type {
        PEERDESC,
        RIBNAME,
        COMMUNITY,
+       LARGE_COMMUNITY,
        LOCALPREF,
        MED,
        NEXTHOP,
@@ -90,11 +91,13 @@ static const struct token t_show_mrt_as[
 static const struct token t_show_prefix[];
 static const struct token t_show_ip[];
 static const struct token t_show_community[];
+static const struct token t_show_largecommunity[];
 static const struct token t_network[];
 static const struct token t_network_show[];
 static const struct token t_prefix[];
 static const struct token t_set[];
 static const struct token t_community[];
+static const struct token t_largecommunity[];
 static const struct token t_localpref[];
 static const struct token t_med[];
 static const struct token t_nexthop[];
@@ -160,6 +163,7 @@ static const struct token t_show_rib[] =
        { ASTYPE,       "peer-as",      AS_PEER,        t_show_rib_as},
        { ASTYPE,       "empty-as",     AS_EMPTY,       t_show_rib},
        { KEYWORD,      "community",    NONE,           t_show_community},
+       { KEYWORD,      "large-community", NONE,        t_show_largecommunity},
        { FLAG,         "best",         F_CTL_ACTIVE,   t_show_rib},
        { FLAG,         "selected",     F_CTL_ACTIVE,   t_show_rib},
        { FLAG,         "detail",       F_CTL_DETAIL,   t_show_rib},
@@ -275,6 +279,11 @@ static const struct token t_show_communi
        { ENDTOKEN,     "",             NONE,           NULL}
 };
 
+static const struct token t_show_largecommunity[] = {
+       { LARGE_COMMUNITY,      "",     NONE,           t_show_rib},
+       { ENDTOKEN,     "",             NONE,           NULL}
+};
+
 static const struct token t_network[] = {
        { KEYWORD,      "add",          NETWORK_ADD,    t_prefix},
        { KEYWORD,      "delete",       NETWORK_REMOVE, t_prefix},
@@ -299,6 +308,7 @@ static const struct token t_network_show
 static const struct token t_set[] = {
        { NOTOKEN,      "",                     NONE,   NULL},
        { KEYWORD,      "community",            NONE,   t_community},
+       { KEYWORD,      "large-community",      NONE,   t_largecommunity},
        { KEYWORD,      "localpref",            NONE,   t_localpref},
        { KEYWORD,      "med",                  NONE,   t_med},
        { KEYWORD,      "metric",               NONE,   t_med},
@@ -317,6 +327,11 @@ static const struct token t_community[] 
        { ENDTOKEN,     "",                     NONE,   NULL}
 };
 
+static const struct token t_largecommunity[] = {
+       { LARGE_COMMUNITY,      "",             NONE,   t_set},
+       { ENDTOKEN,     "",                     NONE,   NULL}
+};
+
 static const struct token t_localpref[] = {
        { LOCALPREF,    "",                     NONE,   t_set},
        { ENDTOKEN,     "",                     NONE,   NULL}
@@ -391,6 +406,8 @@ int                  parse_number(const char *, struct
                             enum token_type);
 int                     getcommunity(const char *);
 int                     parse_community(const char *, struct parse_result *);
+int                     getlargecommunity(const char *);
+int                     parse_largecommunity(const char *, struct parse_result 
*);
 int                     parse_nexthop(const char *, struct parse_result *);
 int                     bgpctl_getopt(int *, char **[], int);
 
@@ -403,6 +420,9 @@ parse(int argc, char *argv[])
        bzero(&res, sizeof(res));
        res.community.as = COMMUNITY_UNSET;
        res.community.type = COMMUNITY_UNSET;
+       res.large_community.as = COMMUNITY_UNSET;
+       res.large_community.ld1 = COMMUNITY_UNSET;
+       res.large_community.ld2 = COMMUNITY_UNSET;
        TAILQ_INIT(&res.set);
        if ((res.irr_outdir = getcwd(NULL, 0)) == NULL) {
                fprintf(stderr, "getcwd failed: %s\n", strerror(errno));
@@ -556,6 +576,13 @@ match_token(int *argc, char **argv[], co
                                t = &table[i];
                        }
                        break;
+               case LARGE_COMMUNITY:
+                       if (word != NULL && wordlen > 0 &&
+                           parse_largecommunity(word, &res)) {
+                               match++;
+                               t = &table[i];
+                       }
+                       break;
                case LOCALPREF:
                case MED:
                case PREPNBR:
@@ -668,6 +695,9 @@ show_valid_args(const struct token table
                case COMMUNITY:
                        fprintf(stderr, "  <community>\n");
                        break;
+               case LARGE_COMMUNITY:
+                       fprintf(stderr, "  <large-community>\n");
+                       break;
                case LOCALPREF:
                case MED:
                case PREPNBR:
@@ -960,6 +990,58 @@ done:
        TAILQ_INSERT_TAIL(&r->set, fs, entry);
        return (1);
 }
+
+int
+getlargecommunity(const char *s)
+{
+       const char      *errstr;
+       u_int32_t        uval;
+
+       if (strcmp(s, "*") == 0)
+               return ((int32_t)COMMUNITY_ANY);
+
+       uval = strtonum(s, 0, UINT_MAX, &errstr);
+       if (errstr)
+               errx(1, "Large Community is %s: %s", errstr, s);
+
+       return (uval);
+}
+
+int
+parse_largecommunity(const char *word, struct parse_result *r)
+{
+       struct filter_set *fs;
+       char            *p = strdup(word);
+       char            *array[3];
+       u_int32_t        as, ld1, ld2;
+       int              i = 0;
+
+       while (p != NULL) {
+               array[i++] = p;
+               p = strchr(p, ':');
+               if (p)
+                       *p++ = NULL;
+       }
+
+       as   = getlargecommunity(array[0]);
+       ld1  = getlargecommunity(array[1]);
+       ld2  = getlargecommunity(array[2]);
+
+       if ((fs = calloc(1, sizeof(struct filter_set))) == NULL)
+               err(1, NULL);
+       fs->type = ACTION_SET_LARGE_COMMUNITY;
+       fs->action.large_community.as = as;
+       fs->action.large_community.ld1 = ld1;
+       fs->action.large_community.ld2 = ld2;
+
+       r->large_community.as = as;
+       r->large_community.ld1 = ld1;
+       r->large_community.ld2 = ld2;
+
+       TAILQ_INSERT_TAIL(&r->set, fs, entry);
+       return (1);
+}
+
 
 int
 parse_nexthop(const char *word, struct parse_result *r)
Index: usr.sbin/bgpctl.large-communities//parser.h
===================================================================
RCS file: /cvs/openbsd/src/usr.sbin/bgpctl/parser.h,v
retrieving revision 1.27
diff -u -p -u -p -r1.27 parser.h
--- usr.sbin/bgpctl.large-communities//parser.h 17 Apr 2015 07:51:09 -0000      
1.27
+++ usr.sbin/bgpctl.large-communities//parser.h 5 Sep 2016 13:41:29 -0000
@@ -63,6 +63,7 @@ struct parse_result {
        struct filter_as         as;
        struct filter_set_head   set;
        struct filter_community  community;
+       struct filter_largecommunity  large_community;
        char                     peerdesc[PEER_DESCR_LEN];
        char                     rib[PEER_DESCR_LEN];
        char                    *irr_outdir;



-- 
Lockwood's Long Shot:
        The chances of getting eaten up by a lion on Main Street aren't
        one in a million, but once would be enough.

Reply via email to