Implement flowspec add and delete to add/remove flowspec rules dynamically.
authorclaudio <claudio@openbsd.org>
Fri, 21 Apr 2023 09:12:41 +0000 (09:12 +0000)
committerclaudio <claudio@openbsd.org>
Fri, 21 Apr 2023 09:12:41 +0000 (09:12 +0000)
OK tb@

usr.sbin/bgpctl/bgpctl.c
usr.sbin/bgpctl/parser.c
usr.sbin/bgpctl/parser.h

index 8f607a1..91387b0 100644 (file)
@@ -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;
+}
index 3f039e9..defe910 100644 (file)
@@ -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;
+}
index c0adf93..c58944f 100644 (file)
@@ -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);