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