summaryrefslogtreecommitdiff
path: root/usr.sbin/bgpd/parse.y
diff options
context:
space:
mode:
authorClaudio Jeker <claudio@cvs.openbsd.org>2009-12-16 15:40:56 +0000
committerClaudio Jeker <claudio@cvs.openbsd.org>2009-12-16 15:40:56 +0000
commita87cdf1b1be47fda7890da3b72e1073ce938fae8 (patch)
treec2b87f7f12b62de25c3a0c38df55b1f43a6590fa /usr.sbin/bgpd/parse.y
parentfaf772dba08a4b68f728672246d07a9674cecf96 (diff)
Implement "set ext-community [delete] subtype key:value" to set and delete
extended communities as specified in RFC 4360. No matching implemented yet and stuff like * and neighbor-as are neither supported but will be soon. Looks good henning & sthen, manpage fixed by jmc
Diffstat (limited to 'usr.sbin/bgpd/parse.y')
-rw-r--r--usr.sbin/bgpd/parse.y192
1 files changed, 190 insertions, 2 deletions
diff --git a/usr.sbin/bgpd/parse.y b/usr.sbin/bgpd/parse.y
index cf0eb382668..1b780cd36f3 100644
--- a/usr.sbin/bgpd/parse.y
+++ b/usr.sbin/bgpd/parse.y
@@ -1,4 +1,4 @@
-/* $OpenBSD: parse.y,v 1.245 2009/12/09 12:52:07 claudio Exp $ */
+/* $OpenBSD: parse.y,v 1.246 2009/12/16 15:40:55 claudio Exp $ */
/*
* Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -129,6 +129,10 @@ struct filter_rule *get_rule(enum action_types);
int getcommunity(char *);
int parsecommunity(struct filter_community *, char *);
+int parsesubtype(char *);
+int parseextvalue(char *, u_int32_t *);
+int parseextcommunity(struct filter_extcommunity *, char *,
+ char *);
typedef struct {
union {
@@ -171,7 +175,8 @@ typedef struct {
%token QUICK
%token FROM TO ANY
%token CONNECTED STATIC
-%token PREFIX PREFIXLEN SOURCEAS TRANSITAS PEERAS COMMUNITY DELETE
+%token COMMUNITY EXTCOMMUNITY
+%token PREFIX PREFIXLEN SOURCEAS TRANSITAS PEERAS DELETE
%token SET LOCALPREF MED METRIC NEXTHOP REJECT BLACKHOLE NOMODIFY SELF
%token PREPEND_SELF PREPEND_PEER PFTABLE WEIGHT RTLABEL ORIGIN
%token ERROR INCLUDE
@@ -1791,6 +1796,24 @@ filter_set_opt : LOCALPREF NUMBER {
YYERROR;
}
}
+ | EXTCOMMUNITY delete STRING STRING {
+ if (($$ = calloc(1, sizeof(struct filter_set))) == NULL)
+ fatal(NULL);
+ if ($2)
+ $$->type = ACTION_DEL_EXT_COMMUNITY;
+ else
+ $$->type = ACTION_SET_EXT_COMMUNITY;
+
+ if (parseextcommunity(&$$->action.ext_community,
+ $3, $4) == -1) {
+ free($3);
+ free($4);
+ free($$);
+ YYERROR;
+ }
+ free($3);
+ free($4);
+ }
| ORIGIN origincode {
if (($$ = calloc(1, sizeof(struct filter_set))) == NULL)
fatal(NULL);
@@ -1888,6 +1911,7 @@ lookup(char *s)
{ "enforce", ENFORCE},
{ "esp", ESP},
{ "evaluate", EVALUATE},
+ { "ext-community", EXTCOMMUNITY},
{ "fib-update", FIBUPDATE},
{ "from", FROM},
{ "group", GROUP},
@@ -2552,6 +2576,156 @@ parsecommunity(struct filter_community *c, char *s)
return (0);
}
+int
+parsesubtype(char *type)
+{
+ /* this has to be sorted always */
+ static const struct keywords keywords[] = {
+ { "bdc", EXT_COMMUNITY_BGP_COLLECT },
+ { "odi", EXT_COMMUNITY_OSPF_DOM_ID },
+ { "ori", EXT_COMMUNITY_OSPF_RTR_ID },
+ { "ort", EXT_COMMUNITY_OSPF_RTR_TYPE },
+ { "rt", EXT_COMMUNITY_ROUTE_TGT },
+ { "soo", EXT_CUMMUNITY_ROUTE_ORIG }
+ };
+ const struct keywords *p;
+
+ p = bsearch(type, keywords, sizeof(keywords)/sizeof(keywords[0]),
+ sizeof(keywords[0]), kw_cmp);
+
+ if (p)
+ return (p->k_val);
+ else
+ return (-1);
+}
+
+int
+parseextvalue(char *s, u_int32_t *v)
+{
+ const char *errstr;
+ char *p;
+ struct in_addr ip;
+ u_int32_t uvalh = 0, uval;
+
+ if ((p = strchr(s, '.')) == NULL) {
+ /* AS_PLAIN number (4 or 2 byte) */
+ uval = strtonum(s, 0, UINT_MAX, &errstr);
+ if (errstr) {
+ yyerror("Bad ext-community %s is %s", s, errstr);
+ return (-1);
+ }
+ *v = uval;
+ if (uval > USHRT_MAX)
+ return (EXT_COMMUNITY_FOUR_AS);
+ else
+ return (EXT_COMMUNITY_TWO_AS);
+ } else if (strchr(p + 1, '.') == NULL) {
+ /* AS_DOT number (4-byte) */
+ *p++ = '\0';
+ uvalh = strtonum(s, 0, USHRT_MAX, &errstr);
+ if (errstr) {
+ yyerror("Bad ext-community %s is %s", s, errstr);
+ return (-1);
+ }
+ uval = strtonum(p, 0, USHRT_MAX, &errstr);
+ if (errstr) {
+ yyerror("Bad ext-community %s is %s", p, errstr);
+ return (-1);
+ }
+ *v = uval | (uvalh << 16);
+ return (EXT_COMMUNITY_FOUR_AS);
+ } else {
+ /* more then one dot -> IP address */
+ if (inet_aton(s, &ip) == 0) {
+ yyerror("Bad ext-community %s not parseable", s);
+ return (-1);
+ }
+ *v = ip.s_addr;
+ return (EXT_COMMUNITY_IPV4);
+ }
+ return (-1);
+}
+
+int
+parseextcommunity(struct filter_extcommunity *c, char *t, char *s)
+{
+ const struct ext_comm_pairs iana[] = IANA_EXT_COMMUNITIES;
+ const char *errstr;
+ u_int64_t ullval;
+ u_int32_t uval;
+ char *p, *ep;
+ unsigned int i;
+ int type, subtype;
+
+ if ((subtype = parsesubtype(t)) == -1) {
+ yyerror("Bad ext-community unknown type");
+ return (-1);
+ }
+
+ if ((p = strchr(s, ':')) == NULL) {
+ type = EXT_COMMUNITY_OPAQUE,
+ errno = 0;
+ ullval = strtoull(s, &ep, 0);
+ if (s[0] == '\0' || *ep != '\0') {
+ yyerror("Bad ext-community bad value");
+ return (-1);
+ }
+ if (errno == ERANGE && ullval > EXT_COMMUNITY_OPAQUE_MAX) {
+ yyerror("Bad ext-community value to big");
+ return (-1);
+ }
+ c->data.ext_opaq = ullval;
+ } else {
+ *p++ = '\0';
+ if ((type = parseextvalue(s, &uval)) == -1)
+ return (-1);
+ switch (type) {
+ case EXT_COMMUNITY_TWO_AS:
+ ullval = strtonum(p, 0, UINT_MAX, &errstr);
+ break;
+ case EXT_COMMUNITY_IPV4:
+ case EXT_COMMUNITY_FOUR_AS:
+ ullval = strtonum(p, 0, USHRT_MAX, &errstr);
+ break;
+ default:
+ fatalx("parseextcommunity: unexpected result");
+ }
+ if (errstr) {
+ yyerror("Bad ext-community %s is %s", p,
+ errstr);
+ return (-1);
+ }
+ switch (type) {
+ case EXT_COMMUNITY_TWO_AS:
+ c->data.ext_as.as = uval;
+ c->data.ext_as.val = ullval;
+ break;
+ case EXT_COMMUNITY_IPV4:
+ c->data.ext_ip.addr.s_addr = uval;
+ c->data.ext_ip.val = ullval;
+ break;
+ case EXT_COMMUNITY_FOUR_AS:
+ c->data.ext_as4.as4 = uval;
+ c->data.ext_as4.val = ullval;
+ break;
+ }
+ }
+ c->type = type;
+ c->subtype = subtype;
+
+ /* verify type/subtype combo */
+ for (i = 0; i < sizeof(iana)/sizeof(iana[0]); i++) {
+ if (iana[i].type == type && iana[i].subtype == subtype) {
+ if (iana[i].transitive)
+ c->type |= EXT_COMMUNITY_TRANSITIVE;
+ return (0);
+ }
+ }
+
+ yyerror("Bad ext-community bad format for type");
+ return (-1);
+}
+
struct peer *
alloc_peer(void)
{
@@ -2933,6 +3107,11 @@ merge_filterset(struct filter_set_head *sh, struct filter_set *s)
yyerror("community is already set");
else if (s->type == ACTION_DEL_COMMUNITY)
yyerror("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)
+ yyerror(
+ "ext-community will already be deleted");
else
yyerror("redefining set parameter %s",
filterset_name(s->type));
@@ -2959,6 +3138,15 @@ merge_filterset(struct filter_set_head *sh, struct filter_set *s)
return (0);
}
break;
+ case ACTION_SET_EXT_COMMUNITY:
+ case ACTION_DEL_EXT_COMMUNITY:
+ if (memcmp(&s->action.ext_community,
+ &t->action.ext_community,
+ sizeof(s->action.ext_community)) < 0) {
+ TAILQ_INSERT_BEFORE(t, s, entry);
+ return (0);
+ }
+ break;
case ACTION_SET_NEXTHOP:
if (s->action.nexthop.aid <
t->action.nexthop.aid) {