jcs's openbsd hax
openbsd
1/* $OpenBSD: parser.c,v 1.139 2025/11/04 15:30:50 claudio Exp $ */
2
3/*
4 * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
5 * Copyright (c) 2016 Job Snijders <job@instituut.net>
6 * Copyright (c) 2016 Peter Hessler <phessler@openbsd.org>
7 *
8 * Permission to use, copy, modify, and distribute this software for any
9 * purpose with or without fee is hereby granted, provided that the above
10 * copyright notice and this permission notice appear in all copies.
11 *
12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 */
20
21#include <sys/types.h>
22#include <sys/socket.h>
23#include <arpa/inet.h>
24
25#include <endian.h>
26#include <err.h>
27#include <errno.h>
28#include <fcntl.h>
29#include <limits.h>
30#include <netdb.h>
31#include <stdio.h>
32#include <stdlib.h>
33#include <string.h>
34#include <unistd.h>
35
36#include "parser.h"
37
38enum token_type {
39 ENDTOKEN,
40 NOTOKEN,
41 ANYTOKEN,
42 KEYWORD,
43 ADDRESS,
44 PEERADDRESS,
45 FLAG,
46 ASNUM,
47 ASTYPE,
48 PREFIX,
49 PEERDESC,
50 GROUPDESC,
51 RIBNAME,
52 COMMUNICATION,
53 COMMUNITY,
54 EXTCOMMUNITY,
55 LRGCOMMUNITY,
56 LOCALPREF,
57 MED,
58 NEXTHOP,
59 PFTABLE,
60 PREPNBR,
61 PREPSELF,
62 WEIGHT,
63 RD,
64 FAMILY,
65 RTABLE,
66 FILENAME,
67 PATHID,
68 FLOW_PROTO,
69 FLOW_SRC,
70 FLOW_DST,
71 FLOW_SRCPORT,
72 FLOW_DSTPORT,
73 FLOW_ICMPTYPE,
74 FLOW_ICMPCODE,
75 FLOW_LENGTH,
76 FLOW_DSCP,
77 FLOW_FLAGS,
78 FLOW_FRAGS,
79};
80
81struct token {
82 enum token_type type;
83 const char *keyword;
84 int value;
85 const struct token *next;
86};
87
88static const struct token *prevtable;
89
90static const struct token t_main[];
91static const struct token t_show[];
92static const struct token t_show_summary[];
93static const struct token t_show_fib[];
94static const struct token t_show_rib[];
95static const struct token t_show_avs[];
96static const struct token t_show_ovs[];
97static const struct token t_show_mrt[];
98static const struct token t_show_mrt_file[];
99static const struct token t_show_rib_neigh[];
100static const struct token t_show_mrt_neigh[];
101static const struct token t_show_rib_rib[];
102static const struct token t_show_neighbor[];
103static const struct token t_show_neighbor_modifiers[];
104static const struct token t_fib[];
105static const struct token t_neighbor[];
106static const struct token t_neighbor_modifiers[];
107static const struct token t_show_rib_as[];
108static const struct token t_show_mrt_as[];
109static const struct token t_show_prefix[];
110static const struct token t_show_ip[];
111static const struct token t_network[];
112static const struct token t_flowspec[];
113static const struct token t_flowfamily[];
114static const struct token t_flowrule[];
115static const struct token t_flowsrc[];
116static const struct token t_flowdst[];
117static const struct token t_flowsrcport[];
118static const struct token t_flowdstport[];
119static const struct token t_flowicmp[];
120static const struct token t_bulk[];
121static const struct token t_network_show[];
122static const struct token t_prefix[];
123static const struct token t_set[];
124static const struct token t_nexthop[];
125static const struct token t_pftable[];
126static const struct token t_log[];
127static const struct token t_communication[];
128
129static const struct token t_main[] = {
130 { KEYWORD, "fib", FIB, t_fib},
131 { KEYWORD, "flowspec", NONE, t_flowspec},
132 { KEYWORD, "log", NONE, t_log},
133 { KEYWORD, "neighbor", NEIGHBOR, t_neighbor},
134 { KEYWORD, "network", NONE, t_network},
135 { KEYWORD, "reload", RELOAD, t_communication},
136 { KEYWORD, "show", SHOW, t_show},
137 { ENDTOKEN, "", NONE, NULL}
138};
139
140static const struct token t_show[] = {
141 { NOTOKEN, "", NONE, NULL},
142 { KEYWORD, "fib", SHOW_FIB, t_show_fib},
143 { KEYWORD, "flowspec", FLOWSPEC_SHOW, t_network_show},
144 { KEYWORD, "interfaces", SHOW_INTERFACE, NULL},
145 { KEYWORD, "ip", NONE, t_show_ip},
146 { KEYWORD, "metrics", SHOW_METRICS, NULL},
147 { KEYWORD, "mrt", SHOW_MRT, t_show_mrt},
148 { KEYWORD, "neighbor", SHOW_NEIGHBOR, t_show_neighbor},
149 { KEYWORD, "network", NETWORK_SHOW, t_network_show},
150 { KEYWORD, "nexthop", SHOW_NEXTHOP, NULL},
151 { KEYWORD, "rib", SHOW_RIB, t_show_rib},
152 { KEYWORD, "rtr", SHOW_RTR, NULL},
153 { KEYWORD, "sets", SHOW_SET, NULL},
154 { KEYWORD, "summary", SHOW_SUMMARY, t_show_summary},
155 { KEYWORD, "tables", SHOW_FIB_TABLES, NULL},
156 { ENDTOKEN, "", NONE, NULL}
157};
158
159static const struct token t_show_summary[] = {
160 { NOTOKEN, "", NONE, NULL},
161 { KEYWORD, "terse", SHOW_SUMMARY_TERSE, NULL},
162 { ENDTOKEN, "", NONE, NULL}
163};
164
165static const struct token t_show_fib[] = {
166 { NOTOKEN, "", NONE, NULL},
167 { FLAG, "bgp", F_BGPD, t_show_fib},
168 { FLAG, "connected", F_CONNECTED, t_show_fib},
169 { FLAG, "nexthop", F_NEXTHOP, t_show_fib},
170 { FLAG, "static", F_STATIC, t_show_fib},
171 { RTABLE, "table", NONE, t_show_fib},
172 { FAMILY, "", NONE, t_show_fib},
173 { ADDRESS, "", NONE, NULL},
174 { ENDTOKEN, "", NONE, NULL}
175};
176
177static const struct token t_show_rib[] = {
178 { NOTOKEN, "", NONE, NULL},
179 { ASTYPE, "as", AS_ALL, t_show_rib_as},
180 { KEYWORD, "avs", NONE, t_show_avs},
181 { FLAG, "best", F_CTL_BEST, t_show_rib},
182 { COMMUNITY, "community", NONE, t_show_rib},
183 { FLAG, "detail", F_CTL_DETAIL, t_show_rib},
184 { FLAG, "disqualified", F_CTL_INELIGIBLE, t_show_rib},
185 { ASTYPE, "empty-as", AS_EMPTY, t_show_rib},
186 { FLAG, "error", F_CTL_INVALID, t_show_rib},
187 { EXTCOMMUNITY, "ext-community", NONE, t_show_rib},
188 { FLAG, "filtered", F_CTL_FILTERED, t_show_rib},
189 { FLAG, "in", F_CTL_ADJ_IN, t_show_rib},
190 { LRGCOMMUNITY, "large-community", NONE, t_show_rib},
191 { FLAG, "leaked", F_CTL_LEAKED, t_show_rib},
192 { KEYWORD, "memory", SHOW_RIB_MEM, NULL},
193 { KEYWORD, "neighbor", NONE, t_show_rib_neigh},
194 { FLAG, "out", F_CTL_ADJ_OUT, t_show_rib},
195 { KEYWORD, "ovs", NONE, t_show_ovs},
196 { PATHID, "path-id", NONE, t_show_rib},
197 { ASTYPE, "peer-as", AS_PEER, t_show_rib_as},
198 { FLAG, "selected", F_CTL_BEST, t_show_rib},
199 { ASTYPE, "source-as", AS_SOURCE, t_show_rib_as},
200 { FLAG, "ssv", F_CTL_SSV, t_show_rib},
201 { KEYWORD, "summary", SHOW_SUMMARY, t_show_summary},
202 { KEYWORD, "table", NONE, t_show_rib_rib},
203 { ASTYPE, "transit-as", AS_TRANSIT, t_show_rib_as},
204 { FAMILY, "", NONE, t_show_rib},
205 { PREFIX, "", NONE, t_show_prefix},
206 { ENDTOKEN, "", NONE, NULL}
207};
208
209static const struct token t_show_avs[] = {
210 { FLAG, "invalid", F_CTL_AVS_INVALID, t_show_rib},
211 { FLAG, "unknown", F_CTL_AVS_UNKNOWN, t_show_rib},
212 { FLAG, "valid" , F_CTL_AVS_VALID, t_show_rib},
213 { ENDTOKEN, "", NONE, NULL}
214};
215
216static const struct token t_show_ovs[] = {
217 { FLAG, "invalid", F_CTL_OVS_INVALID, t_show_rib},
218 { FLAG, "not-found", F_CTL_OVS_NOTFOUND, t_show_rib},
219 { FLAG, "valid" , F_CTL_OVS_VALID, t_show_rib},
220 { ENDTOKEN, "", NONE, NULL}
221};
222
223static const struct token t_show_mrt[] = {
224 { NOTOKEN, "", NONE, NULL},
225 { ASTYPE, "as", AS_ALL, t_show_mrt_as},
226 { FLAG, "detail", F_CTL_DETAIL, t_show_mrt},
227 { ASTYPE, "empty-as", AS_EMPTY, t_show_mrt},
228 { KEYWORD, "file", NONE, t_show_mrt_file},
229 { KEYWORD, "neighbor", NONE, t_show_mrt_neigh},
230 { ASTYPE, "peer-as", AS_PEER, t_show_mrt_as},
231 { FLAG, "peers", F_CTL_NEIGHBORS,t_show_mrt},
232 { ASTYPE, "source-as", AS_SOURCE, t_show_mrt_as},
233 { FLAG, "ssv", F_CTL_SSV, t_show_mrt},
234 { ASTYPE, "transit-as", AS_TRANSIT, t_show_mrt_as},
235 { FAMILY, "", NONE, t_show_mrt},
236 { PREFIX, "", NONE, t_show_prefix},
237 { ENDTOKEN, "", NONE, NULL}
238};
239
240static const struct token t_show_mrt_file[] = {
241 { FILENAME, "", NONE, t_show_mrt},
242 { ENDTOKEN, "", NONE, NULL}
243};
244
245static const struct token t_show_rib_neigh_group[] = {
246 { GROUPDESC, "", NONE, t_show_rib},
247 { ENDTOKEN, "", NONE, NULL}
248};
249
250static const struct token t_show_rib_neigh[] = {
251 { KEYWORD, "group", NONE, t_show_rib_neigh_group},
252 { PEERADDRESS, "", NONE, t_show_rib},
253 { PEERDESC, "", NONE, t_show_rib},
254 { ENDTOKEN, "", NONE, NULL}
255};
256
257static const struct token t_show_mrt_neigh[] = {
258 { PEERADDRESS, "", NONE, t_show_mrt},
259 { ENDTOKEN, "", NONE, NULL}
260};
261
262static const struct token t_show_rib_rib[] = {
263 { RIBNAME, "", NONE, t_show_rib},
264 { ENDTOKEN, "", NONE, NULL}
265};
266
267static const struct token t_show_neighbor_modifiers[] = {
268 { NOTOKEN, "", NONE, NULL},
269 { KEYWORD, "messages", SHOW_NEIGHBOR, NULL},
270 { KEYWORD, "terse", SHOW_NEIGHBOR_TERSE, NULL},
271 { KEYWORD, "timers", SHOW_NEIGHBOR_TIMERS, NULL},
272 { ENDTOKEN, "", NONE, NULL}
273};
274
275static const struct token t_show_neighbor_group[] = {
276 { GROUPDESC, "", NONE, t_show_neighbor_modifiers},
277 { ENDTOKEN, "", NONE, NULL}
278};
279
280static const struct token t_show_neighbor[] = {
281 { NOTOKEN, "", NONE, NULL},
282 { KEYWORD, "group", NONE, t_show_neighbor_group},
283 { PEERADDRESS, "", NONE, t_show_neighbor_modifiers},
284 { PEERDESC, "", NONE, t_show_neighbor_modifiers},
285 { ENDTOKEN, "", NONE, NULL}
286};
287
288static const struct token t_fib[] = {
289 { KEYWORD, "couple", FIB_COUPLE, NULL},
290 { KEYWORD, "decouple", FIB_DECOUPLE, NULL},
291 { RTABLE, "table", NONE, t_fib},
292 { ENDTOKEN, "", NONE, NULL}
293};
294
295static const struct token t_neighbor_group[] = {
296 { GROUPDESC, "", NONE, t_neighbor_modifiers},
297 { ENDTOKEN, "", NONE, NULL}
298};
299
300static const struct token t_neighbor[] = {
301 { KEYWORD, "group", NONE, t_neighbor_group},
302 { PEERADDRESS, "", NONE, t_neighbor_modifiers},
303 { PEERDESC, "", NONE, t_neighbor_modifiers},
304 { ENDTOKEN, "", NONE, NULL}
305};
306
307static const struct token t_communication[] = {
308 { NOTOKEN, "", NONE, NULL},
309 { COMMUNICATION, "", NONE, NULL},
310 { ENDTOKEN, "", NONE, NULL}
311};
312
313static const struct token t_neighbor_modifiers[] = {
314 { KEYWORD, "clear", NEIGHBOR_CLEAR, t_communication},
315 { KEYWORD, "destroy", NEIGHBOR_DESTROY, NULL},
316 { KEYWORD, "down", NEIGHBOR_DOWN, t_communication},
317 { KEYWORD, "refresh", NEIGHBOR_RREFRESH, NULL},
318 { KEYWORD, "up", NEIGHBOR_UP, NULL},
319 { ENDTOKEN, "", NONE, NULL}
320};
321
322static const struct token t_show_rib_as[] = {
323 { ASNUM, "", NONE, t_show_rib},
324 { ENDTOKEN, "", NONE, NULL}
325};
326
327static const struct token t_show_mrt_as[] = {
328 { ASNUM, "", NONE, t_show_mrt},
329 { ENDTOKEN, "", NONE, NULL}
330};
331
332static const struct token t_show_prefix[] = {
333 { FLAG, "all", F_LONGER, t_show_rib},
334 { FLAG, "longer-prefixes", F_LONGER, t_show_rib},
335 { FLAG, "or-longer", F_LONGER, t_show_rib},
336 { FLAG, "or-shorter", F_SHORTER, t_show_rib},
337 { ANYTOKEN, "", NONE, t_show_rib},
338 { ENDTOKEN, "", NONE, NULL}
339};
340
341static const struct token t_show_ip[] = {
342 { KEYWORD, "bgp", SHOW_RIB, t_show_rib},
343 { ENDTOKEN, "", NONE, NULL}
344};
345
346static const struct token t_network[] = {
347 { KEYWORD, "add", NETWORK_ADD, t_prefix},
348 { KEYWORD, "bulk", NONE, t_bulk},
349 { KEYWORD, "delete", NETWORK_REMOVE, t_prefix},
350 { KEYWORD, "flush", NETWORK_FLUSH, NULL},
351 { KEYWORD, "mrt", NETWORK_MRT, t_show_mrt},
352 { KEYWORD, "show", NETWORK_SHOW, t_network_show},
353 { ENDTOKEN, "", NONE, NULL}
354};
355
356static const struct token t_flowspec[] = {
357 { KEYWORD, "add", FLOWSPEC_ADD, t_flowfamily},
358 { KEYWORD, "delete", FLOWSPEC_REMOVE,t_flowfamily},
359 { KEYWORD, "flush", FLOWSPEC_FLUSH, NULL},
360 { KEYWORD, "show", FLOWSPEC_SHOW, t_network_show},
361 { ENDTOKEN, "", NONE, NULL}
362};
363
364static const struct token t_flowfamily[] = {
365 { FAMILY, "", NONE, t_flowrule},
366 { ENDTOKEN, "", NONE, NULL}
367};
368
369static const struct token t_flowrule[] = {
370 { NOTOKEN, "", NONE, NULL},
371 { FLOW_FLAGS, "flags", NONE, t_flowrule},
372 { FLOW_FRAGS, "fragment", NONE, t_flowrule},
373 { KEYWORD, "from", NONE, t_flowsrc},
374 { FLOW_ICMPTYPE,"icmp-type", NONE, t_flowicmp},
375 { FLOW_LENGTH, "length", NONE, t_flowrule},
376 { FLOW_PROTO, "proto", NONE, t_flowrule},
377 { KEYWORD, "set", NONE, t_set},
378 { KEYWORD, "to", NONE, t_flowdst},
379 { FLOW_DSCP, "dscp", NONE, t_flowrule},
380 { ENDTOKEN, "", NONE, NULL}
381};
382
383static const struct token t_flowsrc[] = {
384 { KEYWORD, "any", NONE, t_flowsrcport},
385 { FLOW_SRC, "", NONE, t_flowsrcport},
386 { ENDTOKEN, "", NONE, NULL}
387};
388
389static const struct token t_flowdst[] = {
390 { KEYWORD, "any", NONE, t_flowdstport},
391 { FLOW_DST, "", NONE, t_flowdstport},
392 { ENDTOKEN, "", NONE, NULL}
393};
394
395static const struct token t_flowsrcport[] = {
396 { FLOW_SRCPORT, "port", NONE, t_flowrule},
397 { ANYTOKEN, "", NONE, t_flowrule},
398 { ENDTOKEN, "", NONE, NULL}
399};
400
401static const struct token t_flowdstport[] = {
402 { FLOW_DSTPORT, "port", NONE, t_flowrule},
403 { ANYTOKEN, "", NONE, t_flowrule},
404 { ENDTOKEN, "", NONE, NULL}
405};
406
407static const struct token t_flowicmp[] = {
408 { FLOW_ICMPCODE,"code", NONE, t_flowrule},
409 { ANYTOKEN, "", NONE, t_flowrule},
410 { ENDTOKEN, "", NONE, NULL}
411};
412
413static const struct token t_bulk[] = {
414 { KEYWORD, "add", NETWORK_BULK_ADD, t_set},
415 { KEYWORD, "delete", NETWORK_BULK_REMOVE, NULL},
416 { ENDTOKEN, "", NONE, NULL}
417};
418
419static const struct token t_prefix[] = {
420 { PREFIX, "", NONE, t_set},
421 { ENDTOKEN, "", NONE, NULL}
422};
423
424static const struct token t_network_show[] = {
425 { NOTOKEN, "", NONE, NULL},
426 { FAMILY, "", NONE, NULL},
427 { ENDTOKEN, "", NONE, NULL}
428};
429
430static const struct token t_rd[] = {
431 { RD, "", NONE, t_set},
432 { ENDTOKEN, "", NONE, NULL}
433};
434
435static const struct token t_set[] = {
436 { NOTOKEN, "", NONE, NULL},
437 { COMMUNITY, "community", NONE, t_set},
438 { EXTCOMMUNITY, "ext-community", NONE, t_set},
439 { LRGCOMMUNITY, "large-community", NONE, t_set},
440 { LOCALPREF, "localpref", NONE, t_set},
441 { MED, "med", NONE, t_set},
442 { MED, "metric", NONE, t_set},
443 { KEYWORD, "nexthop", NONE, t_nexthop},
444 { KEYWORD, "pftable", NONE, t_pftable},
445 { PREPNBR, "prepend-neighbor", NONE, t_set},
446 { PREPSELF, "prepend-self", NONE, t_set},
447 { KEYWORD, "rd", NONE, t_rd},
448 { WEIGHT, "weight", NONE, t_set},
449 { ENDTOKEN, "", NONE, NULL}
450};
451
452static const struct token t_nexthop[] = {
453 { NEXTHOP, "", NONE, t_set},
454 { ENDTOKEN, "", NONE, NULL}
455};
456
457static const struct token t_pftable[] = {
458 { PFTABLE, "", NONE, t_set},
459 { ENDTOKEN, "", NONE, NULL}
460};
461
462static const struct token t_log[] = {
463 { KEYWORD, "brief", LOG_BRIEF, NULL},
464 { KEYWORD, "verbose", LOG_VERBOSE, NULL},
465 { ENDTOKEN, "", NONE, NULL}
466};
467
468static struct parse_result res;
469
470const struct token *match_token(int, char *[], const struct token [],
471 int *);
472void show_valid_args(const struct token []);
473
474int parse_addr(const char *, struct bgpd_addr *);
475int parse_asnum(const char *, size_t, uint32_t *);
476int parse_number(const char *, struct parse_result *, enum token_type);
477void parsecommunity(struct community *c, char *s);
478void parselargecommunity(struct community *c, char *s);
479void parseextcommunity(struct community *c, const char *t, char *s);
480int parse_nexthop(const char *, struct parse_result *);
481int parse_flow_numop(int, char *[], struct parse_result *, enum token_type);
482
483struct parse_result *
484parse(int argc, char *argv[])
485{
486 const struct token *table = t_main;
487 const struct token *match;
488 int used;
489
490 memset(&res, 0, sizeof(res));
491 res.rtableid = getrtable();
492 res.mrtfd = -1;
493 TAILQ_INIT(&res.set);
494
495 while (argc >= 0) {
496 if ((match = match_token(argc, argv, table, &used)) == NULL) {
497 fprintf(stderr, "valid commands/args:\n");
498 show_valid_args(table);
499 return (NULL);
500 }
501 if (match->type == ANYTOKEN) {
502 if (prevtable == NULL)
503 prevtable = table;
504 table = match->next;
505 continue;
506 }
507
508 argc -= used;
509 argv += used;
510
511 if (match->type == NOTOKEN || match->next == NULL)
512 break;
513 table = match->next;
514 }
515
516 if (argc > 0) {
517 fprintf(stderr, "superfluous argument: %s\n", argv[0]);
518 return (NULL);
519 }
520
521 return (&res);
522}
523
524const struct token *
525match_token(int argc, char *argv[], const struct token table[], int *argsused)
526{
527 u_int i, match;
528 const struct token *t = NULL;
529 struct filter_set *fs;
530 const char *word = argv[0];
531 size_t wordlen = 0;
532
533 *argsused = 1;
534 match = 0;
535 if (word != NULL)
536 wordlen = strlen(word);
537 for (i = 0; table[i].type != ENDTOKEN; i++) {
538 switch (table[i].type) {
539 case NOTOKEN:
540 if (word == NULL || wordlen == 0) {
541 match++;
542 t = &table[i];
543 }
544 break;
545 case ANYTOKEN:
546 /* match anything if nothing else matched before */
547 if (match == 0) {
548 match++;
549 t = &table[i];
550 }
551 break;
552 case KEYWORD:
553 if (word != NULL && strncmp(word, table[i].keyword,
554 wordlen) == 0) {
555 match++;
556 t = &table[i];
557 if (t->value)
558 res.action = t->value;
559 }
560 break;
561 case FLAG:
562 if (word != NULL && strncmp(word, table[i].keyword,
563 wordlen) == 0) {
564 match++;
565 t = &table[i];
566 res.flags |= t->value;
567 }
568 break;
569 case FAMILY:
570 if (word == NULL)
571 break;
572 if (!strcmp(word, "inet") ||
573 !strcasecmp(word, "IPv4")) {
574 match++;
575 t = &table[i];
576 res.aid = AID_INET;
577 }
578 if (!strcmp(word, "inet6") ||
579 !strcasecmp(word, "IPv6")) {
580 match++;
581 t = &table[i];
582 res.aid = AID_INET6;
583 }
584 if (!strcasecmp(word, "VPNv4")) {
585 match++;
586 t = &table[i];
587 res.aid = AID_VPN_IPv4;
588 }
589 if (!strcasecmp(word, "VPNv6")) {
590 match++;
591 t = &table[i];
592 res.aid = AID_VPN_IPv6;
593 }
594 break;
595 case ADDRESS:
596 if (parse_addr(word, &res.addr)) {
597 match++;
598 t = &table[i];
599 }
600 break;
601 case PEERADDRESS:
602 if (parse_addr(word, &res.peeraddr)) {
603 match++;
604 t = &table[i];
605 }
606 break;
607 case FLOW_SRC:
608 if (parse_prefix(word, wordlen, &res.flow.src,
609 &res.flow.srclen)) {
610 match++;
611 t = &table[i];
612 if (res.aid != res.flow.src.aid)
613 errx(1, "wrong address family in "
614 "flowspec rule");
615 }
616 break;
617 case FLOW_DST:
618 if (parse_prefix(word, wordlen, &res.flow.dst,
619 &res.flow.dstlen)) {
620 match++;
621 t = &table[i];
622 if (res.aid != res.flow.dst.aid)
623 errx(1, "wrong address family in "
624 "flowspec rule");
625 }
626 break;
627 case PREFIX:
628 if (parse_prefix(word, wordlen, &res.addr,
629 &res.prefixlen)) {
630 match++;
631 t = &table[i];
632 }
633 break;
634 case ASTYPE:
635 if (word != NULL && strncmp(word, table[i].keyword,
636 wordlen) == 0) {
637 match++;
638 t = &table[i];
639 res.as.type = t->value;
640 }
641 break;
642 case ASNUM:
643 if (parse_asnum(word, wordlen, &res.as.as_min)) {
644 res.as.as_max = res.as.as_min;
645 match++;
646 t = &table[i];
647 }
648 break;
649 case GROUPDESC:
650 res.is_group = 1;
651 /* FALLTHROUGH */
652 case PEERDESC:
653 if (!match && word != NULL && wordlen > 0) {
654 if (strlcpy(res.peerdesc, word,
655 sizeof(res.peerdesc)) >=
656 sizeof(res.peerdesc))
657 errx(1, "neighbor description too "
658 "long");
659 match++;
660 t = &table[i];
661 }
662 break;
663 case RIBNAME:
664 if (!match && word != NULL && wordlen > 0) {
665 if (strlcpy(res.rib, word, sizeof(res.rib)) >=
666 sizeof(res.rib))
667 errx(1, "rib name too long");
668 match++;
669 t = &table[i];
670 }
671 break;
672 case COMMUNICATION:
673 if (!match && word != NULL && wordlen > 0) {
674 if (strlcpy(res.reason, word,
675 sizeof(res.reason)) >=
676 sizeof(res.reason))
677 errx(1, "shutdown reason too long");
678 match++;
679 t = &table[i];
680 }
681 break;
682 case COMMUNITY:
683 if (word != NULL && strncmp(word, table[i].keyword,
684 wordlen) == 0 && argc > 1) {
685 parsecommunity(&res.community, argv[1]);
686 *argsused += 1;
687
688 if ((fs = calloc(1, sizeof(*fs))) == NULL)
689 err(1, NULL);
690 fs->type = ACTION_SET_COMMUNITY;
691 fs->action.community = res.community;
692 TAILQ_INSERT_TAIL(&res.set, fs, entry);
693
694 match++;
695 t = &table[i];
696 }
697 break;
698 case LRGCOMMUNITY:
699 if (word != NULL && strncmp(word, table[i].keyword,
700 wordlen) == 0 && argc > 1) {
701 parselargecommunity(&res.community, argv[1]);
702 *argsused += 1;
703
704 if ((fs = calloc(1, sizeof(*fs))) == NULL)
705 err(1, NULL);
706 fs->type = ACTION_SET_COMMUNITY;
707 fs->action.community = res.community;
708 TAILQ_INSERT_TAIL(&res.set, fs, entry);
709
710 match++;
711 t = &table[i];
712 }
713 break;
714 case EXTCOMMUNITY:
715 if (word != NULL && strncmp(word, table[i].keyword,
716 wordlen) == 0 && argc > 2) {
717 parseextcommunity(&res.community,
718 argv[1], argv[2]);
719 *argsused += 2;
720
721 if ((fs = calloc(1, sizeof(*fs))) == NULL)
722 err(1, NULL);
723 fs->type = ACTION_SET_COMMUNITY;
724 fs->action.community = res.community;
725 TAILQ_INSERT_TAIL(&res.set, fs, entry);
726
727 match++;
728 t = &table[i];
729 }
730 break;
731 case RD:
732 if (word != NULL && wordlen > 0) {
733 char *p = strdup(word);
734 struct community ext = { 0 };
735 uint64_t rd;
736
737 if (p == NULL)
738 err(1, NULL);
739 parseextcommunity(&ext, "rt", p);
740 free(p);
741
742 switch (ext.data3 >> 8) {
743 case EXT_COMMUNITY_TRANS_TWO_AS:
744 rd = (0ULL << 48);
745 rd |= ((uint64_t)ext.data1 & 0xffff)
746 << 32;
747 rd |= (uint64_t)ext.data2;
748 break;
749 case EXT_COMMUNITY_TRANS_IPV4:
750 rd = (1ULL << 48);
751 rd |= (uint64_t)ext.data1 << 16;
752 rd |= (uint64_t)ext.data2 & 0xffff;
753 break;
754 case EXT_COMMUNITY_TRANS_FOUR_AS:
755 rd = (2ULL << 48);
756 rd |= (uint64_t)ext.data1 << 16;
757 rd |= (uint64_t)ext.data2 & 0xffff;
758 break;
759 default:
760 errx(1, "bad encoding of rd");
761 }
762 res.rd = htobe64(rd);
763 match++;
764 t = &table[i];
765 }
766 break;
767 case LOCALPREF:
768 case MED:
769 case PREPNBR:
770 case PREPSELF:
771 case WEIGHT:
772 case RTABLE:
773 case PATHID:
774 if (word != NULL && strncmp(word, table[i].keyword,
775 wordlen) == 0 && argc > 1 &&
776 parse_number(argv[1], &res, table[i].type)) {
777 *argsused += 1;
778 match++;
779 t = &table[i];
780 }
781 break;
782 case NEXTHOP:
783 if (word != NULL && wordlen > 0 &&
784 parse_nexthop(word, &res)) {
785 match++;
786 t = &table[i];
787 }
788 break;
789 case PFTABLE:
790 if (word != NULL && wordlen > 0) {
791 if ((fs = calloc(1,
792 sizeof(struct filter_set))) == NULL)
793 err(1, NULL);
794 if (strlcpy(fs->action.pftable, word,
795 sizeof(fs->action.pftable)) >=
796 sizeof(fs->action.pftable))
797 errx(1, "pftable name too long");
798 TAILQ_INSERT_TAIL(&res.set, fs, entry);
799 match++;
800 t = &table[i];
801 }
802 break;
803 case FILENAME:
804 if (word != NULL && wordlen > 0) {
805 if (res.mrtfd != -1)
806 errx(1, "mrt file already set");
807 if ((res.mrtfd = open(word, O_RDONLY)) == -1) {
808 /*
809 * ignore error if path has no / and
810 * does not exist. In hope to print
811 * usage.
812 */
813 if (errno == ENOENT &&
814 !strchr(word, '/'))
815 break;
816 err(1, "mrt open(%s)", word);
817 }
818 match++;
819 t = &table[i];
820 }
821 break;
822 case FLOW_SRCPORT:
823 case FLOW_DSTPORT:
824 case FLOW_PROTO:
825 case FLOW_ICMPTYPE:
826 case FLOW_ICMPCODE:
827 case FLOW_LENGTH:
828 case FLOW_DSCP:
829 if (word != NULL && strncmp(word, table[i].keyword,
830 wordlen) == 0 && argc > 1) {
831 *argsused += parse_flow_numop(argc, argv, &res,
832 table[i].type);
833
834 match++;
835 t = &table[i];
836 }
837 break;
838 case FLOW_FLAGS:
839 case FLOW_FRAGS:
840 if (word != NULL && strncmp(word, table[i].keyword,
841 wordlen) == 0) {
842 errx(1, "%s not yet implemented", word);
843 }
844 break;
845 case ENDTOKEN:
846 break;
847 }
848 }
849
850 if (match != 1) {
851 if (word == NULL)
852 fprintf(stderr, "missing argument:\n");
853 else if (match > 1)
854 fprintf(stderr, "ambiguous argument: %s\n", word);
855 else if (match < 1)
856 fprintf(stderr, "unknown argument: %s\n", word);
857 return (NULL);
858 }
859
860 return (t);
861}
862
863void
864show_valid_args(const struct token table[])
865{
866 int i;
867
868 if (prevtable != NULL) {
869 const struct token *t = prevtable;
870 prevtable = NULL;
871 show_valid_args(t);
872 fprintf(stderr, "or any of\n");
873 }
874
875 for (i = 0; table[i].type != ENDTOKEN; i++) {
876 switch (table[i].type) {
877 case NOTOKEN:
878 fprintf(stderr, " <cr>\n");
879 break;
880 case ANYTOKEN:
881 break;
882 case KEYWORD:
883 case FLAG:
884 case ASTYPE:
885 fprintf(stderr, " %s\n", table[i].keyword);
886 break;
887 case ADDRESS:
888 case PEERADDRESS:
889 fprintf(stderr, " <address>\n");
890 break;
891 case PREFIX:
892 case FLOW_SRC:
893 case FLOW_DST:
894 fprintf(stderr, " <address>[/<len>]\n");
895 break;
896 case ASNUM:
897 fprintf(stderr, " <asnum>\n");
898 break;
899 case GROUPDESC:
900 case PEERDESC:
901 fprintf(stderr, " <neighbor description>\n");
902 break;
903 case RIBNAME:
904 fprintf(stderr, " <rib name>\n");
905 break;
906 case COMMUNICATION:
907 fprintf(stderr, " <reason>\n");
908 break;
909 case COMMUNITY:
910 fprintf(stderr, " %s <community>\n",
911 table[i].keyword);
912 break;
913 case LRGCOMMUNITY:
914 fprintf(stderr, " %s <large-community>\n",
915 table[i].keyword);
916 break;
917 case EXTCOMMUNITY:
918 fprintf(stderr, " %s <extended-community>\n",
919 table[i].keyword);
920 break;
921 case RD:
922 fprintf(stderr, " <route-distinguisher>\n");
923 break;
924 case LOCALPREF:
925 case MED:
926 case PREPNBR:
927 case PREPSELF:
928 case WEIGHT:
929 case RTABLE:
930 case PATHID:
931 fprintf(stderr, " %s <number>\n", table[i].keyword);
932 break;
933 case NEXTHOP:
934 fprintf(stderr, " <address>\n");
935 break;
936 case PFTABLE:
937 fprintf(stderr, " <pftable>\n");
938 break;
939 case FAMILY:
940 fprintf(stderr, " [ inet | inet6 | IPv4 | IPv6 | "
941 "VPNv4 | VPNv6 ]\n");
942 break;
943 case FILENAME:
944 fprintf(stderr, " <filename>\n");
945 break;
946 case FLOW_SRCPORT:
947 case FLOW_DSTPORT:
948 case FLOW_PROTO:
949 case FLOW_ICMPTYPE:
950 case FLOW_ICMPCODE:
951 case FLOW_LENGTH:
952 case FLOW_DSCP:
953 fprintf(stderr, " %s <numberspec>\n",
954 table[i].keyword);
955 break;
956 case FLOW_FLAGS:
957 case FLOW_FRAGS:
958 fprintf(stderr, " %s <flagspec>\n",
959 table[i].keyword);
960 break;
961 case ENDTOKEN:
962 break;
963 }
964 }
965}
966
967int
968parse_addr(const char *word, struct bgpd_addr *addr)
969{
970 struct in_addr ina;
971 struct addrinfo hints, *r;
972
973 if (word == NULL)
974 return (0);
975
976 memset(&ina, 0, sizeof(ina));
977
978 if (inet_net_pton(AF_INET, word, &ina, sizeof(ina)) != -1) {
979 memset(addr, 0, sizeof(*addr));
980 addr->aid = AID_INET;
981 addr->v4 = ina;
982 return (1);
983 }
984
985 memset(&hints, 0, sizeof(hints));
986 hints.ai_family = AF_INET6;
987 hints.ai_socktype = SOCK_DGRAM; /*dummy*/
988 hints.ai_flags = AI_NUMERICHOST;
989 if (getaddrinfo(word, "0", &hints, &r) == 0) {
990 sa2addr(r->ai_addr, addr, NULL);
991 freeaddrinfo(r);
992 return (1);
993 }
994
995 return (0);
996}
997
998int
999parse_prefix(const char *word, size_t wordlen, struct bgpd_addr *addr,
1000 uint8_t *prefixlen)
1001{
1002 struct bgpd_addr tmp;
1003 char *p, *ps;
1004 const char *errstr;
1005 int mask = -1;
1006
1007 if (word == NULL)
1008 return (0);
1009
1010 memset(&tmp, 0, sizeof(tmp));
1011
1012 if ((p = strrchr(word, '/')) != NULL) {
1013 size_t plen = strlen(p);
1014 mask = strtonum(p + 1, 0, 128, &errstr);
1015 if (errstr)
1016 errx(1, "netmask %s", errstr);
1017
1018 if ((ps = malloc(wordlen - plen + 1)) == NULL)
1019 err(1, "parse_prefix: malloc");
1020 strlcpy(ps, word, wordlen - plen + 1);
1021
1022 if (parse_addr(ps, &tmp) == 0) {
1023 free(ps);
1024 return (0);
1025 }
1026
1027 free(ps);
1028 } else
1029 if (parse_addr(word, &tmp) == 0)
1030 return (0);
1031
1032 switch (tmp.aid) {
1033 case AID_INET:
1034 if (mask == -1)
1035 mask = 32;
1036 if (mask > 32)
1037 errx(1, "invalid netmask: too large");
1038 break;
1039 case AID_INET6:
1040 if (mask == -1)
1041 mask = 128;
1042 break;
1043 default:
1044 return (0);
1045 }
1046
1047 applymask(addr, &tmp, mask);
1048 *prefixlen = mask;
1049 return (1);
1050}
1051
1052int
1053parse_asnum(const char *word, size_t wordlen, uint32_t *asnum)
1054{
1055 const char *errstr;
1056 char *dot, *parseword;
1057 uint32_t uval, uvalh = 0;
1058
1059 if (word == NULL)
1060 return (0);
1061
1062 if (wordlen < 1 || word[0] < '0' || word[0] > '9')
1063 return (0);
1064
1065 parseword = strdup(word);
1066 if ((dot = strchr(parseword, '.')) != NULL) {
1067 *dot++ = '\0';
1068 uvalh = strtonum(parseword, 0, USHRT_MAX, &errstr);
1069 if (errstr)
1070 errx(1, "AS number is %s: %s", errstr, word);
1071 uval = strtonum(dot, 0, USHRT_MAX, &errstr);
1072 if (errstr)
1073 errx(1, "AS number is %s: %s", errstr, word);
1074 } else {
1075 uval = strtonum(parseword, 0, UINT_MAX, &errstr);
1076 if (errstr)
1077 errx(1, "AS number is %s: %s", errstr, word);
1078 }
1079
1080 free(parseword);
1081 *asnum = uval | (uvalh << 16);
1082 return (1);
1083}
1084
1085int
1086parse_number(const char *word, struct parse_result *r, enum token_type type)
1087{
1088 struct filter_set *fs;
1089 const char *errstr;
1090 u_int uval;
1091
1092 if (word == NULL)
1093 return (0);
1094
1095 uval = strtonum(word, 0, UINT_MAX, &errstr);
1096 if (errstr)
1097 errx(1, "number is %s: %s", errstr, word);
1098
1099 /* number was parseable */
1100 switch (type) {
1101 case RTABLE:
1102 r->rtableid = uval;
1103 return (1);
1104 case PATHID:
1105 r->pathid = uval;
1106 r->flags |= F_CTL_HAS_PATHID;
1107 return (1);
1108 default:
1109 break;
1110 }
1111
1112 if ((fs = calloc(1, sizeof(struct filter_set))) == NULL)
1113 err(1, NULL);
1114 switch (type) {
1115 case LOCALPREF:
1116 fs->type = ACTION_SET_LOCALPREF;
1117 fs->action.metric = uval;
1118 break;
1119 case MED:
1120 fs->type = ACTION_SET_MED;
1121 fs->action.metric = uval;
1122 break;
1123 case PREPNBR:
1124 if (uval > 128) {
1125 free(fs);
1126 return (0);
1127 }
1128 fs->type = ACTION_SET_PREPEND_PEER;
1129 fs->action.prepend = uval;
1130 break;
1131 case PREPSELF:
1132 if (uval > 128) {
1133 free(fs);
1134 return (0);
1135 }
1136 fs->type = ACTION_SET_PREPEND_SELF;
1137 fs->action.prepend = uval;
1138 break;
1139 case WEIGHT:
1140 fs->type = ACTION_SET_WEIGHT;
1141 fs->action.metric = uval;
1142 break;
1143 default:
1144 errx(1, "king bula sez bad things happen");
1145 }
1146
1147 TAILQ_INSERT_TAIL(&r->set, fs, entry);
1148 return (1);
1149}
1150
1151static void
1152getcommunity(char *s, int large, uint32_t *val, uint32_t *flag)
1153{
1154 long long max = USHRT_MAX;
1155 const char *errstr;
1156
1157 *flag = 0;
1158 *val = 0;
1159 if (strcmp(s, "*") == 0) {
1160 *flag = COMMUNITY_ANY;
1161 return;
1162 } else if (strcmp(s, "neighbor-as") == 0) {
1163 *flag = COMMUNITY_NEIGHBOR_AS;
1164 return;
1165 } else if (strcmp(s, "local-as") == 0) {
1166 *flag = COMMUNITY_LOCAL_AS;
1167 return;
1168 }
1169 if (large)
1170 max = UINT_MAX;
1171 *val = strtonum(s, 0, max, &errstr);
1172 if (errstr)
1173 errx(1, "Community %s is %s (max: %llu)", s, errstr, max);
1174}
1175
1176static void
1177setcommunity(struct community *c, uint32_t as, uint32_t data,
1178 uint32_t asflag, uint32_t dataflag)
1179{
1180 c->flags = COMMUNITY_TYPE_BASIC;
1181 c->flags |= asflag << 8;
1182 c->flags |= dataflag << 16;
1183 c->data1 = as;
1184 c->data2 = data;
1185 c->data3 = 0;
1186}
1187
1188void
1189parsecommunity(struct community *c, char *s)
1190{
1191 char *p;
1192 uint32_t as, data, asflag, dataflag;
1193
1194 /* Well-known communities */
1195 if (strcasecmp(s, "GRACEFUL_SHUTDOWN") == 0) {
1196 setcommunity(c, COMMUNITY_WELLKNOWN,
1197 COMMUNITY_GRACEFUL_SHUTDOWN, 0, 0);
1198 return;
1199 } else if (strcasecmp(s, "NO_EXPORT") == 0) {
1200 setcommunity(c, COMMUNITY_WELLKNOWN,
1201 COMMUNITY_NO_EXPORT, 0, 0);
1202 return;
1203 } else if (strcasecmp(s, "NO_ADVERTISE") == 0) {
1204 setcommunity(c, COMMUNITY_WELLKNOWN,
1205 COMMUNITY_NO_ADVERTISE, 0, 0);
1206 return;
1207 } else if (strcasecmp(s, "NO_EXPORT_SUBCONFED") == 0) {
1208 setcommunity(c, COMMUNITY_WELLKNOWN,
1209 COMMUNITY_NO_EXPSUBCONFED, 0, 0);
1210 return;
1211 } else if (strcasecmp(s, "NO_PEER") == 0) {
1212 setcommunity(c, COMMUNITY_WELLKNOWN,
1213 COMMUNITY_NO_PEER, 0, 0);
1214 return;
1215 } else if (strcasecmp(s, "BLACKHOLE") == 0) {
1216 setcommunity(c, COMMUNITY_WELLKNOWN,
1217 COMMUNITY_BLACKHOLE, 0, 0);
1218 return;
1219 }
1220
1221 if ((p = strchr(s, ':')) == NULL)
1222 errx(1, "Bad community syntax");
1223 *p++ = 0;
1224
1225 getcommunity(s, 0, &as, &asflag);
1226 getcommunity(p, 0, &data, &dataflag);
1227 setcommunity(c, as, data, asflag, dataflag);
1228}
1229
1230void
1231parselargecommunity(struct community *c, char *s)
1232{
1233 char *p, *q;
1234 uint32_t dflag1, dflag2, dflag3;
1235
1236 if ((p = strchr(s, ':')) == NULL)
1237 errx(1, "Bad community syntax");
1238 *p++ = 0;
1239
1240 if ((q = strchr(p, ':')) == NULL)
1241 errx(1, "Bad community syntax");
1242 *q++ = 0;
1243
1244 getcommunity(s, 1, &c->data1, &dflag1);
1245 getcommunity(p, 1, &c->data2, &dflag2);
1246 getcommunity(q, 1, &c->data3, &dflag3);
1247
1248 c->flags = COMMUNITY_TYPE_LARGE;
1249 c->flags |= dflag1 << 8;
1250 c->flags |= dflag2 << 16;
1251 c->flags |= dflag3 << 24;
1252}
1253
1254static int
1255parsesubtype(const char *name, int *type, int *subtype)
1256{
1257 const struct ext_comm_pairs *cp;
1258 int found = 0;
1259
1260 for (cp = iana_ext_comms; cp->subname != NULL; cp++) {
1261 if (strcmp(name, cp->subname) == 0) {
1262 if (found == 0) {
1263 *type = cp->type;
1264 *subtype = cp->subtype;
1265 }
1266 found++;
1267 }
1268 }
1269 if (found > 1)
1270 *type = -1;
1271 return (found);
1272}
1273
1274static int
1275parseextvalue(int type, char *s, uint32_t *v, uint32_t *flag)
1276{
1277 const char *errstr;
1278 char *p;
1279 struct in_addr ip;
1280 uint32_t uvalh, uval;
1281
1282 if (type != -1) {
1283 /* nothing */
1284 } else if (strcmp(s, "neighbor-as") == 0) {
1285 *flag = COMMUNITY_NEIGHBOR_AS;
1286 *v = 0;
1287 return EXT_COMMUNITY_TRANS_TWO_AS;
1288 } else if (strcmp(s, "local-as") == 0) {
1289 *flag = COMMUNITY_LOCAL_AS;
1290 *v = 0;
1291 return EXT_COMMUNITY_TRANS_TWO_AS;
1292 } else if ((p = strchr(s, '.')) == NULL) {
1293 /* AS_PLAIN number (4 or 2 byte) */
1294 strtonum(s, 0, USHRT_MAX, &errstr);
1295 if (errstr == NULL)
1296 type = EXT_COMMUNITY_TRANS_TWO_AS;
1297 else
1298 type = EXT_COMMUNITY_TRANS_FOUR_AS;
1299 } else if (strchr(p + 1, '.') == NULL) {
1300 /* AS_DOT number (4-byte) */
1301 type = EXT_COMMUNITY_TRANS_FOUR_AS;
1302 } else {
1303 /* more than one dot -> IP address */
1304 type = EXT_COMMUNITY_TRANS_IPV4;
1305 }
1306
1307 switch (type & EXT_COMMUNITY_VALUE) {
1308 case EXT_COMMUNITY_TRANS_TWO_AS:
1309 uval = strtonum(s, 0, USHRT_MAX, &errstr);
1310 if (errstr)
1311 errx(1, "Bad ext-community %s is %s", s, errstr);
1312 *v = uval;
1313 break;
1314 case EXT_COMMUNITY_TRANS_FOUR_AS:
1315 if ((p = strchr(s, '.')) == NULL) {
1316 uval = strtonum(s, 0, UINT_MAX, &errstr);
1317 if (errstr)
1318 errx(1, "Bad ext-community %s is %s", s,
1319 errstr);
1320 *v = uval;
1321 break;
1322 }
1323 *p++ = '\0';
1324 uvalh = strtonum(s, 0, USHRT_MAX, &errstr);
1325 if (errstr)
1326 errx(1, "Bad ext-community %s is %s", s, errstr);
1327 uval = strtonum(p, 0, USHRT_MAX, &errstr);
1328 if (errstr)
1329 errx(1, "Bad ext-community %s is %s", p, errstr);
1330 *v = uval | (uvalh << 16);
1331 break;
1332 case EXT_COMMUNITY_TRANS_IPV4:
1333 if (inet_pton(AF_INET, s, &ip) != 1)
1334 errx(1, "Bad ext-community %s not parseable", s);
1335 *v = ntohl(ip.s_addr);
1336 break;
1337 default:
1338 errx(1, "%s: unexpected type %d", __func__, type);
1339 }
1340 return (type);
1341}
1342
1343void
1344parseextcommunity(struct community *c, const char *t, char *s)
1345{
1346 const struct ext_comm_pairs *cp;
1347 char *p, *ep;
1348 uint64_t ullval;
1349 uint32_t uval, uval2, dflag1 = 0, dflag2 = 0;
1350 int type = 0, subtype = 0;
1351
1352 if (strcmp(t, "*") == 0 && strcmp(s, "*") == 0) {
1353 c->flags = COMMUNITY_TYPE_EXT;
1354 c->flags |= COMMUNITY_ANY << 24;
1355 return;
1356 }
1357 if (parsesubtype(t, &type, &subtype) == 0)
1358 errx(1, "Bad ext-community unknown type");
1359
1360 switch (type) {
1361 case EXT_COMMUNITY_TRANS_TWO_AS:
1362 case EXT_COMMUNITY_TRANS_FOUR_AS:
1363 case EXT_COMMUNITY_TRANS_IPV4:
1364 case EXT_COMMUNITY_GEN_TWO_AS:
1365 case EXT_COMMUNITY_GEN_FOUR_AS:
1366 case EXT_COMMUNITY_GEN_IPV4:
1367 case -1:
1368 if (strcmp(s, "*") == 0) {
1369 dflag1 = COMMUNITY_ANY;
1370 break;
1371 }
1372 if ((p = strchr(s, ':')) == NULL)
1373 errx(1, "Bad ext-community %s", s);
1374 *p++ = '\0';
1375 type = parseextvalue(type, s, &uval, &dflag1);
1376
1377 switch (type) {
1378 case EXT_COMMUNITY_TRANS_TWO_AS:
1379 case EXT_COMMUNITY_GEN_TWO_AS:
1380 getcommunity(p, 1, &uval2, &dflag2);
1381 break;
1382 case EXT_COMMUNITY_TRANS_IPV4:
1383 case EXT_COMMUNITY_TRANS_FOUR_AS:
1384 case EXT_COMMUNITY_GEN_IPV4:
1385 case EXT_COMMUNITY_GEN_FOUR_AS:
1386 getcommunity(p, 0, &uval2, &dflag2);
1387 break;
1388 default:
1389 errx(1, "parseextcommunity: unexpected result");
1390 }
1391
1392 c->data1 = uval;
1393 c->data2 = uval2;
1394 break;
1395 case EXT_COMMUNITY_TRANS_OPAQUE:
1396 case EXT_COMMUNITY_TRANS_EVPN:
1397 if (strcmp(s, "*") == 0) {
1398 dflag1 = COMMUNITY_ANY;
1399 break;
1400 }
1401 errno = 0;
1402 ullval = strtoull(s, &ep, 0);
1403 if (s[0] == '\0' || *ep != '\0')
1404 errx(1, "Bad ext-community bad value");
1405 if (errno == ERANGE && ullval > EXT_COMMUNITY_OPAQUE_MAX)
1406 errx(1, "Bad ext-community value too big");
1407 c->data1 = ullval >> 32;
1408 c->data2 = ullval;
1409 break;
1410 case EXT_COMMUNITY_NON_TRANS_OPAQUE:
1411 if (subtype == EXT_COMMUNITY_SUBTYPE_OVS) {
1412 if (strcmp(s, "valid") == 0) {
1413 c->data2 = EXT_COMMUNITY_OVS_VALID;
1414 break;
1415 } else if (strcmp(s, "invalid") == 0) {
1416 c->data2 = EXT_COMMUNITY_OVS_INVALID;
1417 break;
1418 } else if (strcmp(s, "not-found") == 0) {
1419 c->data2 = EXT_COMMUNITY_OVS_NOTFOUND;
1420 break;
1421 } else if (strcmp(s, "*") == 0) {
1422 dflag1 = COMMUNITY_ANY;
1423 break;
1424 }
1425 }
1426 errx(1, "Bad ext-community %s", s);
1427 }
1428
1429 c->data3 = type << 8 | subtype;
1430
1431 /* special handling of ext-community rt * since type is not known */
1432 if (dflag1 == COMMUNITY_ANY && type == -1) {
1433 c->flags = COMMUNITY_TYPE_EXT;
1434 c->flags |= dflag1 << 8;
1435 return;
1436 }
1437
1438 /* verify type/subtype combo */
1439 for (cp = iana_ext_comms; cp->subname != NULL; cp++) {
1440 if (cp->type == type && cp->subtype == subtype) {
1441 c->flags = COMMUNITY_TYPE_EXT;
1442 c->flags |= dflag1 << 8;
1443 c->flags |= dflag2 << 16;
1444 return;
1445 }
1446 }
1447
1448 errx(1, "Bad ext-community bad format for type");
1449}
1450
1451int
1452parse_nexthop(const char *word, struct parse_result *r)
1453{
1454 struct filter_set *fs;
1455
1456 if ((fs = calloc(1, sizeof(struct filter_set))) == NULL)
1457 err(1, NULL);
1458
1459 if (strcmp(word, "blackhole") == 0)
1460 fs->type = ACTION_SET_NEXTHOP_BLACKHOLE;
1461 else if (strcmp(word, "reject") == 0)
1462 fs->type = ACTION_SET_NEXTHOP_REJECT;
1463 else if (strcmp(word, "no-modify") == 0)
1464 fs->type = ACTION_SET_NEXTHOP_NOMODIFY;
1465 else if (parse_addr(word, &fs->action.nexthop)) {
1466 fs->type = ACTION_SET_NEXTHOP;
1467 } else {
1468 free(fs);
1469 return (0);
1470 }
1471
1472 TAILQ_INSERT_TAIL(&r->set, fs, entry);
1473 return (1);
1474}
1475
1476static int
1477unary_op(const char *op)
1478{
1479 if (strcmp(op, "=") == 0)
1480 return FLOWSPEC_OP_NUM_EQ;
1481 if (strcmp(op, "!=") == 0)
1482 return FLOWSPEC_OP_NUM_NOT;
1483 if (strcmp(op, ">") == 0)
1484 return FLOWSPEC_OP_NUM_GT;
1485 if (strcmp(op, ">=") == 0)
1486 return FLOWSPEC_OP_NUM_GE;
1487 if (strcmp(op, "<") == 0)
1488 return FLOWSPEC_OP_NUM_LT;
1489 if (strcmp(op, "<=") == 0)
1490 return FLOWSPEC_OP_NUM_LE;
1491 return -1;
1492}
1493
1494static enum comp_ops
1495binary_op(const char *op)
1496{
1497 if (strcmp(op, "-") == 0)
1498 return OP_RANGE;
1499 if (strcmp(op, "><") == 0)
1500 return OP_XRANGE;
1501 return OP_NONE;
1502}
1503
1504static void
1505push_numop(struct parse_result *r, int type, uint8_t op, int and, long long val)
1506{
1507 uint8_t *comp;
1508 void *data;
1509 uint32_t u32;
1510 uint16_t u16;
1511 uint8_t u8, flag = 0;
1512 int len, complen;
1513
1514 flag |= op;
1515 if (and)
1516 flag |= FLOWSPEC_OP_AND;
1517
1518 if (val < 0 || val > 0xffffffff) {
1519 errx(1, "unsupported value for flowspec num_op");
1520 } else if (val <= 255) {
1521 len = 1;
1522 u8 = val;
1523 data = &u8;
1524 } else if (val <= 0xffff) {
1525 len = 2;
1526 u16 = htons(val);
1527 data = &u16;
1528 flag |= 1 << FLOWSPEC_OP_LEN_SHIFT;
1529 } else {
1530 len = 4;
1531 u32 = htonl(val);
1532 data = &u32;
1533 flag |= 2 << FLOWSPEC_OP_LEN_SHIFT;
1534 }
1535
1536 complen = r->flow.complen[type];
1537 comp = realloc(r->flow.components[type], complen + len + 1);
1538 if (comp == NULL)
1539 err(1, NULL);
1540
1541 comp[complen++] = flag;
1542 memcpy(comp + complen, data, len);
1543 complen += len;
1544 r->flow.complen[type] = complen;
1545 r->flow.components[type] = comp;
1546}
1547
1548int
1549parse_flow_numop(int argc, char *argv[], struct parse_result *r,
1550 enum token_type toktype)
1551{
1552 const char *errstr;
1553 long long val, val2;
1554 int numargs, type;
1555 int is_list = 0;
1556 int op;
1557
1558 switch (toktype) {
1559 case FLOW_PROTO:
1560 type = FLOWSPEC_TYPE_PROTO;
1561 break;
1562 case FLOW_SRCPORT:
1563 type = FLOWSPEC_TYPE_SRC_PORT;
1564 break;
1565 case FLOW_DSTPORT:
1566 type = FLOWSPEC_TYPE_DST_PORT;
1567 break;
1568 case FLOW_ICMPTYPE:
1569 type = FLOWSPEC_TYPE_ICMP_TYPE;
1570 break;
1571 case FLOW_ICMPCODE:
1572 type = FLOWSPEC_TYPE_ICMP_CODE;
1573 break;
1574 case FLOW_LENGTH:
1575 type = FLOWSPEC_TYPE_PKT_LEN;
1576 break;
1577 case FLOW_DSCP:
1578 type = FLOWSPEC_TYPE_DSCP;
1579 break;
1580 default:
1581 errx(1, "parse_flow_numop called with unsupported type");
1582 }
1583
1584 /* skip keyword (which is already accounted for) */
1585 argc--;
1586 argv++;
1587 numargs = argc;
1588
1589 while (argc > 0) {
1590 if (strcmp(argv[0], "{") == 0) {
1591 is_list = 1;
1592 argc--;
1593 argv++;
1594 } else if (is_list && strcmp(argv[0], "}") == 0) {
1595 is_list = 0;
1596 argc--;
1597 argv++;
1598 } else if ((op = unary_op(argv[0])) != -1) {
1599 if (argc < 2)
1600 errx(1, "missing argument in flowspec "
1601 "definition");
1602
1603 val = strtonum(argv[1], LLONG_MIN, LLONG_MAX, &errstr);
1604 if (errstr)
1605 errx(1, "\"%s\" invalid number: %s", argv[0],
1606 errstr);
1607 push_numop(r, type, op, 0, val);
1608 argc -= 2;
1609 argv += 2;
1610 } else {
1611 val = strtonum(argv[0], LLONG_MIN, LLONG_MAX, &errstr);
1612 if (errstr)
1613 errx(1, "\"%s\" invalid number: %s", argv[0],
1614 errstr);
1615 if (argc >= 3 && (op = binary_op(argv[1])) != OP_NONE) {
1616 val2 = strtonum(argv[2], LLONG_MIN, LLONG_MAX,
1617 &errstr);
1618 if (errstr)
1619 errx(1, "\"%s\" invalid number: %s",
1620 argv[2], errstr);
1621 switch (op) {
1622 case OP_RANGE:
1623 push_numop(r, type, FLOWSPEC_OP_NUM_GE,
1624 0, val);
1625 push_numop(r, type, FLOWSPEC_OP_NUM_LE,
1626 1, val2);
1627 break;
1628 case OP_XRANGE:
1629 push_numop(r, type, FLOWSPEC_OP_NUM_LT,
1630 0, val);
1631 push_numop(r, type, FLOWSPEC_OP_NUM_GT,
1632 0, val2);
1633 break;
1634 }
1635 argc -= 3;
1636 argv += 3;
1637 } else {
1638 push_numop(r, type, FLOWSPEC_OP_NUM_EQ, 0, val);
1639 argc--;
1640 argv++;
1641 }
1642 }
1643 if (is_list == 0)
1644 break;
1645 }
1646
1647 return numargs - argc;
1648}