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

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