GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/switchctl/parser.c Lines: 0 144 0.0 %
Date: 2017-11-13 Branches: 0 146 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: parser.c,v 1.8 2017/08/01 13:11:11 deraadt Exp $	*/
2
3
/*
4
 * Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org>
5
 * Copyright (c) 2004 Esben Norby <norby@openbsd.org>
6
 * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
7
 *
8
 * Permission to use, copy, modify, and distribute this software for any
9
 * purpose with or without fee is hereby granted, provided that the above
10
 * copyright notice and this permission notice appear in all copies.
11
 *
12
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19
 */
20
21
#include <sys/types.h>
22
#include <sys/socket.h>
23
#include <sys/queue.h>
24
#include <sys/tree.h>
25
#include <sys/un.h>
26
27
#include <err.h>
28
#include <errno.h>
29
#include <limits.h>
30
#include <stdio.h>
31
#include <stdlib.h>
32
#include <string.h>
33
#include <event.h>
34
#include <netdb.h>
35
36
#include "switchd.h"
37
#include "ofp_map.h"
38
#include "parser.h"
39
40
enum token_type {
41
	NOTOKEN,
42
	ENDTOKEN,
43
	KEYWORD,
44
	PATH,
45
	ADDRESS,
46
	URI,
47
	TABLE,
48
	FLOWADD,
49
	FLOWDELETE,
50
	FLOWMODIFY,
51
	FLOWAPPLY,
52
	FLOWWRITE,
53
	FLOWMATCH,
54
	MATCHINPORT,
55
	ACTIONOUTPUT,
56
};
57
58
struct token {
59
	enum token_type		 type;
60
	const char		*keyword;
61
	int			 value;
62
	const struct token	*next;
63
};
64
65
static const struct token t_main[];
66
static const struct token t_reset[];
67
static const struct token t_log[];
68
static const struct token t_load[];
69
static const struct token t_show[];
70
static const struct token t_switch[];
71
#if 0
72
static const struct token t_switchreq[];
73
#endif
74
static const struct token t_table[];
75
static const struct token t_dump[];
76
static const struct token t_flow[];
77
static const struct token t_flowmod[];
78
static const struct token t_flowmatch[];
79
static const struct token t_matchinport[];
80
static const struct token t_flowaction[];
81
static const struct token t_actionoutput[];
82
static const struct token t_connect[];
83
static const struct token t_disconnect[];
84
static const struct token t_forward_to[];
85
static const struct token t_uri[];
86
87
static const struct token t_main[] = {
88
	{ KEYWORD,	"connect",	CONNECT,	t_connect },
89
	{ KEYWORD,	"disconnect",	DISCONNECT,	t_disconnect },
90
	{ KEYWORD,	"dump",		NONE,		t_dump },
91
	{ KEYWORD,	"flow",		NONE,		t_flow },
92
	{ KEYWORD,	"load",		LOAD,		t_load },
93
	{ KEYWORD,	"log",		NONE,		t_log },
94
	{ KEYWORD,	"monitor",	MONITOR,	NULL },
95
	{ KEYWORD,	"reload",	RELOAD,		NULL },
96
	{ KEYWORD,	"reset",	NONE,		t_reset },
97
	{ KEYWORD,	"show",		NONE,		t_show },
98
	{ KEYWORD,	"switch",	NONE,		t_switch },
99
	{ KEYWORD,	"table",	NONE,		t_table },
100
	{ ENDTOKEN,	"",		NONE,		NULL }
101
};
102
103
static const struct token t_log[] = {
104
	{ KEYWORD,	"verbose",	LOG_VERBOSE,	NULL },
105
	{ KEYWORD,	"brief",	LOG_BRIEF,	NULL },
106
	{ ENDTOKEN,	"",		NONE,		NULL }
107
};
108
109
static const struct token t_reset[] = {
110
	{ KEYWORD,	"all",		RESETALL,	NULL },
111
	{ ENDTOKEN,	"",		NONE,		NULL }
112
};
113
114
static const struct token t_load[] = {
115
	{ PATH,		"",		NONE,		NULL },
116
	{ ENDTOKEN,	"",		NONE,		NULL }
117
};
118
119
static const struct token  t_table[] = {
120
	{ TABLE,	"",		NONE,		t_main },
121
	{ ENDTOKEN,	"",		NONE,		NULL }
122
};
123
124
static const struct token  t_switch[] = {
125
	{ URI,		"",		NONE,		t_main },
126
	{ ENDTOKEN,	"",		NONE,		NULL }
127
};
128
129
#if 0
130
static const struct token  t_switchreq[] = {
131
	{ KEYWORD,	"dump",		NONE,		t_dump },
132
	{ KEYWORD,	"flow",		NONE,		t_flow },
133
	{ ENDTOKEN,	"",		NONE,		NULL }
134
};
135
#endif
136
137
static const struct token t_dump[] = {
138
	{ KEYWORD,	"desc",		DUMP_DESC,	NULL },
139
	{ KEYWORD,	"features",	DUMP_FEATURES,	NULL },
140
	{ KEYWORD,	"flows",	DUMP_FLOWS,	NULL },
141
	{ KEYWORD,	"tables",	DUMP_TABLES,	NULL },
142
	{ ENDTOKEN,	"",		NONE,		NULL }
143
};
144
145
static const struct token t_flow[] = {
146
	{ FLOWADD,	"add",		FLOW_ADD,	t_flowmod },
147
	{ FLOWDELETE,	"delete",	FLOW_DELETE,	t_flowmod },
148
	{ FLOWMODIFY,	"modify",	FLOW_MODIFY,	t_flowmod },
149
	{ ENDTOKEN,	"",		NONE,		NULL }
150
};
151
152
static const struct token t_flowmod[] = {
153
	{ NOTOKEN,	"",		NONE,		NULL },
154
	{ FLOWAPPLY,	"apply",	NONE,		t_flowaction },
155
	{ FLOWWRITE,	"write",	NONE,		t_flowaction },
156
	{ FLOWMATCH,	"match",	NONE,		t_flowmatch },
157
	{ ENDTOKEN,	"",		NONE,		NULL }
158
};
159
160
static const struct token t_flowmatch[] = {
161
	{ NOTOKEN,	"",		NONE,		t_flowmod },
162
	{ KEYWORD,	"inport",	NONE,		t_matchinport },
163
	{ ENDTOKEN,	"",		NONE,		NULL }
164
};
165
166
static const struct token t_matchinport[] = {
167
	{ MATCHINPORT,	"",		NONE,		t_flowmatch },
168
	{ ENDTOKEN,	"",		NONE,		NULL }
169
};
170
171
static const struct token t_flowaction[] = {
172
	{ NOTOKEN,	"",		NONE,		t_flowmod },
173
	{ KEYWORD,	"output",	NONE,		t_actionoutput },
174
	{ ENDTOKEN,	"",		NONE,		NULL }
175
};
176
177
static const struct token t_actionoutput[] = {
178
	{ ACTIONOUTPUT,	"",		NONE,		t_flowaction },
179
	{ ENDTOKEN,	"",		NONE,		NULL }
180
};
181
182
static const struct token t_show[] = {
183
	{ KEYWORD,	"summary",	SHOW_SUM,	NULL },
184
	{ KEYWORD,	"switches",	SHOW_SWITCHES,	NULL },
185
	{ KEYWORD,	"macs",		SHOW_MACS,	NULL },
186
	{ ENDTOKEN,	"",		NONE,		NULL }
187
};
188
189
static const struct token t_connect[] = {
190
	{ ADDRESS,	"",		NONE,		t_forward_to },
191
	{ ENDTOKEN,	"",		NONE,		NULL }
192
};
193
static const struct token t_disconnect[] = {
194
	{ ADDRESS,	"",		NONE,		NULL },
195
	{ ENDTOKEN,	"",		NONE,		NULL }
196
};
197
static const struct token t_forward_to[] = {
198
	{ NOTOKEN,	"",		NONE,		NULL },
199
	{ KEYWORD,	"forward-to",	NONE,		t_uri },
200
	{ ENDTOKEN,	"",		NONE,		NULL }
201
};
202
203
static const struct token  t_uri[] = {
204
	{ URI,		"",		NONE,		NULL },
205
	{ ENDTOKEN,	"",		NONE,		NULL }
206
};
207
208
static struct parse_result	 res;
209
210
const struct token	*match_token(char *, const struct token [], int);
211
void			 show_valid_args(const struct token [], int);
212
int			 parse_addr(const char *,
213
			    struct sockaddr_storage *);
214
215
struct parse_result *
216
parse(int argc, char *argv[])
217
{
218
	const struct token	*table = t_main;
219
	const struct token	*match;
220
221
	bzero(&res, sizeof(res));
222
223
	res.table = OFP_TABLE_ID_ALL;
224
225
	while (argc >= 0) {
226
		if ((match = match_token(argv[0], table, 0)) == NULL) {
227
			fprintf(stderr, "valid commands/args:\n");
228
			show_valid_args(table, 0);
229
			return (NULL);
230
		}
231
232
		argc--;
233
		argv++;
234
235
		if (match->type == NOTOKEN || match->next == NULL)
236
			break;
237
238
		table = match->next;
239
	}
240
241
	if (argc > 0) {
242
		fprintf(stderr, "superfluous argument: %s\n", argv[0]);
243
		return (NULL);
244
	}
245
246
	return (&res);
247
}
248
249
int
250
parse_addr(const char *word, struct sockaddr_storage *ss)
251
{
252
	struct addrinfo		 hints, *ai;
253
	struct sockaddr_un	*un;
254
255
	memset(ss, 0, sizeof(*ss));
256
257
	/* device */
258
	if (*word == '/') {
259
		un = (struct sockaddr_un *)ss;
260
		if (strlcpy(un->sun_path, word, sizeof(un->sun_path)) >=
261
		    sizeof(un->sun_path)) {
262
			warnx("invalid path");
263
			return (-1);
264
		}
265
		un->sun_family = AF_LOCAL;
266
		un->sun_len = sizeof(*un);
267
		return (0);
268
	}
269
270
	/* address */
271
	memset(&hints, 0, sizeof(hints));
272
	hints.ai_socktype = SOCK_DGRAM; /* dummy */
273
	hints.ai_family = PF_UNSPEC;
274
	hints.ai_flags = AI_NUMERICHOST;
275
	if (getaddrinfo(word, "0", &hints, &ai) == 0) {
276
		if (ai->ai_addrlen > sizeof(*ss)) {
277
			warnx("invalid address length");
278
			return (-1);
279
		}
280
		memcpy(ss, ai->ai_addr, ai->ai_addrlen);
281
		ss->ss_len = ai->ai_addrlen;
282
		freeaddrinfo(ai);
283
		return (0);
284
	}
285
286
	/* FQDN */
287
	memset(&hints, 0, sizeof(hints));
288
	hints.ai_socktype = SOCK_DGRAM; /* dummy */
289
	hints.ai_family = PF_UNSPEC;
290
	hints.ai_flags = AI_ADDRCONFIG;
291
	if (getaddrinfo(word, "0", &hints, &ai) == 0) {
292
		/* Pick first name only */
293
		if (ai->ai_addrlen > sizeof(*ss)) {
294
			warnx("invalid address length");
295
			return (-1);
296
		}
297
		memcpy(ss, ai->ai_addr, ai->ai_addrlen);
298
		ss->ss_len = ai->ai_addrlen;
299
		freeaddrinfo(ai);
300
		return (0);
301
	}
302
303
	return (-1);
304
}
305
306
307
const struct token *
308
match_token(char *word, const struct token table[], int level)
309
{
310
	unsigned int		 i, j, match = 0;
311
	int64_t			 val;
312
	struct constmap		*cm;
313
	const char		*errstr = NULL;
314
	const struct token	*t = NULL;
315
	size_t			 len;
316
317
	for (i = 0; table[i].type != ENDTOKEN; i++) {
318
		switch (table[i].type) {
319
		case NOTOKEN:
320
			if (word == NULL || strlen(word) == 0) {
321
				match++;
322
				t = &table[i];
323
			}
324
			break;
325
		case KEYWORD:
326
		case FLOWADD:
327
		case FLOWDELETE:
328
		case FLOWMODIFY:
329
		case FLOWMATCH:
330
		case FLOWAPPLY:
331
		case FLOWWRITE:
332
			if (word != NULL && strncmp(word, table[i].keyword,
333
			    strlen(word)) == 0) {
334
				match++;
335
				t = &table[i];
336
				if (t->value)
337
					res.action = t->value;
338
				switch (table[i].type) {
339
				case FLOWADD:
340
				case FLOWDELETE:
341
				case FLOWMODIFY:
342
					if ((res.fbuf =
343
					    oflowmod_open(&res.fctx,
344
					    NULL, NULL, 0)) == NULL)
345
						goto flowerr;
346
347
					/* Update header */
348
					if (table[i].type == FLOWDELETE)
349
						res.fctx.ctx_fm->fm_command =
350
						    OFP_FLOWCMD_DELETE;
351
					else if (table[i].type == FLOWMODIFY)
352
						res.fctx.ctx_fm->fm_command =
353
						    OFP_FLOWCMD_MODIFY;
354
					break;
355
				case FLOWAPPLY:
356
					val = OFP_INSTRUCTION_T_APPLY_ACTIONS;
357
					if (oflowmod_instruction(&res.fctx,
358
					    val) == -1)
359
						goto flowerr;
360
					break;
361
				case FLOWWRITE:
362
					val = OFP_INSTRUCTION_T_WRITE_ACTIONS;
363
					if (oflowmod_instruction(&res.fctx,
364
					    val) == -1)
365
						goto flowerr;
366
					break;
367
				case FLOWMATCH:
368
					if (oflowmod_mopen(&res.fctx) == -1)
369
						goto flowerr;
370
					break;
371
				default:
372
					break;
373
				}
374
			}
375
			break;
376
		case MATCHINPORT:
377
		case ACTIONOUTPUT:
378
			if (!match && word != NULL && strlen(word) > 0) {
379
				match++;
380
				t = &table[i];
381
382
				val = -1;
383
384
				/* Is the port a keyword? */
385
				cm = ofp_port_map;
386
				for (j = 0; cm[j].cm_name != NULL; j++) {
387
					if (strcasecmp(cm[j].cm_name,
388
					    word) == 0) {
389
						val = cm[j].cm_type;
390
						break;
391
					}
392
				}
393
394
				/* Is the port a number? */
395
				if (val == -1) {
396
					val = strtonum(word, 1,
397
					    UINT32_MAX, &errstr);
398
					if (errstr != NULL)
399
						val = -1;
400
				}
401
402
				if (val == -1) {
403
					fprintf(stderr,
404
					    "could not parse port:"
405
					    " %s\n", word);
406
					return (NULL);
407
				}
408
409
				switch (table[i].type) {
410
				case MATCHINPORT:
411
					if (oxm_inport(res.fbuf, val) == -1)
412
						goto flowerr;
413
					break;
414
				case ACTIONOUTPUT:
415
					if (action_output(res.fbuf, val,
416
					    OFP_CONTROLLER_MAXLEN_MAX) == -1)
417
						goto flowerr;
418
					break;
419
				default:
420
					break;
421
				}
422
			}
423
			break;
424
		case PATH:
425
			if (!match && word != NULL && strlen(word) > 0) {
426
				res.path = strdup(word);
427
				match++;
428
				t = &table[i];
429
			}
430
			break;
431
		case ADDRESS:
432
			if (!match && word != NULL && strlen(word) > 0) {
433
				parse_addr(word, &res.addr);
434
				match++;
435
				t = &table[i];
436
			}
437
			break;
438
		case TABLE:
439
			if (word == NULL)
440
				break;
441
			res.table = strtonum(word, 0,
442
			    OFP_TABLE_ID_MAX, &errstr);
443
			if (errstr)
444
				res.table = OFP_TABLE_ID_ALL;
445
			t = &table[i];
446
			match++;
447
			break;
448
		case URI:
449
			if (!match && word != NULL && strlen(word) > 0) {
450
				len = 4;
451
				if (strncmp(word, "tcp:", len) == 0)
452
					res.uri.swa_type = SWITCH_CONN_TCP;
453
				else if (strncmp(word, "tls:", len) == 0)
454
					res.uri.swa_type = SWITCH_CONN_TLS;
455
				else {
456
					/* set the default */
457
					res.uri.swa_type = SWITCH_CONN_TCP;
458
					len = 0;
459
				}
460
				if (parsehostport(word + len,
461
				    (struct sockaddr *)&res.uri.swa_addr,
462
				    sizeof(res.uri.swa_addr)) != 0) {
463
					fprintf(stderr,
464
					    "could not parse address: %s\n",
465
					    word);
466
					return (NULL);
467
				}
468
				match++;
469
				t = &table[i];
470
			}
471
			break;
472
		case ENDTOKEN:
473
			break;
474
		}
475
	}
476
477
	if (match != 1) {
478
		if (word == NULL)
479
			fprintf(stderr, "missing argument:\n");
480
		else if (match > 1)
481
			fprintf(stderr, "ambiguous argument: %s\n", word);
482
		else if (match < 1) {
483
			if (level == 0 &&
484
			    table[0].type == NOTOKEN && table[0].next)
485
				return (match_token(word, table[0].next, 1));
486
			else
487
				fprintf(stderr, "unknown argument: %s\n", word);
488
		}
489
		return (NULL);
490
	}
491
492
	return (t);
493
494
 flowerr:
495
	(void)oflowmod_err(&res.fctx, __func__, __LINE__);
496
	fprintf(stderr, "flow invalid\n");
497
	return (NULL);
498
}
499
500
void
501
show_valid_args(const struct token table[], int level)
502
{
503
	int	i;
504
505
	for (i = 0; table[i].type != ENDTOKEN; i++) {
506
		switch (table[i].type) {
507
		case NOTOKEN:
508
			if (level == 0)
509
				fprintf(stderr, "  <cr>\n");
510
			break;
511
		case KEYWORD:
512
		case FLOWADD:
513
		case FLOWDELETE:
514
		case FLOWMODIFY:
515
		case FLOWMATCH:
516
		case FLOWAPPLY:
517
		case FLOWWRITE:
518
			fprintf(stderr, "  %s\n", table[i].keyword);
519
			break;
520
		case MATCHINPORT:
521
		case ACTIONOUTPUT:
522
			fprintf(stderr, "  <port>\n");
523
			break;
524
		case PATH:
525
			fprintf(stderr, "  <path>\n");
526
			break;
527
		case ADDRESS:
528
			fprintf(stderr, "  <address>\n");
529
			break;
530
		case TABLE:
531
			fprintf(stderr, "  <table>\n");
532
			break;
533
		case URI:
534
			fprintf(stderr, "  <uri>\n");
535
			break;
536
		case ENDTOKEN:
537
			break;
538
		}
539
	}
540
541
	if (level == 0 && table[0].type == NOTOKEN && table[0].next)
542
		return (show_valid_args(table[0].next, 1));
543
}