GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/switchd/switchd.c Lines: 69 268 25.7 %
Date: 2017-11-13 Branches: 20 158 12.7 %

Line Branch Exec Source
1
/*	$OpenBSD: switchd.c,v 1.15 2017/01/09 14:49:22 reyk Exp $	*/
2
3
/*
4
 * Copyright (c) 2013-2016 Reyk Floeter <reyk@openbsd.org>
5
 *
6
 * Permission to use, copy, modify, and distribute this software for any
7
 * purpose with or without fee is hereby granted, provided that the above
8
 * copyright notice and this permission notice appear in all copies.
9
 *
10
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
 */
18
19
#include <sys/types.h>
20
#include <sys/socket.h>
21
#include <sys/uio.h>
22
#include <sys/un.h>
23
#include <sys/queue.h>
24
25
#include <arpa/inet.h>
26
#include <netinet/in.h>
27
#include <netinet/tcp.h>
28
29
#include <limits.h>
30
#include <stdio.h>
31
#include <stdlib.h>
32
#include <unistd.h>
33
#include <string.h>
34
#include <signal.h>
35
#include <syslog.h>
36
#include <fcntl.h>
37
#include <errno.h>
38
#include <pwd.h>
39
#include <event.h>
40
41
#include "switchd.h"
42
43
void	 parent_shutdown(struct switchd *);
44
void	 parent_sig_handler(int, short, void *);
45
int	 parent_dispatch_ofp(int, struct privsep_proc *, struct imsg *);
46
int	 parent_dispatch_control(int, struct privsep_proc *, struct imsg *);
47
int	 parent_configure(struct switchd *);
48
int	 parent_reload(struct switchd *);
49
void	 parent_connect(struct privsep *, struct switch_client *);
50
void	 parent_connected(int, short, void *);
51
void	 parent_disconnect(struct privsep *, struct switch_client *);
52
53
__dead void	usage(void);
54
55
static struct privsep_proc procs[] = {
56
	{ "ofp",	PROC_OFP,	NULL, ofp },
57
	{ "control",	PROC_CONTROL,	parent_dispatch_control, control },
58
	{ "ofcconn",	PROC_OFCCONN,	NULL, ofcconn }
59
};
60
61
__dead void
62
usage(void)
63
{
64
	extern const char	*__progname;
65
	fprintf(stderr, "usage: %s [-dnv] [-c cachesize]  [-D macro=value] "
66
	    "[-f file] [-t timeout]\n",
67
	    __progname);
68
	exit(1);
69
}
70
71
int
72
main(int argc, char *argv[])
73
{
74
	struct switchd		*sc = NULL;
75
	struct privsep		*ps = NULL;
76
	struct switch_server	*srv;
77
24
	const char		*errstr = NULL;
78
	int			 c;
79
	int			 debug = 0, verbose = 0;
80
	uint32_t		 opts = 0;
81
	unsigned int		 cache = SWITCHD_CACHE_MAX;
82
	unsigned int		 timeout = SWITCHD_CACHE_TIMEOUT;
83
	const char		*conffile = SWITCHD_CONFIG;
84
12
	const char		*errp, *title = NULL;
85
	enum privsep_procid	 proc_id = PROC_PARENT;
86
	int			 argc0 = argc, proc_instance = 0;
87
88
12
	log_init(1, LOG_DAEMON);
89
90
120
	while ((c = getopt(argc, argv, "c:dD:f:hI:nP:t:v")) != -1) {
91


48
		switch (c) {
92
		case 'c':
93
			cache = strtonum(optarg, 1, UINT32_MAX, &errstr);
94
			if (errstr != NULL) {
95
				log_warn("max cache size: %s", errstr);
96
				usage();
97
			}
98
			break;
99
		case 'd':
100
12
			debug++;
101
12
			break;
102
		case 'D':
103
			if (cmdline_symset(optarg) < 0)
104
				log_warnx("could not parse macro definition %s",
105
				    optarg);
106
			break;
107
		case 'f':
108
12
			conffile = optarg;
109
12
			break;
110
		case 'I':
111
			proc_instance = strtonum(optarg, 0,
112
			    PROC_MAX_INSTANCES, &errp);
113
			if (errp)
114
				fatalx("invalid process instance");
115
			break;
116
		case 'n':
117
			opts |= SWITCHD_OPT_NOACTION;
118
			break;
119
		case 'P':
120
			title = optarg;
121
			proc_id = proc_getid(procs, nitems(procs), title);
122
			if (proc_id == PROC_MAX)
123
				fatalx("invalid process name");
124
			break;
125
		case 't':
126
			timeout = strtonum(optarg, 0, UINT32_MAX, &errstr);
127
			if (errstr != NULL) {
128
				log_warn("cache timeout: %s", errstr);
129
				usage();
130
			}
131
			break;
132
		case 'v':
133
24
			verbose++;
134
24
			opts |= SWITCHD_OPT_VERBOSE;
135
24
			break;
136
		default:
137
			usage();
138
		}
139
	}
140
141
12
	if ((sc = calloc(1, sizeof(*sc))) == NULL)
142
		fatal("calloc");
143
144
12
	if (strlcpy(sc->sc_conffile, conffile, PATH_MAX) >= PATH_MAX)
145
		fatal("config file exceeds PATH_MAX");
146
147
12
	sc->sc_cache_max = cache;
148
12
	sc->sc_cache_timeout = timeout;
149
12
	sc->sc_opts = opts;
150
151
12
	srv = &sc->sc_server;
152
12
	srv->srv_sc = sc;
153
154
12
	ps = &sc->sc_ps;
155
12
	ps->ps_env = sc;
156
12
	TAILQ_INIT(&ps->ps_rcsocks);
157
12
	TAILQ_INIT(&sc->sc_clients);
158
159
12
	if (parse_config(sc->sc_conffile, sc) == -1) {
160
		proc_kill(&sc->sc_ps);
161
		exit(1);
162
	}
163
164
12
	if (opts & SWITCHD_OPT_NOACTION) {
165
		fprintf(stderr, "configuration OK\n");
166
		proc_kill(&sc->sc_ps);
167
		exit(0);
168
	}
169
170
	/* check for root privileges */
171
12
	if (geteuid())
172
		fatalx("need root privileges");
173
174
12
	if ((ps->ps_pw =  getpwnam(SWITCHD_USER)) == NULL)
175
		fatalx("unknown user " SWITCHD_USER);
176
177
12
	log_init(debug, LOG_DAEMON);
178
12
	log_setverbose(verbose);
179
180
	/* Configure the control socket */
181
12
	ps->ps_csock.cs_name = SWITCHD_SOCKET;
182
12
	ps->ps_instance = proc_instance;
183
12
	if (title)
184
		ps->ps_title[proc_id] = title;
185
186
	/* Only the parent returns. */
187
12
	proc_init(ps, procs, nitems(procs), argc0, argv, proc_id);
188
189

12
	if (!debug && daemon(0, 0) == -1)
190
		fatal("failed to daemonize");
191
192
12
	log_procinit("parent");
193
194
	/*
195
	 * pledge in the parent process:
196
	 * stdio - for malloc and basic I/O including events.
197
	 * rpath - for reload to open and read the configuration files.
198
	 * wpath - for accessing the /dev/switch device.
199
	 * inet - for opening OpenFlow and device sockets.
200
	 * dns - for resolving host in the configuration files.
201
	 * sendfd - send sockets to child processes on reload.
202
	 */
203
12
	if (pledge("stdio rpath wpath inet dns sendfd flock cpath", NULL) == -1)
204
		fatal("pledge");
205
206
12
	event_init();
207
208
12
	signal_set(&ps->ps_evsigint, SIGINT, parent_sig_handler, ps);
209
12
	signal_set(&ps->ps_evsigterm, SIGTERM, parent_sig_handler, ps);
210
12
	signal_set(&ps->ps_evsighup, SIGHUP, parent_sig_handler, ps);
211
12
	signal_set(&ps->ps_evsigpipe, SIGPIPE, parent_sig_handler, ps);
212
12
	signal_set(&ps->ps_evsigusr1, SIGUSR1, parent_sig_handler, ps);
213
214
12
	signal_add(&ps->ps_evsigint, NULL);
215
12
	signal_add(&ps->ps_evsigterm, NULL);
216
12
	signal_add(&ps->ps_evsighup, NULL);
217
12
	signal_add(&ps->ps_evsigpipe, NULL);
218
12
	signal_add(&ps->ps_evsigusr1, NULL);
219
220
12
	proc_connect(ps);
221
222
12
	if (parent_configure(sc) == -1)
223
		fatalx("configuration failed");
224
225
12
	event_dispatch();
226
227
12
	log_debug("%d parent exiting", getpid());
228
229
12
	return (0);
230
12
}
231
232
int
233
switchd_socket(struct sockaddr *sock, int reuseport)
234
{
235
	int s = -1, val;
236
	struct linger lng;
237
238
	if ((s = socket(sock->sa_family, SOCK_STREAM | SOCK_NONBLOCK,
239
	    IPPROTO_TCP)) == -1)
240
		goto bad;
241
242
	/*
243
	 * Socket options
244
	 */
245
	bzero(&lng, sizeof(lng));
246
	if (setsockopt(s, SOL_SOCKET, SO_LINGER, &lng, sizeof(lng)) == -1)
247
		goto bad;
248
	if (reuseport) {
249
		val = 1;
250
		if (setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &val,
251
		    sizeof(int)) == -1)
252
			goto bad;
253
	}
254
255
	/*
256
	 * TCP options
257
	 */
258
	val = 1;
259
	if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY,
260
	    &val, sizeof(val)) == -1)
261
		goto bad;
262
263
	return (s);
264
265
 bad:
266
	if (s != -1)
267
		close(s);
268
	return (-1);
269
}
270
271
int
272
switchd_listen(struct sockaddr *sock)
273
{
274
	int s;
275
276
	if ((s = switchd_socket(sock, 1)) == -1)
277
		return (-1);
278
279
	if (bind(s, sock, sock->sa_len) == -1)
280
		goto bad;
281
	if (listen(s, 10) == -1)
282
		goto bad;
283
284
	return (s);
285
286
 bad:
287
	close(s);
288
	return (-1);
289
}
290
291
int
292
switchd_tap(void)
293
{
294
24
	char	 path[PATH_MAX];
295
	int	 i, fd;
296
297
24
	for (i = 0; i < SWITCHD_MAX_TAP; i++) {
298
12
		snprintf(path, PATH_MAX, "/dev/tap%d", i);
299
12
		fd = open(path, O_RDWR | O_NONBLOCK);
300
12
		if (fd != -1)
301
12
			return (fd);
302
	}
303
304
	return (-1);
305
12
}
306
307
struct switch_connection *
308
switchd_connbyid(struct switchd *sc, unsigned int id, unsigned int instance)
309
{
310
	struct switch_connection	*con;
311
312
	TAILQ_FOREACH(con, &sc->sc_conns, con_entry) {
313
		if (con->con_id == id && con->con_instance == instance)
314
			return (con);
315
	}
316
317
	return (NULL);
318
}
319
320
struct switch_connection *
321
switchd_connbyaddr(struct switchd *sc, struct sockaddr *sa)
322
{
323
	struct switch_connection	*con;
324
325
	TAILQ_FOREACH(con, &sc->sc_conns, con_entry) {
326
		if (sockaddr_cmp((struct sockaddr *)
327
		    &con->con_peer, sa, -1) == 0)
328
			return (con);
329
	}
330
331
	return (NULL);
332
}
333
334
void
335
parent_sig_handler(int sig, short event, void *arg)
336
{
337
24
	struct privsep	*ps = arg;
338
339

12
	switch (sig) {
340
	case SIGHUP:
341
		log_info("%s: reload requested with SIGHUP", __func__);
342
343
		/*
344
		 * This is safe because libevent uses async signal handlers
345
		 * that run in the event loop and not in signal context.
346
		 */
347
		parent_reload(ps->ps_env);
348
		break;
349
	case SIGPIPE:
350
		log_info("%s: ignoring SIGPIPE", __func__);
351
		break;
352
	case SIGUSR1:
353
		log_info("%s: ignoring SIGUSR1", __func__);
354
		break;
355
	case SIGTERM:
356
	case SIGINT:
357
12
		parent_shutdown(ps->ps_env);
358
12
		break;
359
	default:
360
		fatalx("unexpected signal");
361
	}
362
}
363
364
int
365
parent_configure(struct switchd *sc)
366
{
367
	struct switch_client	*swc, *swcn;
368
	int			 fd;
369
370
24
	if ((fd = switchd_tap()) == -1)
371
		fatal("%s: tap", __func__);
372
12
	proc_compose_imsg(&sc->sc_ps, PROC_OFP, -1,
373
	    IMSG_TAPFD, -1, fd, NULL, 0);
374
375
24
	TAILQ_FOREACH_SAFE(swc, &sc->sc_clients, swc_next, swcn) {
376
		parent_connect(&sc->sc_ps, swc);
377
	}
378
379
12
	return (0);
380
}
381
382
int
383
parent_reload(struct switchd *sc)
384
{
385
	struct switchd			 newconf;
386
	struct switch_client		*swc, *oswc, *swcn;
387
388
	memset(&newconf, 0, sizeof(newconf));
389
	TAILQ_INIT(&newconf.sc_clients);
390
	TAILQ_INIT(&newconf.sc_conns);
391
392
	if (parse_config(sc->sc_conffile, &newconf) != -1) {
393
		TAILQ_FOREACH_SAFE(swc, &sc->sc_clients, swc_next, swcn) {
394
			TAILQ_FOREACH(oswc, &newconf.sc_clients, swc_next) {
395
				if (sockaddr_cmp((struct sockaddr *)
396
				    &oswc->swc_addr.swa_addr,
397
				    (struct sockaddr *)
398
				    &swc->swc_addr.swa_addr, -1) == 0) {
399
					TAILQ_REMOVE(&newconf.sc_clients,
400
					    oswc, swc_next);
401
					break;
402
				}
403
			}
404
			if (oswc == NULL) {
405
				/* Removed */
406
				parent_disconnect(&sc->sc_ps, swc);
407
			} else {
408
				/* Keep the existing one */
409
				TAILQ_REMOVE(&newconf.sc_clients,
410
				    oswc, swc_next);
411
				free(oswc);
412
			}
413
		}
414
		TAILQ_FOREACH_SAFE(swc, &newconf.sc_clients, swc_next, swcn) {
415
			TAILQ_REMOVE(&newconf.sc_clients, swc, swc_next);
416
			TAILQ_INSERT_TAIL(&sc->sc_clients, swc, swc_next);
417
418
			parent_connect(&sc->sc_ps, swc);
419
		}
420
	}
421
422
	return (0);
423
}
424
425
int
426
parent_dispatch_control(int fd, struct privsep_proc *p, struct imsg *imsg)
427
{
428
	struct switch_client	*swc, *oswc;
429
	struct privsep		*ps = p->p_ps;
430
	struct switchd		*sc = ps->ps_env;
431
432
	switch (imsg->hdr.type) {
433
	case IMSG_CTL_CONNECT:
434
	case IMSG_CTL_DISCONNECT:
435
		IMSG_SIZE_CHECK(imsg, swc);
436
437
		/* Need to allocate it in case it is reused */
438
		if ((swc = calloc(1, sizeof(*swc))) == NULL) {
439
			log_warnx("%s: calloc", __func__);
440
			return (0);
441
		}
442
		memcpy(swc, imsg->data, sizeof(*swc));
443
		memset(&swc->swc_ev, 0, sizeof(swc->swc_ev));
444
445
		if (imsg->hdr.type == IMSG_CTL_CONNECT) {
446
			TAILQ_INSERT_TAIL(&sc->sc_clients, swc, swc_next);
447
			parent_connect(p->p_ps, swc);
448
		} else {
449
			TAILQ_FOREACH(oswc, &sc->sc_clients, swc_next) {
450
				if (sockaddr_cmp((struct sockaddr *)
451
				    &oswc->swc_addr.swa_addr,
452
				    (struct sockaddr *)
453
				    &swc->swc_addr.swa_addr, -1) == 0) {
454
					parent_disconnect(ps, oswc);
455
					break;
456
				}
457
			}
458
			if (oswc == NULL)
459
				log_warnx("client %s is not connected",
460
				    print_host(&swc->swc_addr.swa_addr,
461
				    NULL, 0));
462
			free(swc);
463
		}
464
		return (0);
465
	default:
466
		break;
467
	}
468
469
	return (-1);
470
}
471
472
void
473
parent_shutdown(struct switchd *sc)
474
{
475
12
	proc_kill(&sc->sc_ps);
476
477
	free(sc);
478
479
	log_warnx("parent terminating");
480
	exit(0);
481
}
482
483
void
484
parent_connect(struct privsep *ps, struct switch_client *swc)
485
{
486
	struct switchd		*sc = ps->ps_env;
487
	struct sockaddr_storage	*ss;
488
	struct sockaddr_un	*un;
489
	struct sockaddr_in	*sin4;
490
	struct sockaddr_in6	*sin6;
491
	int			 fd = -1;
492
	struct timeval		 tv;
493
494
	ss = &swc->swc_addr.swa_addr;
495
496
	if (ss->ss_len == 0) {
497
		log_warnx("%s: invalid address", __func__);
498
		goto fail;
499
	}
500
	swc->swc_arg = ps;
501
	memset(&swc->swc_ev, 0, sizeof(swc->swc_ev));
502
503
	switch (ss->ss_family) {
504
	case AF_LOCAL:
505
		un = (struct sockaddr_un *)ss;
506
507
		/* restrict the opening path to /dev/switch* */
508
		if (strncmp(un->sun_path, "/dev/switch",
509
		    strlen("/dev/switch")) != 0) {
510
			log_warnx("%s: device path is wrong: %s", __func__,
511
			    un->sun_path);
512
			goto fail;
513
		}
514
515
		if ((fd = open(un->sun_path, O_RDWR | O_NONBLOCK)) == -1) {
516
			log_warn("%s: failed to open %s",
517
			    __func__, un->sun_path);
518
			goto fail;
519
		}
520
		break;
521
	case AF_INET:
522
	case AF_INET6:
523
		if (ss->ss_family == AF_INET) {
524
			sin4 = (struct sockaddr_in *)ss;
525
			if (sin4->sin_port == 0)
526
				sin4->sin_port = htons(SWITCHD_CTLR_PORT);
527
		} else if (ss->ss_family == AF_INET6) {
528
			sin6 = (struct sockaddr_in6 *)ss;
529
			if (sin6->sin6_port == 0)
530
				sin6->sin6_port = htons(SWITCHD_CTLR_PORT);
531
		}
532
533
		if ((fd = switchd_socket((struct sockaddr *)ss, 0)) == -1) {
534
			log_debug("%s: failed to get socket for %s", __func__,
535
			    print_host(ss, NULL, 0));
536
			goto fail;
537
		}
538
539
 retry:
540
		if (connect(fd, (struct sockaddr *)ss, ss->ss_len) == -1) {
541
			if (errno == EINTR)
542
				goto retry;
543
			if (errno == EINPROGRESS) {
544
				tv.tv_sec = SWITCHD_CONNECT_TIMEOUT;
545
				tv.tv_usec = 0;
546
				event_set(&swc->swc_ev, fd, EV_WRITE|EV_TIMEOUT,
547
				    parent_connected, swc);
548
				event_add(&swc->swc_ev, &tv);
549
				return;
550
			}
551
552
			log_warn("%s: failed to connect to %s, fd %d", __func__,
553
			    print_host(ss, NULL, 0), fd);
554
			goto fail;
555
		}
556
557
		break;
558
	}
559
560
	parent_connected(fd, 0, swc);
561
	return;
562
563
 fail:
564
	TAILQ_REMOVE(&sc->sc_clients, swc, swc_next);
565
	free(swc);
566
}
567
568
void
569
parent_connected(int fd, short event, void *arg)
570
{
571
	struct switch_client	*swc = arg;
572
	struct privsep		*ps = swc->swc_arg;
573
	struct switchd		*sc = ps->ps_env;
574
575
	if (event & EV_TIMEOUT) {
576
		log_debug("%s: failed to connect to %s", __func__,
577
		    print_host(&swc->swc_addr.swa_addr, NULL, 0));
578
		TAILQ_REMOVE(&sc->sc_clients, swc, swc_next);
579
		free(swc);
580
		return;
581
	}
582
583
	switch (swc->swc_target.swa_type) {
584
	case SWITCH_CONN_LOCAL:
585
		proc_compose_imsg(ps, PROC_OFP, -1, IMSG_CTL_CONNECT,
586
		    -1, fd, swc, sizeof(*swc));
587
		break;
588
	case SWITCH_CONN_TLS:
589
	case SWITCH_CONN_TCP:
590
		proc_compose_imsg(ps, PROC_OFCCONN, -1, IMSG_CTL_CONNECT,
591
		    -1, fd, swc, sizeof(*swc));
592
		break;
593
	default:
594
		fatalx("not implemented");
595
	}
596
}
597
598
void
599
parent_disconnect(struct privsep *ps, struct switch_client *swc)
600
{
601
	struct switchd		*sc = ps->ps_env;
602
	enum privsep_procid	 target;
603
604
	TAILQ_REMOVE(&sc->sc_clients, swc, swc_next);
605
606
	target = swc->swc_target.swa_type == SWITCH_CONN_LOCAL ?
607
	    PROC_OFP : PROC_OFCCONN;
608
	proc_compose(ps, target, IMSG_CTL_DISCONNECT, swc, sizeof(*swc));
609
610
	free(swc);
611
}