GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/bgplg/bgpctl/../../../usr.sbin/bgpctl/parser.c Lines: 0 471 0.0 %
Date: 2017-11-13 Branches: 0 359 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: parser.c,v 1.81 2017/10/15 20:44:21 deraadt 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 <err.h>
24
#include <errno.h>
25
#include <fcntl.h>
26
#include <limits.h>
27
#include <netdb.h>
28
#include <stdio.h>
29
#include <stdlib.h>
30
#include <string.h>
31
#include <unistd.h>
32
33
#include "parser.h"
34
#include "irrfilter.h"
35
36
enum token_type {
37
	NOTOKEN,
38
	ENDTOKEN,
39
	KEYWORD,
40
	ADDRESS,
41
	PEERADDRESS,
42
	FLAG,
43
	ASNUM,
44
	ASTYPE,
45
	PREFIX,
46
	PEERDESC,
47
	RIBNAME,
48
	SHUTDOWN_COMMUNICATION,
49
	COMMUNITY,
50
	EXTCOMMUNITY,
51
	EXTCOM_SUBTYPE,
52
	LARGE_COMMUNITY,
53
	LOCALPREF,
54
	MED,
55
	NEXTHOP,
56
	PFTABLE,
57
	PREPNBR,
58
	PREPSELF,
59
	WEIGHT,
60
	FAMILY,
61
	GETOPT,
62
	RTABLE,
63
	FILENAME,
64
	BULK
65
};
66
67
enum getopts {
68
	GETOPT_NONE,
69
	GETOPT_IRRFILTER
70
};
71
72
struct token {
73
	enum token_type		 type;
74
	const char		*keyword;
75
	int			 value;
76
	const struct token	*next;
77
};
78
79
static const struct token t_main[];
80
static const struct token t_show[];
81
static const struct token t_show_summary[];
82
static const struct token t_show_fib[];
83
static const struct token t_show_rib[];
84
static const struct token t_show_mrt[];
85
static const struct token t_show_mrt_file[];
86
static const struct token t_show_rib_neigh[];
87
static const struct token t_show_mrt_neigh[];
88
static const struct token t_show_rib_rib[];
89
static const struct token t_show_neighbor[];
90
static const struct token t_show_neighbor_modifiers[];
91
static const struct token t_fib[];
92
static const struct token t_neighbor[];
93
static const struct token t_neighbor_modifiers[];
94
static const struct token t_show_rib_as[];
95
static const struct token t_show_mrt_as[];
96
static const struct token t_show_prefix[];
97
static const struct token t_show_ip[];
98
static const struct token t_show_community[];
99
static const struct token t_show_extcommunity[];
100
static const struct token t_show_ext_subtype[];
101
static const struct token t_show_largecommunity[];
102
static const struct token t_network[];
103
static const struct token t_network_show[];
104
static const struct token t_prefix[];
105
static const struct token t_set[];
106
static const struct token t_community[];
107
static const struct token t_extcommunity[];
108
static const struct token t_ext_subtype[];
109
static const struct token t_largecommunity[];
110
static const struct token t_localpref[];
111
static const struct token t_med[];
112
static const struct token t_nexthop[];
113
static const struct token t_pftable[];
114
static const struct token t_prepnbr[];
115
static const struct token t_prepself[];
116
static const struct token t_weight[];
117
static const struct token t_irrfilter[];
118
static const struct token t_irrfilter_opts[];
119
static const struct token t_log[];
120
static const struct token t_fib_table[];
121
static const struct token t_show_fib_table[];
122
123
static const struct token t_main[] = {
124
	{ KEYWORD,	"reload",	RELOAD,		NULL},
125
	{ KEYWORD,	"show",		SHOW,		t_show},
126
	{ KEYWORD,	"fib",		FIB,		t_fib},
127
	{ KEYWORD,	"neighbor",	NEIGHBOR,	t_neighbor},
128
	{ KEYWORD,	"network",	NONE,		t_network},
129
	{ KEYWORD,	"irrfilter",	IRRFILTER,	t_irrfilter},
130
	{ KEYWORD,	"log",		NONE,		t_log},
131
	{ ENDTOKEN,	"",		NONE,		NULL}
132
};
133
134
static const struct token t_show[] = {
135
	{ NOTOKEN,	"",		NONE,		NULL},
136
	{ KEYWORD,	"fib",		SHOW_FIB,	t_show_fib},
137
	{ KEYWORD,	"interfaces",	SHOW_INTERFACE,	NULL},
138
	{ KEYWORD,	"neighbor",	SHOW_NEIGHBOR,	t_show_neighbor},
139
	{ KEYWORD,	"network",	NETWORK_SHOW,	t_network_show},
140
	{ KEYWORD,	"nexthop",	SHOW_NEXTHOP,	NULL},
141
	{ KEYWORD,	"rib",		SHOW_RIB,	t_show_rib},
142
	{ KEYWORD,	"tables",	SHOW_FIB_TABLES, NULL},
143
	{ KEYWORD,	"ip",		NONE,		t_show_ip},
144
	{ KEYWORD,	"summary",	SHOW_SUMMARY,	t_show_summary},
145
	{ KEYWORD,	"mrt",		SHOW_MRT,	t_show_mrt},
146
	{ ENDTOKEN,	"",		NONE,		NULL}
147
};
148
149
static const struct token t_show_summary[] = {
150
	{ NOTOKEN,	"",		NONE,			NULL},
151
	{ KEYWORD,	"terse",	SHOW_SUMMARY_TERSE,	NULL},
152
	{ ENDTOKEN,	"",		NONE,			NULL}
153
};
154
155
static const struct token t_show_fib[] = {
156
	{ NOTOKEN,	"",		NONE,		 NULL},
157
	{ FLAG,		"connected",	F_CONNECTED,	 t_show_fib},
158
	{ FLAG,		"static",	F_STATIC,	 t_show_fib},
159
	{ FLAG,		"bgp",		F_BGPD_INSERTED, t_show_fib},
160
	{ FLAG,		"nexthop",	F_NEXTHOP,	 t_show_fib},
161
	{ KEYWORD,	"table",	NONE,		 t_show_fib_table},
162
	{ FAMILY,	"",		NONE,		 t_show_fib},
163
	{ ADDRESS,	"",		NONE,		 NULL},
164
	{ ENDTOKEN,	"",		NONE,		 NULL}
165
};
166
167
static const struct token t_show_rib[] = {
168
	{ NOTOKEN,	"",		NONE,		NULL},
169
	{ ASTYPE,	"as",		AS_ALL,		t_show_rib_as},
170
	{ ASTYPE,	"source-as",	AS_SOURCE,	t_show_rib_as},
171
	{ ASTYPE,	"transit-as",	AS_TRANSIT,	t_show_rib_as},
172
	{ ASTYPE,	"peer-as",	AS_PEER,	t_show_rib_as},
173
	{ ASTYPE,	"empty-as",	AS_EMPTY,	t_show_rib},
174
	{ KEYWORD,	"community",	NONE,		t_show_community},
175
	{ KEYWORD,	"ext-community", NONE,		t_show_extcommunity},
176
	{ KEYWORD,	"large-community", NONE,	t_show_largecommunity},
177
	{ FLAG,		"best",		F_CTL_ACTIVE,	t_show_rib},
178
	{ FLAG,		"selected",	F_CTL_ACTIVE,	t_show_rib},
179
	{ FLAG,		"detail",	F_CTL_DETAIL,	t_show_rib},
180
	{ FLAG,		"ssv"	,	F_CTL_SSV,	t_show_rib},
181
	{ FLAG,		"in",		F_CTL_ADJ_IN,	t_show_rib},
182
	{ FLAG,		"out",		F_CTL_ADJ_OUT,	t_show_rib},
183
	{ KEYWORD,	"neighbor",	NONE,		t_show_rib_neigh},
184
	{ KEYWORD,	"table",	NONE,		t_show_rib_rib},
185
	{ KEYWORD,	"summary",	SHOW_SUMMARY,	t_show_summary},
186
	{ KEYWORD,	"memory",	SHOW_RIB_MEM,	NULL},
187
	{ FAMILY,	"",		NONE,		t_show_rib},
188
	{ PREFIX,	"",		NONE,		t_show_prefix},
189
	{ ENDTOKEN,	"",		NONE,		NULL}
190
};
191
192
193
static const struct token t_show_mrt[] = {
194
	{ NOTOKEN,	"",		NONE,		NULL},
195
	{ ASTYPE,	"as",		AS_ALL,		t_show_mrt_as},
196
	{ ASTYPE,	"source-as",	AS_SOURCE,	t_show_mrt_as},
197
	{ ASTYPE,	"transit-as",	AS_TRANSIT,	t_show_mrt_as},
198
	{ ASTYPE,	"peer-as",	AS_PEER,	t_show_mrt_as},
199
	{ ASTYPE,	"empty-as",	AS_EMPTY,	t_show_mrt},
200
	{ FLAG,		"detail",	F_CTL_DETAIL,	t_show_mrt},
201
	{ FLAG,		"ssv"	,	F_CTL_SSV,	t_show_mrt},
202
	{ KEYWORD,	"neighbor",	NONE,		t_show_mrt_neigh},
203
	{ KEYWORD,	"file",		NONE,		t_show_mrt_file},
204
	{ FAMILY,	"",		NONE,		t_show_mrt},
205
	{ PREFIX,	"",		NONE,		t_show_prefix},
206
	{ ENDTOKEN,	"",		NONE,		NULL}
207
};
208
209
static const struct token t_show_mrt_file[] = {
210
	{ FILENAME,	"",		NONE,		t_show_mrt},
211
	{ ENDTOKEN,	"",		NONE,	NULL}
212
};
213
214
static const struct token t_show_rib_neigh[] = {
215
	{ PEERADDRESS,	"",		NONE,	t_show_rib},
216
	{ PEERDESC,	"",		NONE,	t_show_rib},
217
	{ ENDTOKEN,	"",		NONE,	NULL}
218
};
219
220
static const struct token t_show_mrt_neigh[] = {
221
	{ PEERADDRESS,	"",		NONE,	t_show_mrt},
222
	{ ENDTOKEN,	"",		NONE,	NULL}
223
};
224
225
static const struct token t_show_rib_rib[] = {
226
	{ RIBNAME,	"",		NONE,	t_show_rib},
227
	{ ENDTOKEN,	"",		NONE,	NULL}
228
};
229
230
static const struct token t_show_neighbor[] = {
231
	{ NOTOKEN,	"",		NONE,	NULL},
232
	{ PEERADDRESS,	"",		NONE,	t_show_neighbor_modifiers},
233
	{ PEERDESC,	"",		NONE,	t_show_neighbor_modifiers},
234
	{ ENDTOKEN,	"",		NONE,	NULL}
235
};
236
237
static const struct token t_show_neighbor_modifiers[] = {
238
	{ NOTOKEN,	"",		NONE,			NULL},
239
	{ KEYWORD,	"timers",	SHOW_NEIGHBOR_TIMERS,	NULL},
240
	{ KEYWORD,	"messages",	SHOW_NEIGHBOR,		NULL},
241
	{ KEYWORD,	"terse",	SHOW_NEIGHBOR_TERSE,	NULL},
242
	{ ENDTOKEN,	"",		NONE,			NULL}
243
};
244
245
static const struct token t_fib[] = {
246
	{ KEYWORD,	"couple",	FIB_COUPLE,	NULL},
247
	{ KEYWORD,	"decouple",	FIB_DECOUPLE,	NULL},
248
	{ KEYWORD,	"table",	NONE,		t_fib_table},
249
	{ ENDTOKEN,	"",		NONE,		NULL}
250
};
251
252
static const struct token t_neighbor[] = {
253
	{ PEERADDRESS,	"",		NONE,		t_neighbor_modifiers},
254
	{ PEERDESC,	"",		NONE,		t_neighbor_modifiers},
255
	{ ENDTOKEN,	"",		NONE,		NULL}
256
};
257
258
static const struct token t_nei_mod_shutc[] = {
259
	{ NOTOKEN,	"",		NONE,		NULL},
260
	{ SHUTDOWN_COMMUNICATION, "",	NONE,		NULL},
261
	{ ENDTOKEN,	"",		NONE,		NULL}
262
};
263
264
static const struct token t_neighbor_modifiers[] = {
265
	{ KEYWORD,	"up",		NEIGHBOR_UP,		NULL},
266
	{ KEYWORD,	"down",		NEIGHBOR_DOWN,		t_nei_mod_shutc},
267
	{ KEYWORD,	"clear",	NEIGHBOR_CLEAR,		t_nei_mod_shutc},
268
	{ KEYWORD,	"refresh",	NEIGHBOR_RREFRESH,	NULL},
269
	{ KEYWORD,	"destroy",	NEIGHBOR_DESTROY,	NULL},
270
	{ ENDTOKEN,	"",		NONE,			NULL}
271
};
272
273
static const struct token t_show_rib_as[] = {
274
	{ ASNUM,	"",		NONE,		t_show_rib},
275
	{ ENDTOKEN,	"",		NONE,		NULL}
276
};
277
278
static const struct token t_show_mrt_as[] = {
279
	{ ASNUM,	"",		NONE,		t_show_mrt},
280
	{ ENDTOKEN,	"",		NONE,		NULL}
281
};
282
283
static const struct token t_show_prefix[] = {
284
	{ NOTOKEN,	"",		NONE,		NULL},
285
	{ FLAG,		"all",		F_LONGER,	NULL},
286
	{ FLAG,		"longer-prefixes", F_LONGER,	NULL},
287
	{ ENDTOKEN,	"",		NONE,		NULL}
288
};
289
290
static const struct token t_show_ip[] = {
291
	{ KEYWORD,	"bgp",		SHOW_RIB,	t_show_rib},
292
	{ ENDTOKEN,	"",		NONE,		NULL}
293
};
294
295
static const struct token t_show_community[] = {
296
	{ COMMUNITY,	"",		NONE,		t_show_rib},
297
	{ ENDTOKEN,	"",		NONE,		NULL}
298
};
299
300
static const struct token t_show_extcommunity[] = {
301
	{ EXTCOM_SUBTYPE,	"bdc",		NONE,	t_show_ext_subtype},
302
	{ EXTCOM_SUBTYPE,	"defgw",	NONE,	t_show_ext_subtype},
303
	{ EXTCOM_SUBTYPE,	"esi-lab",	NONE,	t_show_ext_subtype},
304
	{ EXTCOM_SUBTYPE,	"esi-rt",	NONE,	t_show_ext_subtype},
305
	{ EXTCOM_SUBTYPE,	"l2vid",	NONE,	t_show_ext_subtype},
306
	{ EXTCOM_SUBTYPE,	"mac-mob",	NONE,	t_show_ext_subtype},
307
	{ EXTCOM_SUBTYPE,	"odi",		NONE,	t_show_ext_subtype},
308
	{ EXTCOM_SUBTYPE,	"ort",		NONE,	t_show_ext_subtype},
309
	{ EXTCOM_SUBTYPE,	"ori",		NONE,	t_show_ext_subtype},
310
	{ EXTCOM_SUBTYPE,	"ovs",		NONE,	t_show_ext_subtype},
311
	{ EXTCOM_SUBTYPE,	"rt",		NONE,	t_show_ext_subtype},
312
	{ EXTCOM_SUBTYPE,	"soo",		NONE,	t_show_ext_subtype},
313
	{ EXTCOM_SUBTYPE,	"srcas",	NONE,	t_show_ext_subtype},
314
	{ EXTCOM_SUBTYPE,	"vrfri",	NONE,	t_show_ext_subtype},
315
	{ ENDTOKEN,	"",	NONE,	NULL}
316
};
317
318
static const struct token t_show_ext_subtype[] = {
319
	{ EXTCOMMUNITY,	"",	NONE,	t_show_rib},
320
	{ ENDTOKEN,	"",	NONE,	NULL}
321
};
322
323
static const struct token t_show_largecommunity[] = {
324
	{ LARGE_COMMUNITY,	"",	NONE,		t_show_rib},
325
	{ ENDTOKEN,	"",		NONE,		NULL}
326
};
327
328
static const struct token t_network[] = {
329
	{ KEYWORD,	"add",		NETWORK_ADD,	t_prefix},
330
	{ KEYWORD,	"delete",	NETWORK_REMOVE,	t_prefix},
331
	{ KEYWORD,	"flush",	NETWORK_FLUSH,	NULL},
332
	{ KEYWORD,	"show",		NETWORK_SHOW,	t_network_show},
333
	{ KEYWORD,	"mrt",		NETWORK_MRT,	t_show_mrt},
334
	{ KEYWORD,	"bulk",		NETWORK_BULK_ADD,	t_set},
335
	{ ENDTOKEN,	"",		NONE,		NULL}
336
};
337
338
static const struct token t_prefix[] = {
339
	{ PREFIX,	"",		NONE,		t_set},
340
	{ ENDTOKEN,	"",		NONE,		NULL}
341
};
342
343
static const struct token t_network_show[] = {
344
	{ NOTOKEN,	"",		NONE,			NULL},
345
	{ FAMILY,	"",		NONE,			NULL},
346
	{ ENDTOKEN,	"",		NONE,			NULL}
347
};
348
349
static const struct token t_set[] = {
350
	{ NOTOKEN,	"",			NONE,	NULL},
351
	{ KEYWORD,	"community",		NONE,	t_community},
352
	{ KEYWORD,	"ext-community",	NONE,	t_extcommunity},
353
	{ KEYWORD,	"large-community",	NONE,	t_largecommunity},
354
	{ KEYWORD,	"localpref",		NONE,	t_localpref},
355
	{ KEYWORD,	"med",			NONE,	t_med},
356
	{ KEYWORD,	"metric",		NONE,	t_med},
357
	{ KEYWORD,	"nexthop",		NONE,	t_nexthop},
358
	{ KEYWORD,	"pftable",		NONE,	t_pftable},
359
	{ KEYWORD,	"prepend-neighbor",	NONE,	t_prepnbr},
360
	{ KEYWORD,	"prepend-self",		NONE,	t_prepself},
361
	{ KEYWORD,	"weight",		NONE,	t_weight},
362
	{ KEYWORD,	"add",			NETWORK_BULK_ADD,	NULL},
363
	{ KEYWORD,	"delete",		NETWORK_BULK_REMOVE,	NULL},
364
	{ ENDTOKEN,	"",			NONE,	NULL}
365
};
366
367
static const struct token t_community[] = {
368
	{ COMMUNITY,	"",			NONE,	t_set},
369
	{ ENDTOKEN,	"",			NONE,	NULL}
370
};
371
372
static const struct token t_extcommunity[] = {
373
	{ EXTCOM_SUBTYPE,	"bdc",		NONE,	t_ext_subtype},
374
	{ EXTCOM_SUBTYPE,	"defgw",	NONE,	t_ext_subtype},
375
	{ EXTCOM_SUBTYPE,	"esi-lab",	NONE,	t_ext_subtype},
376
	{ EXTCOM_SUBTYPE,	"esi-rt",	NONE,	t_ext_subtype},
377
	{ EXTCOM_SUBTYPE,	"l2vid",	NONE,	t_ext_subtype},
378
	{ EXTCOM_SUBTYPE,	"mac-mob",	NONE,	t_ext_subtype},
379
	{ EXTCOM_SUBTYPE,	"odi",		NONE,	t_ext_subtype},
380
	{ EXTCOM_SUBTYPE,	"ort",		NONE,	t_ext_subtype},
381
	{ EXTCOM_SUBTYPE,	"ori",		NONE,	t_ext_subtype},
382
	{ EXTCOM_SUBTYPE,	"ovs",		NONE,	t_ext_subtype},
383
	{ EXTCOM_SUBTYPE,	"rt",		NONE,	t_ext_subtype},
384
	{ EXTCOM_SUBTYPE,	"soo",		NONE,	t_ext_subtype},
385
	{ EXTCOM_SUBTYPE,	"srcas",	NONE,	t_ext_subtype},
386
	{ EXTCOM_SUBTYPE,	"vrfri",	NONE,	t_ext_subtype},
387
	{ ENDTOKEN,	"",	NONE,	NULL}
388
};
389
390
static const struct token t_ext_subtype[] = {
391
	{ EXTCOMMUNITY,	"",	NONE,	t_set},
392
	{ ENDTOKEN,	"",	NONE,	NULL}
393
};
394
395
static const struct token t_largecommunity[] = {
396
	{ LARGE_COMMUNITY,	"",		NONE,	t_set},
397
	{ ENDTOKEN,	"",			NONE,	NULL}
398
};
399
400
static const struct token t_localpref[] = {
401
	{ LOCALPREF,	"",			NONE,	t_set},
402
	{ ENDTOKEN,	"",			NONE,	NULL}
403
};
404
405
static const struct token t_med[] = {
406
	{ MED,		"",			NONE,	t_set},
407
	{ ENDTOKEN,	"",			NONE,	NULL}
408
};
409
410
static const struct token t_nexthop[] = {
411
	{ NEXTHOP,	"",			NONE,	t_set},
412
	{ ENDTOKEN,	"",			NONE,	NULL}
413
};
414
415
static const struct token t_pftable[] = {
416
	{ PFTABLE,	"",			NONE,	t_set},
417
	{ ENDTOKEN,	"",			NONE,	NULL}
418
};
419
420
static const struct token t_prepnbr[] = {
421
	{ PREPNBR,	"",			NONE,	t_set},
422
	{ ENDTOKEN,	"",			NONE,	NULL}
423
};
424
425
static const struct token t_prepself[] = {
426
	{ PREPSELF,	"",			NONE,	t_set},
427
	{ ENDTOKEN,	"",			NONE,	NULL}
428
};
429
430
static const struct token t_weight[] = {
431
	{ WEIGHT,	"",			NONE,	t_set},
432
	{ ENDTOKEN,	"",			NONE,	NULL}
433
};
434
435
static const struct token t_irrfilter[] = {
436
	{ GETOPT,	"",	GETOPT_IRRFILTER,	t_irrfilter},
437
	{ ASNUM,	"",	NONE,			t_irrfilter_opts},
438
	{ ENDTOKEN,	"",	NONE,			NULL}
439
};
440
441
static const struct token t_irrfilter_opts[] = {
442
	{ NOTOKEN,	"",		NONE,			NULL},
443
	{ FLAG,		"importonly",	F_IMPORTONLY,		t_irrfilter_opts},
444
	{ ENDTOKEN,	"",		NONE,			NULL}
445
};
446
447
static const struct token t_log[] = {
448
	{ KEYWORD,	"verbose",	LOG_VERBOSE,	NULL},
449
	{ KEYWORD,	"brief",	LOG_BRIEF,	NULL},
450
	{ ENDTOKEN,	"",		NONE,		NULL}
451
};
452
453
static const struct token t_fib_table[] = {
454
	{ RTABLE,	"",			NONE,	t_fib},
455
	{ ENDTOKEN,	"",			NONE,	NULL}
456
};
457
458
static const struct token t_show_fib_table[] = {
459
	{ RTABLE,	"",			NONE,	t_show_fib},
460
	{ ENDTOKEN,	"",			NONE,	NULL}
461
};
462
463
static struct parse_result	res;
464
465
const struct token	*match_token(int *argc, char **argv[],
466
			    const struct token []);
467
void			 show_valid_args(const struct token []);
468
int			 parse_addr(const char *, struct bgpd_addr *);
469
int			 parse_asnum(const char *, size_t, u_int32_t *);
470
int			 parse_number(const char *, struct parse_result *,
471
			     enum token_type);
472
int			 getcommunity(const char *);
473
int			 parse_community(const char *, struct parse_result *);
474
int			 parsesubtype(const char *, u_int8_t *, u_int8_t *);
475
int			 parseextvalue(const char *, u_int32_t *);
476
u_int			 parseextcommunity(const char *, struct parse_result *);
477
u_int			 getlargecommunity(const char *);
478
int			 parse_largecommunity(const char *,
479
			     struct parse_result *);
480
int			 parse_nexthop(const char *, struct parse_result *);
481
int			 bgpctl_getopt(int *, char **[], int);
482
483
struct parse_result *
484
parse(int argc, char *argv[])
485
{
486
	const struct token	*table = t_main;
487
	const struct token	*match;
488
489
	bzero(&res, sizeof(res));
490
	res.community.as = COMMUNITY_UNSET;
491
	res.community.type = COMMUNITY_UNSET;
492
	res.large_community.as = COMMUNITY_UNSET;
493
	res.large_community.ld1 = COMMUNITY_UNSET;
494
	res.large_community.ld2 = COMMUNITY_UNSET;
495
	TAILQ_INIT(&res.set);
496
	if ((res.irr_outdir = getcwd(NULL, 0)) == NULL) {
497
		fprintf(stderr, "getcwd failed: %s\n", strerror(errno));
498
		return (NULL);
499
	}
500
501
	while (argc >= 0) {
502
		if ((match = match_token(&argc, &argv, table)) == NULL) {
503
			fprintf(stderr, "valid commands/args:\n");
504
			show_valid_args(table);
505
			return (NULL);
506
		}
507
508
		argc--;
509
		argv++;
510
511
		if (match->type == NOTOKEN || match->next == NULL)
512
			break;
513
514
		table = match->next;
515
	}
516
517
	if (argc > 0) {
518
		fprintf(stderr, "superfluous argument: %s\n", argv[0]);
519
		return (NULL);
520
	}
521
522
	return (&res);
523
}
524
525
const struct token *
526
match_token(int *argc, char **argv[], const struct token table[])
527
{
528
	u_int			 i, match;
529
	const struct token	*t = NULL;
530
	struct filter_set	*fs;
531
	const char		*word = *argv[0];
532
	size_t			wordlen = 0;
533
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 KEYWORD:
546
			if (word != NULL && strncmp(word, table[i].keyword,
547
			    wordlen) == 0) {
548
				match++;
549
				t = &table[i];
550
				if (t->value)
551
					res.action = t->value;
552
			}
553
			break;
554
		case FLAG:
555
			if (word != NULL && strncmp(word, table[i].keyword,
556
			    wordlen) == 0) {
557
				match++;
558
				t = &table[i];
559
				res.flags |= t->value;
560
			}
561
			break;
562
		case FAMILY:
563
			if (word == NULL)
564
				break;
565
			if (!strcmp(word, "inet") ||
566
			    !strcasecmp(word, "IPv4")) {
567
				match++;
568
				t = &table[i];
569
				res.aid = AID_INET;
570
			}
571
			if (!strcmp(word, "inet6") ||
572
			    !strcasecmp(word, "IPv6")) {
573
				match++;
574
				t = &table[i];
575
				res.aid = AID_INET6;
576
			}
577
			if (!strcasecmp(word, "VPNv4")) {
578
				match++;
579
				t = &table[i];
580
				res.aid = AID_VPN_IPv4;
581
			}
582
			break;
583
		case ADDRESS:
584
			if (parse_addr(word, &res.addr)) {
585
				match++;
586
				t = &table[i];
587
				if (t->value)
588
					res.action = t->value;
589
			}
590
			break;
591
		case PEERADDRESS:
592
			if (parse_addr(word, &res.peeraddr)) {
593
				match++;
594
				t = &table[i];
595
				if (t->value)
596
					res.action = t->value;
597
			}
598
			break;
599
		case PREFIX:
600
			if (parse_prefix(word, wordlen, &res.addr, &res.prefixlen)) {
601
				match++;
602
				t = &table[i];
603
				if (t->value)
604
					res.action = t->value;
605
			}
606
			break;
607
		case ASTYPE:
608
			if (word != NULL && strncmp(word, table[i].keyword,
609
			    wordlen) == 0) {
610
				match++;
611
				t = &table[i];
612
				res.as.type = t->value;
613
			}
614
			break;
615
		case ASNUM:
616
			if (parse_asnum(word, wordlen, &res.as.as)) {
617
				match++;
618
				t = &table[i];
619
			}
620
			break;
621
		case PEERDESC:
622
			if (!match && word != NULL && wordlen > 0) {
623
				if (strlcpy(res.peerdesc, word,
624
				    sizeof(res.peerdesc)) >=
625
				    sizeof(res.peerdesc))
626
					errx(1, "neighbor description too "
627
					    "long");
628
				match++;
629
				t = &table[i];
630
			}
631
			break;
632
		case RIBNAME:
633
			if (!match && word != NULL && wordlen > 0) {
634
				if (strlcpy(res.rib, word, sizeof(res.rib)) >=
635
				    sizeof(res.rib))
636
					errx(1, "rib name too long");
637
				match++;
638
				t = &table[i];
639
			}
640
			break;
641
		case SHUTDOWN_COMMUNICATION:
642
			if (!match && word != NULL && wordlen > 0) {
643
				if (strlcpy(res.shutcomm, word,
644
				    sizeof(res.shutcomm)) >=
645
				    sizeof(res.shutcomm))
646
					errx(1, "shutdown reason too long");
647
				match++;
648
				t = &table[i];
649
			}
650
			break;
651
		case COMMUNITY:
652
			if (word != NULL && wordlen > 0 &&
653
			    parse_community(word, &res)) {
654
				match++;
655
				t = &table[i];
656
			}
657
			break;
658
		case EXTCOM_SUBTYPE:
659
			if (word != NULL && strncmp(word, table[i].keyword,
660
			    wordlen) == 0) {
661
				if (parsesubtype(word, &res.extcommunity.type,
662
				    &res.extcommunity.subtype) == 0)
663
					errx(1, "Bad ext-community unknown "
664
					    "type");
665
				match++;
666
				t = &table[i];
667
			}
668
			break;
669
		case EXTCOMMUNITY:
670
			if (word != NULL && wordlen > 0 &&
671
			    parseextcommunity(word, &res)) {
672
				match++;
673
				t = &table[i];
674
			}
675
			break;
676
		case LARGE_COMMUNITY:
677
			if (word != NULL && wordlen > 0 &&
678
			    parse_largecommunity(word, &res)) {
679
				match++;
680
				t = &table[i];
681
			}
682
			break;
683
		case LOCALPREF:
684
		case MED:
685
		case PREPNBR:
686
		case PREPSELF:
687
		case WEIGHT:
688
		case RTABLE:
689
			if (word != NULL && wordlen > 0 &&
690
			    parse_number(word, &res, table[i].type)) {
691
				match++;
692
				t = &table[i];
693
			}
694
			break;
695
		case NEXTHOP:
696
			if (word != NULL && wordlen > 0 &&
697
			    parse_nexthop(word, &res)) {
698
				match++;
699
				t = &table[i];
700
			}
701
			break;
702
		case PFTABLE:
703
			if (word != NULL && wordlen > 0) {
704
				if ((fs = calloc(1,
705
				    sizeof(struct filter_set))) == NULL)
706
					err(1, NULL);
707
				if (strlcpy(fs->action.pftable, word,
708
				    sizeof(fs->action.pftable)) >=
709
				    sizeof(fs->action.pftable))
710
					errx(1, "pftable name too long");
711
				TAILQ_INSERT_TAIL(&res.set, fs, entry);
712
				match++;
713
				t = &table[i];
714
			}
715
			break;
716
		case GETOPT:
717
			if (bgpctl_getopt(argc, argv, table[i].value)) {
718
				match++;
719
				t = &table[i];
720
			}
721
			break;
722
		case FILENAME:
723
			if (word != NULL && wordlen > 0) {
724
				if ((res.mrtfd = open(word, O_RDONLY)) == -1) {
725
					/*
726
					 * ignore error if path has no / and
727
					 * does not exist. In hope to print
728
					 * usage.
729
					 */
730
					if (errno == ENOENT &&
731
					    !strchr(word, '/'))
732
						break;
733
					err(1, "mrt open(%s)", word);
734
				}
735
				match++;
736
				t = &table[i];
737
			}
738
			break;
739
		case BULK:
740
			match++;
741
			t = &table[i];
742
			break;
743
		case ENDTOKEN:
744
			break;
745
		}
746
	}
747
748
	if (match != 1) {
749
		if (word == NULL)
750
			fprintf(stderr, "missing argument:\n");
751
		else if (match > 1)
752
			fprintf(stderr, "ambiguous argument: %s\n", word);
753
		else if (match < 1)
754
			fprintf(stderr, "unknown argument: %s\n", word);
755
		return (NULL);
756
	}
757
758
	return (t);
759
}
760
761
void
762
show_valid_args(const struct token table[])
763
{
764
	int	i;
765
766
	for (i = 0; table[i].type != ENDTOKEN; i++) {
767
		switch (table[i].type) {
768
		case NOTOKEN:
769
			fprintf(stderr, "  <cr>\n");
770
			break;
771
		case KEYWORD:
772
		case FLAG:
773
		case ASTYPE:
774
		case EXTCOM_SUBTYPE:
775
			fprintf(stderr, "  %s\n", table[i].keyword);
776
			break;
777
		case ADDRESS:
778
		case PEERADDRESS:
779
			fprintf(stderr, "  <address>\n");
780
			break;
781
		case PREFIX:
782
			fprintf(stderr, "  <address>[/<len>]\n");
783
			break;
784
		case ASNUM:
785
			fprintf(stderr, "  <asnum>\n");
786
			break;
787
		case PEERDESC:
788
			fprintf(stderr, "  <neighbor description>\n");
789
			break;
790
		case RIBNAME:
791
			fprintf(stderr, "  <rib name>\n");
792
			break;
793
		case SHUTDOWN_COMMUNICATION:
794
			fprintf(stderr, "  <shutdown reason>\n");
795
			break;
796
		case COMMUNITY:
797
			fprintf(stderr, "  <community>\n");
798
			break;
799
		case EXTCOMMUNITY:
800
			fprintf(stderr, "  <extended-community>\n");
801
			break;
802
		case LARGE_COMMUNITY:
803
			fprintf(stderr, "  <large-community>\n");
804
			break;
805
		case LOCALPREF:
806
		case MED:
807
		case PREPNBR:
808
		case PREPSELF:
809
		case WEIGHT:
810
			fprintf(stderr, "  <number>\n");
811
			break;
812
		case RTABLE:
813
			fprintf(stderr, "  <rtableid>\n");
814
			break;
815
		case NEXTHOP:
816
			fprintf(stderr, "  <address>\n");
817
			break;
818
		case PFTABLE:
819
			fprintf(stderr, "  <pftable>\n");
820
			break;
821
		case FAMILY:
822
			fprintf(stderr, "  [ inet | inet6 | IPv4 | IPv6 | VPNv4 ]\n");
823
			break;
824
		case GETOPT:
825
			fprintf(stderr, "  <options>\n");
826
			break;
827
		case FILENAME:
828
			fprintf(stderr, "  <filename>\n");
829
			break;
830
		case BULK:
831
		case ENDTOKEN:
832
			break;
833
		}
834
	}
835
}
836
837
int
838
parse_addr(const char *word, struct bgpd_addr *addr)
839
{
840
	struct in_addr	ina;
841
	struct addrinfo	hints, *r;
842
843
	if (word == NULL)
844
		return (0);
845
846
	bzero(addr, sizeof(struct bgpd_addr));
847
	bzero(&ina, sizeof(ina));
848
849
	if (inet_net_pton(AF_INET, word, &ina, sizeof(ina)) != -1) {
850
		addr->aid = AID_INET;
851
		addr->v4 = ina;
852
		return (1);
853
	}
854
855
	bzero(&hints, sizeof(hints));
856
	hints.ai_family = AF_INET6;
857
	hints.ai_socktype = SOCK_DGRAM; /*dummy*/
858
	hints.ai_flags = AI_NUMERICHOST;
859
	if (getaddrinfo(word, "0", &hints, &r) == 0) {
860
		sa2addr(r->ai_addr, addr);
861
		freeaddrinfo(r);
862
		return (1);
863
	}
864
865
	return (0);
866
}
867
868
int
869
parse_prefix(const char *word, size_t wordlen, struct bgpd_addr *addr, u_int8_t *prefixlen)
870
{
871
	char		*p, *ps;
872
	const char	*errstr;
873
	int		 mask = -1;
874
875
	if (word == NULL)
876
		return (0);
877
878
	bzero(addr, sizeof(struct bgpd_addr));
879
880
	if ((p = strrchr(word, '/')) != NULL) {
881
		size_t plen = strlen(p);
882
		mask = strtonum(p + 1, 0, 128, &errstr);
883
		if (errstr)
884
			errx(1, "netmask %s", errstr);
885
886
		if ((ps = malloc(wordlen - plen + 1)) == NULL)
887
			err(1, "parse_prefix: malloc");
888
		strlcpy(ps, word, wordlen - plen + 1);
889
890
		if (parse_addr(ps, addr) == 0) {
891
			free(ps);
892
			return (0);
893
		}
894
895
		free(ps);
896
	} else
897
		if (parse_addr(word, addr) == 0)
898
			return (0);
899
900
	switch (addr->aid) {
901
	case AID_INET:
902
		if (mask == -1)
903
			mask = 32;
904
		if (mask > 32)
905
			errx(1, "invalid netmask: too large");
906
		addr->v4.s_addr = addr->v4.s_addr & htonl(prefixlen2mask(mask));
907
		break;
908
	case AID_INET6:
909
		if (mask == -1)
910
			mask = 128;
911
		inet6applymask(&addr->v6, &addr->v6, mask);
912
		break;
913
	default:
914
		return (0);
915
	}
916
917
	*prefixlen = mask;
918
	return (1);
919
}
920
921
int
922
parse_asnum(const char *word, size_t wordlen, u_int32_t *asnum)
923
{
924
	const char	*errstr;
925
	char		*dot;
926
	u_int32_t	 uval, uvalh = 0;
927
928
	if (word == NULL)
929
		return (0);
930
931
	if (wordlen < 1 || word[0] < '0' || word[0] > '9')
932
		return (0);
933
934
	if ((dot = strchr(word,'.')) != NULL) {
935
		*dot++ = '\0';
936
		uvalh = strtonum(word, 0, USHRT_MAX, &errstr);
937
		if (errstr)
938
			errx(1, "AS number is %s: %s", errstr, word);
939
		uval = strtonum(dot, 0, USHRT_MAX, &errstr);
940
		if (errstr)
941
			errx(1, "AS number is %s: %s", errstr, word);
942
	} else {
943
		uval = strtonum(word, 0, UINT_MAX, &errstr);
944
		if (errstr)
945
			errx(1, "AS number is %s: %s", errstr, word);
946
	}
947
948
	*asnum = uval | (uvalh << 16);
949
	return (1);
950
}
951
952
int
953
parse_number(const char *word, struct parse_result *r, enum token_type type)
954
{
955
	struct filter_set	*fs;
956
	const char		*errstr;
957
	u_int			 uval;
958
959
	if (word == NULL)
960
		return (0);
961
962
	uval = strtonum(word, 0, UINT_MAX, &errstr);
963
	if (errstr)
964
		errx(1, "number is %s: %s", errstr, word);
965
966
	/* number was parseable */
967
	if (type == RTABLE) {
968
		r->rtableid = uval;
969
		return (1);
970
	}
971
972
	if ((fs = calloc(1, sizeof(struct filter_set))) == NULL)
973
		err(1, NULL);
974
	switch (type) {
975
	case LOCALPREF:
976
		fs->type = ACTION_SET_LOCALPREF;
977
		fs->action.metric = uval;
978
		break;
979
	case MED:
980
		fs->type = ACTION_SET_MED;
981
		fs->action.metric = uval;
982
		break;
983
	case PREPNBR:
984
		if (uval > 128) {
985
			free(fs);
986
			return (0);
987
		}
988
		fs->type = ACTION_SET_PREPEND_PEER;
989
		fs->action.prepend = uval;
990
		break;
991
	case PREPSELF:
992
		if (uval > 128) {
993
			free(fs);
994
			return (0);
995
		}
996
		fs->type = ACTION_SET_PREPEND_SELF;
997
		fs->action.prepend = uval;
998
		break;
999
	case WEIGHT:
1000
		fs->type = ACTION_SET_WEIGHT;
1001
		fs->action.metric = uval;
1002
		break;
1003
	default:
1004
		errx(1, "king bula sez bad things happen");
1005
	}
1006
1007
	TAILQ_INSERT_TAIL(&r->set, fs, entry);
1008
	return (1);
1009
}
1010
1011
int
1012
getcommunity(const char *s)
1013
{
1014
	const char	*errstr;
1015
	u_int16_t	 uval;
1016
1017
	if (strcmp(s, "*") == 0)
1018
		return (COMMUNITY_ANY);
1019
1020
	uval = strtonum(s, 0, USHRT_MAX, &errstr);
1021
	if (errstr)
1022
		errx(1, "Community is %s: %s", errstr, s);
1023
1024
	return (uval);
1025
}
1026
1027
int
1028
parse_community(const char *word, struct parse_result *r)
1029
{
1030
	struct filter_set	*fs;
1031
	char			*p;
1032
	int			 as, type;
1033
1034
	/* Well-known communities */
1035
	if (strcasecmp(word, "GRACEFUL_SHUTDOWN") == 0) {
1036
		as = COMMUNITY_WELLKNOWN;
1037
		type = COMMUNITY_GRACEFUL_SHUTDOWN;
1038
		goto done;
1039
	} else if (strcasecmp(word, "NO_EXPORT") == 0) {
1040
		as = COMMUNITY_WELLKNOWN;
1041
		type = COMMUNITY_NO_EXPORT;
1042
		goto done;
1043
	} else if (strcasecmp(word, "NO_ADVERTISE") == 0) {
1044
		as = COMMUNITY_WELLKNOWN;
1045
		type = COMMUNITY_NO_ADVERTISE;
1046
		goto done;
1047
	} else if (strcasecmp(word, "NO_EXPORT_SUBCONFED") == 0) {
1048
		as = COMMUNITY_WELLKNOWN;
1049
		type = COMMUNITY_NO_EXPSUBCONFED;
1050
		goto done;
1051
	} else if (strcasecmp(word, "NO_PEER") == 0) {
1052
		as = COMMUNITY_WELLKNOWN;
1053
		type = COMMUNITY_NO_PEER;
1054
		goto done;
1055
	} else if (strcasecmp(word, "BLACKHOLE") == 0) {
1056
		as = COMMUNITY_WELLKNOWN;
1057
		type = COMMUNITY_BLACKHOLE;
1058
		goto done;
1059
	}
1060
1061
	if ((p = strchr(word, ':')) == NULL) {
1062
		fprintf(stderr, "Bad community syntax\n");
1063
		return (0);
1064
	}
1065
	*p++ = 0;
1066
1067
	as = getcommunity(word);
1068
	type = getcommunity(p);
1069
1070
done:
1071
	if (as == 0) {
1072
		fprintf(stderr, "Invalid community\n");
1073
		return (0);
1074
	}
1075
	if (as == COMMUNITY_WELLKNOWN)
1076
		switch (type) {
1077
		case COMMUNITY_GRACEFUL_SHUTDOWN:
1078
		case COMMUNITY_NO_EXPORT:
1079
		case COMMUNITY_NO_ADVERTISE:
1080
		case COMMUNITY_NO_EXPSUBCONFED:
1081
		case COMMUNITY_BLACKHOLE:
1082
			/* valid */
1083
			break;
1084
		}
1085
1086
	if ((fs = calloc(1, sizeof(struct filter_set))) == NULL)
1087
		err(1, NULL);
1088
	fs->type = ACTION_SET_COMMUNITY;
1089
	fs->action.community.as = as;
1090
	fs->action.community.type = type;
1091
1092
	r->community.as = as;
1093
	r->community.type = type;
1094
1095
	TAILQ_INSERT_TAIL(&r->set, fs, entry);
1096
	return (1);
1097
}
1098
1099
int
1100
parsesubtype(const char *name, u_int8_t *type, u_int8_t *subtype)
1101
{
1102
	const struct ext_comm_pairs *cp;
1103
	int found = 0;
1104
1105
	for (cp = iana_ext_comms; cp->subname != NULL; cp++) {
1106
		if (strcmp(name, cp->subname) == 0) {
1107
			if (found == 0) {
1108
				*type = cp->type;
1109
				*subtype = cp->subtype;
1110
			}
1111
			found++;
1112
		}
1113
	}
1114
	if (found > 1)
1115
		*type = -1;
1116
	return (found);
1117
}
1118
1119
int
1120
parseextvalue(const char *s, u_int32_t *v)
1121
{
1122
	const char	*errstr;
1123
	char		*p;
1124
	struct in_addr	 ip;
1125
	u_int32_t	 uvalh = 0, uval;
1126
1127
	if ((p = strchr(s, '.')) == NULL) {
1128
		/* AS_PLAIN number (4 or 2 byte) */
1129
		uval = strtonum(s, 0, UINT_MAX, &errstr);
1130
		if (errstr) {
1131
			fprintf(stderr, "Bad ext-community: %s is %s\n", s,
1132
			    errstr);
1133
			return (-1);
1134
		}
1135
		*v = uval;
1136
		if (uval <= USHRT_MAX)
1137
			return (EXT_COMMUNITY_TRANS_TWO_AS);
1138
		else
1139
			return (EXT_COMMUNITY_TRANS_FOUR_AS);
1140
	} else if (strchr(p + 1, '.') == NULL) {
1141
		/* AS_DOT number (4-byte) */
1142
		*p++ = '\0';
1143
		uvalh = strtonum(s, 0, USHRT_MAX, &errstr);
1144
		if (errstr) {
1145
			fprintf(stderr, "Bad ext-community: %s is %s\n", s,
1146
			    errstr);
1147
			return (-1);
1148
		}
1149
		uval = strtonum(p, 0, USHRT_MAX, &errstr);
1150
		if (errstr) {
1151
			fprintf(stderr, "Bad ext-community: %s is %s\n", p,
1152
			    errstr);
1153
			return (-1);
1154
		}
1155
		*v = uval | (uvalh << 16);
1156
		return (EXT_COMMUNITY_TRANS_FOUR_AS);
1157
	} else {
1158
		/* more than one dot -> IP address */
1159
		if (inet_aton(s, &ip) == 0) {
1160
			fprintf(stderr, "Bad ext-community: %s not parseable\n",
1161
			    s);
1162
			return (-1);
1163
		}
1164
		*v = ip.s_addr;
1165
		return (EXT_COMMUNITY_TRANS_IPV4);
1166
	}
1167
	return (-1);
1168
}
1169
1170
u_int
1171
parseextcommunity(const char *word, struct parse_result *r)
1172
{
1173
	struct filter_set		*fs;
1174
	const struct ext_comm_pairs	*cp;
1175
	const char			*errstr;
1176
	u_int64_t			 ullval;
1177
	u_int32_t			 uval;
1178
	char				*p, *ep;
1179
	int				 type;
1180
1181
	type = r->extcommunity.type;
1182
1183
	switch (type) {
1184
	case 0xff:
1185
		if ((p = strchr(word, ':')) == NULL) {
1186
			fprintf(stderr, "Bad ext-community: %s\n", word);
1187
			return (0);
1188
		}
1189
		*p++ = '\0';
1190
		if ((type = parseextvalue(word, &uval)) == -1)
1191
			return (0);
1192
		switch (type) {
1193
		case EXT_COMMUNITY_TRANS_TWO_AS:
1194
			ullval = strtonum(p, 0, UINT_MAX, &errstr);
1195
			break;
1196
		case EXT_COMMUNITY_TRANS_IPV4:
1197
		case EXT_COMMUNITY_TRANS_FOUR_AS:
1198
			ullval = strtonum(p, 0, USHRT_MAX, &errstr);
1199
			break;
1200
		default:
1201
			fprintf(stderr, "parseextcommunity: unexpected "
1202
			    "result\n");
1203
			return (0);
1204
		}
1205
		if (errstr) {
1206
			fprintf(stderr, "Bad ext-community: %s is %s\n", p,
1207
			    errstr);
1208
			return (0);
1209
		}
1210
		switch (type) {
1211
		case EXT_COMMUNITY_TRANS_TWO_AS:
1212
			r->extcommunity.data.ext_as.as = uval;
1213
			r->extcommunity.data.ext_as.val = ullval;
1214
			break;
1215
		case EXT_COMMUNITY_TRANS_IPV4:
1216
			r->extcommunity.data.ext_ip.addr.s_addr = uval;
1217
			r->extcommunity.data.ext_ip.val = ullval;
1218
			break;
1219
		case EXT_COMMUNITY_TRANS_FOUR_AS:
1220
			r->extcommunity.data.ext_as4.as4 = uval;
1221
			r->extcommunity.data.ext_as4.val = ullval;
1222
			break;
1223
		}
1224
		break;
1225
	case EXT_COMMUNITY_TRANS_OPAQUE:
1226
	case EXT_COMMUNITY_TRANS_EVPN:
1227
		errno = 0;
1228
		ullval = strtoull(word, &ep, 0);
1229
		if (word[0] == '\0' || *ep != '\0') {
1230
			fprintf(stderr, "Bad ext-community: bad value\n");
1231
			return (0);
1232
		}
1233
		if (errno == ERANGE && ullval > EXT_COMMUNITY_OPAQUE_MAX) {
1234
			fprintf(stderr, "Bad ext-community: too big\n");
1235
			return (0);
1236
		}
1237
		r->extcommunity.data.ext_opaq = ullval;
1238
		break;
1239
	case EXT_COMMUNITY_NON_TRANS_OPAQUE:
1240
		if (strcmp(word, "valid") == 0)
1241
			r->extcommunity.data.ext_opaq = EXT_COMMUNITY_OVS_VALID;
1242
		else if (strcmp(word, "invalid") == 0)
1243
			r->extcommunity.data.ext_opaq =
1244
			    EXT_COMMUNITY_OVS_INVALID;
1245
		else if (strcmp(word, "not-found") == 0)
1246
			r->extcommunity.data.ext_opaq =
1247
			    EXT_COMMUNITY_OVS_NOTFOUND;
1248
		else {
1249
			fprintf(stderr, "Bad ext-community value: %s\n", word);
1250
			return (0);
1251
		}
1252
		break;
1253
	}
1254
	r->extcommunity.type = type;
1255
1256
	/* verify type/subtype combo */
1257
	for (cp = iana_ext_comms; cp->subname != NULL; cp++) {
1258
		if (cp->type == r->extcommunity.type &&
1259
		    cp->subtype == r->extcommunity.subtype) {
1260
			r->extcommunity.flags |= EXT_COMMUNITY_FLAG_VALID;
1261
			if ((fs = calloc(1, sizeof(struct filter_set))) == NULL)
1262
				err(1, NULL);
1263
1264
			fs->type = ACTION_SET_EXT_COMMUNITY;
1265
			memcpy(&fs->action.ext_community, &r->extcommunity,
1266
			    sizeof(struct filter_extcommunity));
1267
1268
			TAILQ_INSERT_TAIL(&r->set, fs, entry);
1269
			return (1);
1270
		}
1271
	}
1272
1273
	fprintf(stderr, "Bad ext-community: bad format for type\n");
1274
	return (0);
1275
}
1276
1277
u_int
1278
getlargecommunity(const char *s)
1279
{
1280
	const char	*errstr;
1281
	u_int32_t	 uval;
1282
1283
	if (strcmp(s, "*") == 0)
1284
		return (COMMUNITY_ANY);
1285
1286
	uval = strtonum(s, 0, UINT_MAX, &errstr);
1287
	if (errstr)
1288
		errx(1, "Large Community is %s: %s", errstr, s);
1289
1290
	return (uval);
1291
}
1292
1293
int
1294
parse_largecommunity(const char *word, struct parse_result *r)
1295
{
1296
	struct filter_set *fs;
1297
	char		*p, *po = strdup(word);
1298
	char		*array[3] = { NULL, NULL, NULL };
1299
	char		*val;
1300
	int64_t		 as, ld1, ld2;
1301
	int		 i = 0;
1302
1303
	p = po;
1304
	while ((p != NULL) && (i < 3)) {
1305
		val = strsep(&p, ":");
1306
		array[i++] = val;
1307
	}
1308
1309
	if ((p != NULL) || !(array[0] && array[1] && array[2]))
1310
		errx(1, "Invalid Large-Community syntax");
1311
1312
	as   = getlargecommunity(array[0]);
1313
	ld1  = getlargecommunity(array[1]);
1314
	ld2  = getlargecommunity(array[2]);
1315
1316
	free(po);
1317
1318
	if ((fs = calloc(1, sizeof(struct filter_set))) == NULL)
1319
		err(1, NULL);
1320
	fs->type = ACTION_SET_LARGE_COMMUNITY;
1321
	fs->action.large_community.as = as;
1322
	fs->action.large_community.ld1 = ld1;
1323
	fs->action.large_community.ld2 = ld2;
1324
1325
	r->large_community.as = as;
1326
	r->large_community.ld1 = ld1;
1327
	r->large_community.ld2 = ld2;
1328
1329
	TAILQ_INSERT_TAIL(&r->set, fs, entry);
1330
	return (1);
1331
}
1332
1333
int
1334
parse_nexthop(const char *word, struct parse_result *r)
1335
{
1336
	struct filter_set	*fs;
1337
1338
	if ((fs = calloc(1, sizeof(struct filter_set))) == NULL)
1339
		err(1, NULL);
1340
1341
	if (strcmp(word, "blackhole") == 0)
1342
		fs->type = ACTION_SET_NEXTHOP_BLACKHOLE;
1343
	else if (strcmp(word, "reject") == 0)
1344
		fs->type = ACTION_SET_NEXTHOP_REJECT;
1345
	else if (strcmp(word, "no-modify") == 0)
1346
		fs->type = ACTION_SET_NEXTHOP_NOMODIFY;
1347
	else if (parse_addr(word, &fs->action.nexthop)) {
1348
		fs->type = ACTION_SET_NEXTHOP;
1349
	} else {
1350
		free(fs);
1351
		return (0);
1352
	}
1353
1354
	TAILQ_INSERT_TAIL(&r->set, fs, entry);
1355
	return (1);
1356
}
1357
1358
int
1359
bgpctl_getopt(int *argc, char **argv[], int type)
1360
{
1361
	int	  ch;
1362
1363
	optind = optreset = 1;
1364
	while ((ch = getopt((*argc) + 1, (*argv) - 1, "46o:")) != -1) {
1365
		switch (ch) {
1366
		case '4':
1367
			res.flags = (res.flags | F_IPV4) & ~F_IPV6;
1368
			break;
1369
		case '6':
1370
			res.flags = (res.flags | F_IPV6) & ~F_IPV4;
1371
			break;
1372
		case 'o':
1373
			res.irr_outdir = optarg;
1374
			break;
1375
		default:
1376
			usage();
1377
			/* NOTREACHED */
1378
		}
1379
	}
1380
1381
	if (optind > 1) {
1382
		(*argc) -= (optind - 1);
1383
		(*argv) += (optind - 1);
1384
1385
		/* need to move one backwards as calling code moves forward */
1386
		(*argc)++;
1387
		(*argv)--;
1388
		return (1);
1389
	} else
1390
		return (0);
1391
}