summaryrefslogtreecommitdiff
path: root/usr.sbin/bgpctl
diff options
context:
space:
mode:
authorClaudio Jeker <claudio@cvs.openbsd.org>2023-04-21 09:12:42 +0000
committerClaudio Jeker <claudio@cvs.openbsd.org>2023-04-21 09:12:42 +0000
commitc3e547b753522b25f8f329ccf24a70ef7733e0bc (patch)
tree969d7ec6395093c6650ccdd6d77d3d47a847e273 /usr.sbin/bgpctl
parentbd1979ead4d118a2ba81e93ff7827a6efcf56b2a (diff)
Implement flowspec add and delete to add/remove flowspec rules dynamically.
OK tb@
Diffstat (limited to 'usr.sbin/bgpctl')
-rw-r--r--usr.sbin/bgpctl/bgpctl.c126
-rw-r--r--usr.sbin/bgpctl/parser.c312
-rw-r--r--usr.sbin/bgpctl/parser.h12
3 files changed, 443 insertions, 7 deletions
diff --git a/usr.sbin/bgpctl/bgpctl.c b/usr.sbin/bgpctl/bgpctl.c
index 8f607a1a519..91387b0ab01 100644
--- a/usr.sbin/bgpctl/bgpctl.c
+++ b/usr.sbin/bgpctl/bgpctl.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: bgpctl.c,v 1.291 2023/04/20 14:01:50 claudio Exp $ */
+/* $OpenBSD: bgpctl.c,v 1.292 2023/04/21 09:12:41 claudio Exp $ */
/*
* Copyright (c) 2003 Henning Brauer <henning@openbsd.org>
@@ -57,6 +57,7 @@ void show_mrt_msg(struct mrt_bgp_msg *, void *);
const char *msg_type(uint8_t);
void network_bulk(struct parse_result *);
int match_aspath(void *, uint16_t, struct filter_as *);
+struct flowspec *res_to_flowspec(struct parse_result *);
struct imsgbuf *ibuf;
struct mrt_parser show_mrt = { show_mrt_dump, show_mrt_state, show_mrt_msg };
@@ -85,6 +86,7 @@ main(int argc, char *argv[])
struct parse_result *res;
struct ctl_neighbor neighbor;
struct ctl_show_rib_request ribreq;
+ struct flowspec *f;
char *sockname;
enum imsg_type type;
@@ -363,6 +365,18 @@ main(int argc, char *argv[])
break;
case FLOWSPEC_ADD:
case FLOWSPEC_REMOVE:
+ f = res_to_flowspec(res);
+ /* attribute sets are not supported */
+ if (res->action == FLOWSPEC_ADD) {
+ imsg_compose(ibuf, IMSG_FLOWSPEC_ADD, 0, 0, -1,
+ f, FLOWSPEC_SIZE + f->len);
+ send_filterset(ibuf, &res->set);
+ imsg_compose(ibuf, IMSG_FLOWSPEC_DONE, 0, 0, -1,
+ NULL, 0);
+ } else
+ imsg_compose(ibuf, IMSG_FLOWSPEC_REMOVE, 0, 0, -1,
+ f, FLOWSPEC_SIZE + f->len);
+ printf("request sent.\n");
done = 1;
break;
case FLOWSPEC_FLUSH:
@@ -1954,3 +1968,113 @@ match_aspath(void *data, uint16_t len, struct filter_as *f)
}
return (0);
}
+
+static void
+component_finish(int type, uint8_t *data, int len)
+{
+ uint8_t *last;
+ int i;
+
+ switch (type) {
+ case FLOWSPEC_TYPE_DEST:
+ case FLOWSPEC_TYPE_SOURCE:
+ /* nothing todo */
+ return;
+ default:
+ break;
+ }
+
+ i = 0;
+ do {
+ last = data + i;
+ i += FLOWSPEC_OP_LEN(*last) + 1;
+ } while (i < len);
+ *last |= FLOWSPEC_OP_EOL;
+}
+
+static void
+push_prefix(struct parse_result *r, int type, struct bgpd_addr *addr,
+ uint8_t len)
+{
+ void *data;
+ uint8_t *comp;
+ int complen, l;
+
+ switch (addr->aid) {
+ case AID_UNSPEC:
+ return;
+ case AID_INET:
+ complen = PREFIX_SIZE(len);
+ data = &addr->v4;
+ break;
+ case AID_INET6:
+ /* IPv6 includes an offset byte */
+ complen = PREFIX_SIZE(len) + 1;
+ data = &addr->v6;
+ break;
+ }
+ comp = malloc(complen);
+ if (comp == NULL)
+ err(1, NULL);
+
+ l = 0;
+ comp[l++] = len;
+ if (addr->aid == AID_INET6)
+ comp[l++] = 0;
+ memcpy(comp + l, data, complen - l);
+
+ r->flow.complen[type] = complen;
+ r->flow.components[type] = comp;
+}
+
+
+struct flowspec *
+res_to_flowspec(struct parse_result *r)
+{
+ struct flowspec *f;
+ int i, len = 0;
+ uint8_t aid;
+
+ switch (r->aid) {
+ case AID_INET:
+ aid = AID_FLOWSPECv4;
+ break;
+ case AID_INET6:
+ aid = AID_FLOWSPECv6;
+ break;
+ default:
+ errx(1, "unsupported AFI %s for flowspec rule",
+ aid2str(r->aid));
+ }
+
+ push_prefix(r, FLOWSPEC_TYPE_DEST, &r->flow.dst, r->flow.dstlen);
+ push_prefix(r, FLOWSPEC_TYPE_SOURCE, &r->flow.src, r->flow.srclen);
+
+ for (i = FLOWSPEC_TYPE_MIN; i < FLOWSPEC_TYPE_MAX; i++)
+ if (r->flow.components[i] != NULL)
+ len += r->flow.complen[i] + 1;
+
+ if (len == 0)
+ errx(1, "no flowspec rule defined");
+
+ f = malloc(FLOWSPEC_SIZE + len);
+ if (f == NULL)
+ err(1, NULL);
+ memset(f, 0, FLOWSPEC_SIZE);
+
+ f->aid = aid;
+ f->len = len;
+
+ len = 0;
+ for (i = FLOWSPEC_TYPE_MIN; i < FLOWSPEC_TYPE_MAX; i++)
+ if (r->flow.components[i] != NULL) {
+ f->data[len++] = i;
+ component_finish(i, r->flow.components[i],
+ r->flow.complen[i]);
+ memcpy(f->data + len, r->flow.components[i],
+ r->flow.complen[i]);
+ len += r->flow.complen[i];
+ }
+
+ return f;
+}
diff --git a/usr.sbin/bgpctl/parser.c b/usr.sbin/bgpctl/parser.c
index 3f039e97c05..defe91007eb 100644
--- a/usr.sbin/bgpctl/parser.c
+++ b/usr.sbin/bgpctl/parser.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: parser.c,v 1.130 2023/04/20 14:01:50 claudio Exp $ */
+/* $OpenBSD: parser.c,v 1.131 2023/04/21 09:12:41 claudio Exp $ */
/*
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -63,6 +63,17 @@ enum token_type {
RTABLE,
FILENAME,
PATHID,
+ FLOW_PROTO,
+ FLOW_SRC,
+ FLOW_DST,
+ FLOW_SRCPORT,
+ FLOW_DSTPORT,
+ FLOW_ICMPTYPE,
+ FLOW_ICMPCODE,
+ FLOW_LENGTH,
+ FLOW_DSCP,
+ FLOW_FLAGS,
+ FLOW_FRAGS,
};
struct token {
@@ -97,6 +108,13 @@ static const struct token t_show_prefix[];
static const struct token t_show_ip[];
static const struct token t_network[];
static const struct token t_flowspec[];
+static const struct token t_flowfamily[];
+static const struct token t_flowrule[];
+static const struct token t_flowsrc[];
+static const struct token t_flowdst[];
+static const struct token t_flowsrcport[];
+static const struct token t_flowdstport[];
+static const struct token t_flowicmp[];
static const struct token t_bulk[];
static const struct token t_network_show[];
static const struct token t_prefix[];
@@ -333,11 +351,62 @@ static const struct token t_network[] = {
};
static const struct token t_flowspec[] = {
+ { KEYWORD, "add", FLOWSPEC_ADD, t_flowfamily},
+ { KEYWORD, "delete", FLOWSPEC_REMOVE,t_flowfamily},
{ KEYWORD, "flush", FLOWSPEC_FLUSH, NULL},
{ KEYWORD, "show", FLOWSPEC_SHOW, t_network_show},
{ ENDTOKEN, "", NONE, NULL}
};
+static const struct token t_flowfamily[] = {
+ { FAMILY, "", NONE, t_flowrule},
+ { ENDTOKEN, "", NONE, NULL}
+};
+
+static const struct token t_flowrule[] = {
+ { NOTOKEN, "", NONE, NULL},
+ { FLOW_FLAGS, "flags", NONE, t_flowrule},
+ { FLOW_FRAGS, "fragment", NONE, t_flowrule},
+ { KEYWORD, "from", NONE, t_flowsrc},
+ { FLOW_ICMPTYPE,"icmp-type", NONE, t_flowicmp},
+ { FLOW_LENGTH, "length", NONE, t_flowrule},
+ { FLOW_PROTO, "proto", NONE, t_flowrule},
+ { KEYWORD, "set", NONE, t_set},
+ { KEYWORD, "to", NONE, t_flowdst},
+ { FLOW_DSCP, "dscp", NONE, t_flowrule},
+ { ENDTOKEN, "", NONE, NULL}
+};
+
+static const struct token t_flowsrc[] = {
+ { KEYWORD, "any", NONE, t_flowsrcport},
+ { FLOW_SRC, "", NONE, t_flowsrcport},
+ { ENDTOKEN, "", NONE, NULL}
+};
+
+static const struct token t_flowdst[] = {
+ { KEYWORD, "any", NONE, t_flowdstport},
+ { FLOW_DST, "", NONE, t_flowdstport},
+ { ENDTOKEN, "", NONE, NULL}
+};
+
+static const struct token t_flowsrcport[] = {
+ { FLOW_SRCPORT, "port", NONE, t_flowrule},
+ { ANYTOKEN, "", NONE, t_flowrule},
+ { ENDTOKEN, "", NONE, NULL}
+};
+
+static const struct token t_flowdstport[] = {
+ { FLOW_DSTPORT, "port", NONE, t_flowrule},
+ { ANYTOKEN, "", NONE, t_flowrule},
+ { ENDTOKEN, "", NONE, NULL}
+};
+
+static const struct token t_flowicmp[] = {
+ { FLOW_ICMPCODE,"code", NONE, t_flowrule},
+ { ANYTOKEN, "", NONE, t_flowrule},
+ { ENDTOKEN, "", NONE, NULL}
+};
+
static const struct token t_bulk[] = {
{ KEYWORD, "add", NETWORK_BULK_ADD, t_set},
{ KEYWORD, "delete", NETWORK_BULK_REMOVE, NULL},
@@ -350,9 +419,9 @@ static const struct token t_prefix[] = {
};
static const struct token t_network_show[] = {
- { NOTOKEN, "", NONE, NULL},
- { FAMILY, "", NONE, NULL},
- { ENDTOKEN, "", NONE, NULL}
+ { NOTOKEN, "", NONE, NULL},
+ { FAMILY, "", NONE, NULL},
+ { ENDTOKEN, "", NONE, NULL}
};
static const struct token t_rd[] = {
@@ -406,6 +475,7 @@ void parsecommunity(struct community *c, char *s);
void parselargecommunity(struct community *c, char *s);
void parseextcommunity(struct community *c, const char *t, char *s);
int parse_nexthop(const char *, struct parse_result *);
+int parse_flow_numop(int, char *[], struct parse_result *, enum token_type);
struct parse_result *
parse(int argc, char *argv[])
@@ -530,6 +600,26 @@ match_token(int argc, char *argv[], const struct token table[], int *argsused)
t = &table[i];
}
break;
+ case FLOW_SRC:
+ if (parse_prefix(word, wordlen, &res.flow.src,
+ &res.flow.srclen)) {
+ match++;
+ t = &table[i];
+ if (res.aid != res.flow.src.aid)
+ errx(1, "wrong address family in "
+ "flowspec rule");
+ }
+ break;
+ case FLOW_DST:
+ if (parse_prefix(word, wordlen, &res.flow.dst,
+ &res.flow.dstlen)) {
+ match++;
+ t = &table[i];
+ if (res.aid != res.flow.dst.aid)
+ errx(1, "wrong address family in "
+ "flowspec rule");
+ }
+ break;
case PREFIX:
if (parse_prefix(word, wordlen, &res.addr,
&res.prefixlen)) {
@@ -723,6 +813,29 @@ match_token(int argc, char *argv[], const struct token table[], int *argsused)
t = &table[i];
}
break;
+ case FLOW_SRCPORT:
+ case FLOW_DSTPORT:
+ case FLOW_PROTO:
+ case FLOW_ICMPTYPE:
+ case FLOW_ICMPCODE:
+ case FLOW_LENGTH:
+ case FLOW_DSCP:
+ if (word != NULL && strncmp(word, table[i].keyword,
+ wordlen) == 0 && argc > 1) {
+ *argsused += parse_flow_numop(argc, argv, &res,
+ table[i].type);
+
+ match++;
+ t = &table[i];
+ }
+ break;
+ case FLOW_FLAGS:
+ case FLOW_FRAGS:
+ if (word != NULL && strncmp(word, table[i].keyword,
+ wordlen) == 0) {
+ errx(1, "%s not yet implemented", word);
+ }
+ break;
case ENDTOKEN:
break;
}
@@ -770,6 +883,8 @@ show_valid_args(const struct token table[])
fprintf(stderr, " <address>\n");
break;
case PREFIX:
+ case FLOW_SRC:
+ case FLOW_DST:
fprintf(stderr, " <address>[/<len>]\n");
break;
case ASNUM:
@@ -822,6 +937,21 @@ show_valid_args(const struct token table[])
case FILENAME:
fprintf(stderr, " <filename>\n");
break;
+ case FLOW_SRCPORT:
+ case FLOW_DSTPORT:
+ case FLOW_PROTO:
+ case FLOW_ICMPTYPE:
+ case FLOW_ICMPCODE:
+ case FLOW_LENGTH:
+ case FLOW_DSCP:
+ fprintf(stderr, " %s <numberspec>\n",
+ table[i].keyword);
+ break;
+ case FLOW_FLAGS:
+ case FLOW_FRAGS:
+ fprintf(stderr, " %s <flagspec>\n",
+ table[i].keyword);
+ break;
case ENDTOKEN:
break;
}
@@ -1330,3 +1460,177 @@ parse_nexthop(const char *word, struct parse_result *r)
TAILQ_INSERT_TAIL(&r->set, fs, entry);
return (1);
}
+
+static int
+unary_op(const char *op)
+{
+ if (strcmp(op, "=") == 0)
+ return FLOWSPEC_OP_NUM_EQ;
+ if (strcmp(op, "!=") == 0)
+ return FLOWSPEC_OP_NUM_NOT;
+ if (strcmp(op, ">") == 0)
+ return FLOWSPEC_OP_NUM_GT;
+ if (strcmp(op, ">=") == 0)
+ return FLOWSPEC_OP_NUM_GE;
+ if (strcmp(op, "<") == 0)
+ return FLOWSPEC_OP_NUM_LT;
+ if (strcmp(op, "<=") == 0)
+ return FLOWSPEC_OP_NUM_LE;
+ return -1;
+}
+
+static enum comp_ops
+binary_op(const char *op)
+{
+ if (strcmp(op, "-") == 0)
+ return OP_RANGE;
+ if (strcmp(op, "><") == 0)
+ return OP_XRANGE;
+ return OP_NONE;
+}
+
+static void
+push_numop(struct parse_result *r, int type, uint8_t op, int and, long long val)
+{
+ uint8_t *comp;
+ void *data;
+ uint32_t u32;
+ uint16_t u16;
+ uint8_t u8, flag = 0;
+ int len, complen;
+
+ flag |= op;
+ if (and)
+ flag |= FLOWSPEC_OP_AND;
+
+ if (val < 0 || val > 0xffffffff) {
+ errx(1, "unsupported value for flowspec num_op");
+ } else if (val <= 255) {
+ len = 1;
+ u8 = val;
+ data = &u8;
+ } else if (val <= 0xffff) {
+ len = 2;
+ u16 = htons(val);
+ data = &u16;
+ flag |= 1 << FLOWSPEC_OP_LEN_SHIFT;
+ } else {
+ len = 4;
+ u32 = htonl(val);
+ data = &u32;
+ flag |= 2 << FLOWSPEC_OP_LEN_SHIFT;
+ }
+
+ complen = r->flow.complen[type];
+ comp = realloc(r->flow.components[type], complen + len + 1);
+ if (comp == NULL)
+ err(1, NULL);
+
+ comp[complen++] = flag;
+ memcpy(comp + complen, data, len);
+ complen += len;
+ r->flow.complen[type] = complen;
+ r->flow.components[type] = comp;
+}
+
+int
+parse_flow_numop(int argc, char *argv[], struct parse_result *r,
+ enum token_type toktype)
+{
+ const char *errstr;
+ long long val, val2;
+ int numargs, type;
+ int is_list = 0;
+ int op;
+
+ switch (toktype) {
+ case FLOW_PROTO:
+ type = FLOWSPEC_TYPE_PROTO;
+ break;
+ case FLOW_SRCPORT:
+ type = FLOWSPEC_TYPE_SRC_PORT;
+ break;
+ case FLOW_DSTPORT:
+ type = FLOWSPEC_TYPE_DST_PORT;
+ break;
+ case FLOW_ICMPTYPE:
+ type = FLOWSPEC_TYPE_ICMP_TYPE;
+ break;
+ case FLOW_ICMPCODE:
+ type = FLOWSPEC_TYPE_ICMP_CODE;
+ break;
+ case FLOW_LENGTH:
+ type = FLOWSPEC_TYPE_PKT_LEN;
+ break;
+ case FLOW_DSCP:
+ type = FLOWSPEC_TYPE_DSCP;
+ break;
+ default:
+ errx(1, "parse_flow_numop called with unsupported type");
+ }
+
+ /* skip keyword (which is already accounted for) */
+ argc--;
+ argv++;
+ numargs = argc;
+
+ while (argc > 0) {
+ if (strcmp(argv[0], "{") == 0) {
+ is_list = 1;
+ argc--;
+ argv++;
+ } else if (is_list && strcmp(argv[0], "}") == 0) {
+ is_list = 0;
+ argc--;
+ argv++;
+ } else if ((op = unary_op(argv[0])) != -1) {
+ if (argc < 2)
+ errx(1, "missing argument in flowspec "
+ "definition");
+
+ val = strtonum(argv[1], LLONG_MIN, LLONG_MAX, &errstr);
+ if (errstr)
+ errx(1, "\"%s\" invalid number: %s", argv[0],
+ errstr);
+ push_numop(r, type, op, 0, val);
+ argc -= 2;
+ argv += 2;
+ } else {
+ val = strtonum(argv[0], LLONG_MIN, LLONG_MAX, &errstr);
+ if (errstr)
+ errx(1, "\"%s\" invalid number: %s", argv[0],
+ errstr);
+ if (argc >= 3 && (op = binary_op(argv[1])) != OP_NONE) {
+ val2 = strtonum(argv[2], LLONG_MIN, LLONG_MAX,
+ &errstr);
+ if (errstr)
+ errx(1, "\"%s\" invalid number: %s",
+ argv[2], errstr);
+ switch (op) {
+ case OP_RANGE:
+ push_numop(r, type, FLOWSPEC_OP_NUM_GE,
+ 0, val);
+ push_numop(r, type, FLOWSPEC_OP_NUM_LE,
+ 1, val2);
+ break;
+ case OP_XRANGE:
+ push_numop(r, type, FLOWSPEC_OP_NUM_LT,
+ 0, val);
+ push_numop(r, type, FLOWSPEC_OP_NUM_GT,
+ 0, val2);
+ break;
+ }
+ argc -= 3;
+ argv += 3;
+ } else {
+ push_numop(r, type, FLOWSPEC_OP_NUM_EQ, 0, val);
+ argc--;
+ argv++;
+ }
+ }
+ if (is_list == 0)
+ break;
+ }
+
+ return numargs - argc;
+}
diff --git a/usr.sbin/bgpctl/parser.h b/usr.sbin/bgpctl/parser.h
index c0adf93e538..c58944f22ce 100644
--- a/usr.sbin/bgpctl/parser.h
+++ b/usr.sbin/bgpctl/parser.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: parser.h,v 1.45 2023/04/20 14:01:50 claudio Exp $ */
+/* $OpenBSD: parser.h,v 1.46 2023/04/21 09:12:41 claudio Exp $ */
/*
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -76,13 +76,21 @@ struct parse_result {
uint64_t rd;
int flags;
int is_group;
+ int mrtfd;
u_int rtableid;
uint32_t pathid;
enum actions action;
uint8_t validation_state;
uint8_t prefixlen;
uint8_t aid;
- int mrtfd;
+ struct flowstate {
+ struct bgpd_addr src;
+ struct bgpd_addr dst;
+ uint8_t *components[FLOWSPEC_TYPE_MAX];
+ uint16_t complen[FLOWSPEC_TYPE_MAX];
+ uint8_t srclen;
+ uint8_t dstlen;
+ } flow;
};
__dead void usage(void);