On 2016 Oct 11 (Tue) at 00:00:53 +0200 (+0200), Peter Hessler wrote:
: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?
:

Updated diff:
 - assert copyright for the non-trivial changes
 - since the magic ASN matching canaries use valid bits, seperate the
filter storage and a wire storage
 - clean up warnings
 - fix a few printing issues

OK?


Index: usr.sbin/bgpctl/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/bgpctl.8    25 May 2016 14:15:59 -0000      1.69
+++ usr.sbin/bgpctl/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/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/bgpctl.c    3 Jun 2016 17:36:37 -0000       1.188
+++ usr.sbin/bgpctl/bgpctl.c    13 Oct 2016 15:32:30 -0000
@@ -2,6 +2,8 @@
 
 /*
  * Copyright (c) 2003 Henning Brauer <henn...@openbsd.org>
+ * Copyright (c) 2016 Job Snijders <j...@instituut.net>
+ * Copyright (c) 2016 Peter Hessler <phess...@openbsd.org>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -82,6 +84,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 +257,13 @@ main(int argc, char *argv[])
                            sizeof(res->community));
                        type = IMSG_CTL_SHOW_RIB_COMMUNITY;
                }
+               if (res->large_community.as != COMMUNITY_UNSET &&
+                   res->large_community.ld1 != COMMUNITY_UNSET &&
+                   res->large_community.ld2 != 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 +285,11 @@ main(int argc, char *argv[])
                    res->community.type != COMMUNITY_UNSET)
                        memcpy(&ribreq.community, &res->community,
                            sizeof(res->community));
+               if (res->large_community.as != COMMUNITY_UNSET &&
+                   res->large_community.ld1 != COMMUNITY_UNSET &&
+                   res->large_community.ld2 != 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 +392,11 @@ main(int argc, char *argv[])
                    res->community.type != COMMUNITY_UNSET)
                        memcpy(&ribreq.community, &res->community,
                            sizeof(res->community));
+               if (res->large_community.as != COMMUNITY_UNSET &&
+                   res->large_community.ld1 != COMMUNITY_UNSET &&
+                   res->large_community.ld2 != 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 +1444,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));
@@ -1493,6 +1518,29 @@ show_community(u_char *data, u_int16_t l
                        printf("%hu:%hu", a, v);
 
                if (i + 4 < len)
+                       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(" ");
        }
 }
Index: usr.sbin/bgpctl/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/parser.c    11 Oct 2015 19:53:57 -0000      1.73
+++ usr.sbin/bgpctl/parser.c    13 Oct 2016 15:32:35 -0000
@@ -2,6 +2,8 @@
 
 /*
  * Copyright (c) 2003, 2004 Henning Brauer <henn...@openbsd.org>
+ * Copyright (c) 2016 Job Snijders <j...@instituut.net>
+ * Copyright (c) 2016 Peter Hessler <phess...@openbsd.org>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -44,6 +46,7 @@ enum token_type {
        PEERDESC,
        RIBNAME,
        COMMUNITY,
+       LARGE_COMMUNITY,
        LOCALPREF,
        MED,
        NEXTHOP,
@@ -90,11 +93,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 +165,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 +281,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 +310,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 +329,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 +408,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 +422,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 +578,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 +697,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:
@@ -956,6 +988,57 @@ done:
 
        r->community.as = as;
        r->community.type = type;
+
+       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 (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];
+       int64_t          as, ld1, ld2;
+       int              i = 0;
+
+       while (p != NULL) {
+               array[i++] = p;
+               p = strchr(p, ':');
+               if (p)
+                       *p++ = 0;
+       }
+
+       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);
Index: usr.sbin/bgpctl/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/parser.h    17 Apr 2015 07:51:09 -0000      1.27
+++ usr.sbin/bgpctl/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;
Index: usr.sbin/bgpd/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/bgpd.8        14 Aug 2013 06:32:36 -0000      1.48
+++ usr.sbin/bgpd/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/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/bgpd.conf.5   5 Oct 2016 07:38:06 -0000       1.147
+++ usr.sbin/bgpd/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/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/bgpd.h        5 Oct 2016 07:38:06 -0000       1.296
+++ usr.sbin/bgpd/bgpd.h        13 Oct 2016 14:41:12 -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,18 @@ struct filter_community {
        int             type;
 };
 
+struct filter_largecommunity {
+       int64_t         as;
+       int64_t         ld1;
+       int64_t         ld2;
+};
+
+struct wire_largecommunity {
+       uint32_t        as;
+       uint32_t        ld1;
+       uint32_t        ld2;
+};
+
 struct filter_extcommunity {
        u_int16_t       flags;
        u_int8_t        type;
@@ -675,6 +688,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 +807,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 +849,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 +869,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/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/control.c     5 Dec 2015 18:28:04 -0000       1.82
+++ usr.sbin/bgpd/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/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/parse.y       5 Oct 2016 07:38:06 -0000       1.289
+++ usr.sbin/bgpd/parse.y       13 Oct 2016 15:31:31 -0000
@@ -5,6 +5,8 @@
  * Copyright (c) 2001 Markus Friedl.  All rights reserved.
  * Copyright (c) 2001 Daniel Hartmeier.  All rights reserved.
  * Copyright (c) 2001 Theo de Raadt.  All rights reserved.
+ * Copyright (c) 2016 Job Snijders <j...@instituut.net>
+ * Copyright (c) 2016 Peter Hessler <phess...@openbsd.org>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -138,6 +140,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 +189,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 +1716,12 @@ filter_as       : as4number_any         {
 filter_match_h : /* empty */                   {
                        bzero(&$$, sizeof($$));
                        $$.m.community.as = COMMUNITY_UNSET;
+                       $$.m.large_community.as = COMMUNITY_UNSET;
                }
                | {
                        bzero(&fmopts, sizeof(fmopts));
                        fmopts.m.community.as = COMMUNITY_UNSET;
+                       fmopts.m.large_community.as = COMMUNITY_UNSET;
                }
                    filter_match                {
                        memcpy(&$$, &fmopts, sizeof($$));
@@ -1776,6 +1782,18 @@ filter_elm       : filter_prefix_h       {
                        }
                        free($2);
                }
+               | LARGECOMMUNITY STRING {
+                       if (fmopts.m.large_community.as != 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 +2149,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 == COMMUNITY_ANY ||
+                           $$->action.large_community.ld1 == COMMUNITY_ANY ||
+                           $$->action.large_community.ld2 == 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 +2309,7 @@ lookup(char *s)
                { "inet6",              IPV6},
                { "ipsec",              IPSEC},
                { "key",                KEY},
+               { "large-community",    LARGECOMMUNITY},
                { "listen",             LISTEN},
                { "local-address",      LOCALADDR},
                { "localpref",          LOCALPREF},
@@ -2911,6 +2955,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 +3624,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 +3659,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 +3747,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 = COMMUNITY_UNSET;
                TAILQ_INIT(&r->set);
                if (curpeer == curgroup) {
                        /* group */
Index: usr.sbin/bgpd/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/printconf.c   5 Oct 2016 07:38:06 -0000       1.98
+++ usr.sbin/bgpd/printconf.c   13 Oct 2016 15:31:27 -0000
@@ -2,6 +2,8 @@
 
 /*
  * Copyright (c) 2003, 2004 Henning Brauer <henn...@openbsd.org>
+ * Copyright (c) 2016 Job Snijders <j...@instituut.net>
+ * Copyright (c) 2016 Peter Hessler <phess...@openbsd.org>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -28,6 +30,7 @@
 
 void            print_op(enum comp_ops);
 void            print_community(int, int);
+void            print_largecommunity(int64_t , int64_t, int64_t);
 void            print_extcommunity(struct filter_extcommunity *);
 void            print_origin(u_int8_t);
 void            print_set(struct filter_set_head *);
@@ -102,6 +105,33 @@ print_community(int as, int type)
 }
 
 void
+print_largecommunity(int64_t as, int64_t ld1, int64_t ld2)
+{
+       if (as == COMMUNITY_ANY)
+               printf("*:");
+       else if (as == COMMUNITY_NEIGHBOR_AS)
+               printf("neighbor-as:");
+       else
+               printf("%llu:", as);
+
+       if (ld1 == COMMUNITY_ANY)
+               printf("*:");
+       else if (ld1 == COMMUNITY_NEIGHBOR_AS)
+               printf("neighbor-as:");
+       else
+               printf("%llu:", ld1);
+
+       if (ld2 == COMMUNITY_ANY)
+               printf("* ");
+       else if (ld2 == COMMUNITY_NEIGHBOR_AS)
+               printf("neighbor-as ");
+       else
+               printf("%llu ", ld2);
+
+}
+
+
+void
 print_extcommunity(struct filter_extcommunity *c)
 {
        switch (c->type & EXT_COMMUNITY_VALUE) {
@@ -202,6 +232,20 @@ print_set(struct filter_set_head *set)
                            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:
                        printf("pftable %s ", s->action.pftable);
                        break;
@@ -627,6 +671,12 @@ print_rule(struct peer *peer_l, struct f
        if (r->match.ext_community.flags & EXT_COMMUNITY_FLAG_VALID) {
                printf("ext-community ");
                print_extcommunity(&r->match.ext_community);
+       }
+       if (r->match.large_community.as != COMMUNITY_UNSET) {
+               printf("large-community ");
+               print_largecommunity(r->match.large_community.as,
+                   r->match.large_community.ld1,
+                   r->match.large_community.ld2);
        }
 
        print_set(&r->set);
Index: usr.sbin/bgpd/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/rde.c 3 Sep 2016 16:22:17 -0000       1.350
+++ usr.sbin/bgpd/rde.c 13 Oct 2016 15:31:23 -0000
@@ -2,6 +2,8 @@
 
 /*
  * Copyright (c) 2003, 2004 Henning Brauer <henn...@openbsd.org>
+ * Copyright (c) 2016 Job Snijders <j...@instituut.net>
+ * Copyright (c) 2016 Peter Hessler <phess...@openbsd.org>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -559,6 +561,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 +1580,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 +2286,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 +2372,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/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/rde.h 6 Nov 2015 16:23:26 -0000       1.149
+++ usr.sbin/bgpd/rde.h 13 Oct 2016 14:10:00 -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 *, int64_t, int64_t,
+                   int64_t);
+int             community_large_set(struct rde_aspath *, int64_t, int64_t,
+                   int64_t);
+void            community_large_delete(struct rde_aspath *, int64_t,
+                   int64_t, int64_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/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/rde_attr.c    24 Oct 2015 08:00:42 -0000      1.95
+++ usr.sbin/bgpd/rde_attr.c    13 Oct 2016 15:31:21 -0000
@@ -2,6 +2,8 @@
 
 /*
  * Copyright (c) 2004 Claudio Jeker <clau...@openbsd.org>
+ * Copyright (c) 2016 Job Snijders <j...@instituut.net>
+ * Copyright (c) 2016 Peter Hessler <phess...@openbsd.org>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -1351,4 +1353,148 @@ community_ext_matchone(struct filter_ext
        }
 
        return (0);
+}
+
+int
+community_large_match(struct rde_aspath *asp, int64_t as, int64_t ld1,
+    int64_t ld2)
+{
+       struct wire_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 wire_largecommunity *)p;
+               p += 12;
+               eas = betoh32(bar->as);
+               eld1 = betoh32(bar->ld1);
+               eld2 = betoh32(bar->ld2);
+
+               if ((as == COMMUNITY_ANY || as == eas) &&
+                   (ld1 == COMMUNITY_ANY || ld1 == eld1) &&
+                   (ld2 == COMMUNITY_ANY || ld2 == eld2))
+                       return (1);
+       }
+       return (0);
+}
+
+int
+community_large_set(struct rde_aspath *asp, int64_t as, int64_t ld1,
+    int64_t ld2)
+{
+       struct wire_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 wire_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 wire_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, int64_t as, int64_t ld1,
+    int64_t ld2)
+{
+       struct wire_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 wire_largecommunity *)p;
+               p += 12;
+               eas = betoh32(bar->as);
+               eld1 = betoh32(bar->ld1);
+               eld2 = betoh32(bar->ld2);
+
+               if ((as == COMMUNITY_ANY || as == eas) &&
+                   (ld1 == COMMUNITY_ANY || ld1 == eld1) &&
+                   (ld2 == 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 wire_largecommunity *)p;
+               p += 12;
+               eas = betoh32(bar->as);
+               eld1 = betoh32(bar->ld1);
+               eld2 = betoh32(bar->ld2);
+
+               if ((as == COMMUNITY_ANY || as == eas) &&
+                   (ld1 == COMMUNITY_ANY || ld1 == eld1) &&
+                   (ld2 == 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/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/rde_filter.c  3 Jun 2016 17:36:37 -0000       1.77
+++ usr.sbin/bgpd/rde_filter.c  13 Oct 2016 15:31:13 -0000
@@ -2,6 +2,8 @@
 
 /*
  * Copyright (c) 2004 Claudio Jeker <clau...@openbsd.org>
+ * Copyright (c) 2016 Job Snijders <j...@instituut.net>
+ * Copyright (c) 2016 Peter Hessler <phess...@openbsd.org>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -35,7 +37,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 +183,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 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_delete(asp, as, ld1, ld2);
+                       break;
                case ACTION_PFTABLE:
                        /* convert pftable name to an id */
                        set->action.id = pftable_name2id(set->action.pftable);
@@ -222,7 +302,7 @@ rde_filter_match(struct filter_rule *f, 
     struct rde_peer *from)
 {
        u_int32_t       pas;
-       int             cas, type;
+       int             cas, type, ld1, ld2;
 
        if (asp != NULL && f->match.as.type != AS_NONE) {
                if (f->match.as.flags & AS_FLAG_NEIGHBORAS)
@@ -270,6 +350,44 @@ rde_filter_match(struct filter_rule *f, 
                if (community_ext_match(asp, &f->match.ext_community,
                    peer->conf.remote_as) == 0)
                        return (0);
+       if (asp != NULL && f->match.large_community.as !=
+           COMMUNITY_UNSET) {
+               switch (f->match.large_community.as) {
+               case COMMUNITY_ERROR:
+                       fatalx("rde_apply_set bad community string");
+               case COMMUNITY_NEIGHBOR_AS:
+                       cas = peer->conf.remote_as;
+                       break;
+               default:
+                       cas = f->match.large_community.as;
+                       break;
+               }
+
+               switch (f->match.large_community.ld1) {
+               case COMMUNITY_ERROR:
+                       fatalx("rde_apply_set bad community string");
+               case COMMUNITY_NEIGHBOR_AS:
+                       ld1 = peer->conf.remote_as;
+                       break;
+               default:
+                       ld1 = f->match.large_community.ld1;
+                       break;
+               }
+
+               switch (f->match.large_community.ld2) {
+               case COMMUNITY_ERROR:
+                       fatalx("rde_apply_set bad community string");
+               case COMMUNITY_NEIGHBOR_AS:
+                       ld2 = peer->conf.remote_as;
+                       break;
+               default:
+                       ld2 = f->match.large_community.ld2;
+                       break;
+               }
+
+               if (community_large_match(asp, cas, ld1, ld2) == 0)
+                       return (0);
+       }
 
        if (f->match.prefix.addr.aid != 0) {
                if (f->match.prefix.addr.aid != prefix->aid)
@@ -547,6 +665,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 +756,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");




-- 
What I've done, of course, is total garbage.
                -- R. Willard, Pure Math 430a

Reply via email to