GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/switchd/ofcconn.c Lines: 0 274 0.0 %
Date: 2017-11-13 Branches: 0 159 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: ofcconn.c,v 1.12 2016/10/12 19:07:42 reyk Exp $	*/
2
3
/*
4
 * Copyright (c) 2016 YASUOKA Masahiko <yasuoka@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/queue.h>
21
#include <sys/uio.h>
22
#include <sys/socket.h>
23
#include <sys/un.h>
24
25
#include <net/ofp.h>
26
27
#include <errno.h>
28
#include <event.h>
29
#include <imsg.h>
30
#include <stdio.h>
31
#include <stdlib.h>
32
#include <string.h>
33
#include <unistd.h>
34
#include <imsg.h>
35
36
#include "ofp10.h"
37
#include "types.h"
38
#include "switchd.h"
39
40
int	 ofcconn_dispatch_parent(int, struct privsep_proc *, struct imsg *);
41
42
static struct privsep_proc procs[] = {
43
	{ "parent",	PROC_PARENT,	ofcconn_dispatch_parent },
44
	{ "control",	PROC_CONTROL,	NULL },
45
};
46
47
struct ofcconn;
48
49
/* OpenFlow Switch */
50
struct ofsw {
51
	int			 os_fd;
52
	char			*os_name;
53
	int			 os_write_ready;
54
	TAILQ_HEAD(,ofcconn)	 os_ofcconns;
55
	struct event		 os_evio;
56
	TAILQ_ENTRY(ofsw)	 os_next;
57
};
58
TAILQ_HEAD(, ofsw)	 ofsw_list = TAILQ_HEAD_INITIALIZER(ofsw_list);
59
60
/* OpenFlow Channel Connection */
61
struct ofcconn {
62
	struct ofsw		*oc_sw;
63
	char			*oc_name;
64
	struct sockaddr_storage	 oc_peer;
65
	int			 oc_sock;
66
	int			 oc_write_ready;
67
	int			 oc_connected;
68
	int			 oc_conn_fails;
69
	struct ibuf		*oc_buf;
70
	TAILQ_ENTRY(ofcconn)	 oc_next;
71
	struct event		 oc_evsock;
72
	struct event		 oc_evtimer;
73
};
74
75
struct ofsw	*ofsw_create(const char *, int);
76
void		 ofsw_close(struct ofsw *);
77
void		 ofsw_free(struct ofsw *);
78
void		 ofsw_on_io(int, short, void *);
79
int		 ofsw_write(struct ofsw *, struct ofcconn *);
80
int		 ofsw_ofc_write_ready(struct ofsw *);
81
void		 ofsw_reset_event_handlers(struct ofsw *);
82
int		 ofsw_new_ofcconn(struct ofsw *, struct switch_address *);
83
int		 ofcconn_connect(struct ofcconn *);
84
void		 ofcconn_on_sockio(int, short, void *);
85
void		 ofcconn_connect_again(struct ofcconn *);
86
void		 ofcconn_on_timer(int, short, void *);
87
void		 ofcconn_reset_event_handlers(struct ofcconn *);
88
void		 ofcconn_io_fail(struct ofcconn *);
89
void		 ofcconn_close(struct ofcconn *);
90
void		 ofcconn_free(struct ofcconn *);
91
void		 ofcconn_shutdown_all(void);
92
int		 ofcconn_send_hello(struct ofcconn *);
93
void		 ofcconn_run(struct privsep *, struct privsep_proc *, void *);
94
95
void
96
ofcconn(struct privsep *ps, struct privsep_proc *p)
97
{
98
	p->p_shutdown = ofcconn_shutdown;
99
	proc_run(ps, p, procs, nitems(procs), ofcconn_run, NULL);
100
}
101
102
void
103
ofcconn_run(struct privsep *ps, struct privsep_proc *p, void *arg)
104
{
105
	/*
106
	 * pledge in the ofcconn process:
107
 	 * stdio - for malloc and basic I/O including events.
108
	 * inet - for socket operations and OpenFlow connections.
109
	 * recvfd - for receiving new sockets on reload.
110
	 */
111
	if (pledge("stdio inet recvfd flock rpath cpath wpath", NULL) == -1)
112
		fatal("pledge");
113
}
114
115
void
116
ofcconn_shutdown(void)
117
{
118
	struct ofsw	*e, *t;
119
120
	TAILQ_FOREACH_SAFE(e, &ofsw_list, os_next, t) {
121
		ofsw_close(e);
122
		ofsw_free(e);
123
	}
124
}
125
126
int
127
ofcconn_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg)
128
{
129
	struct ofsw			*os;
130
	struct switch_client		 swc;
131
	struct sockaddr_un		*un;
132
133
	switch (imsg->hdr.type) {
134
	case IMSG_CTL_CONNECT:
135
		if (IMSG_DATA_SIZE(imsg) < sizeof(swc)) {
136
			log_warnx("%s: IMSG_CTL_CONNECT: "
137
			    "invalid message size", __func__);
138
			return (0);
139
		}
140
		memcpy(&swc, imsg->data, sizeof(swc));
141
		un = (struct sockaddr_un *)&swc.swc_addr.swa_addr;
142
143
		if ((os = ofsw_create(un->sun_path, imsg->fd)) != NULL)
144
			ofsw_new_ofcconn(os, &swc.swc_target);
145
		return (0);
146
	case IMSG_CTL_DISCONNECT:
147
		if (IMSG_DATA_SIZE(imsg) < sizeof(swc)) {
148
			log_warnx("%s: IMSG_CTL_DEVICE_DISCONNECT: "
149
			    "invalid message size", __func__);
150
			return (0);
151
		}
152
		memcpy(&swc, imsg->data, sizeof(swc));
153
		un = (struct sockaddr_un *)&swc.swc_addr.swa_addr;
154
155
		TAILQ_FOREACH(os, &ofsw_list, os_next) {
156
			if (!strcmp(os->os_name, un->sun_path))
157
				break;
158
		}
159
		if (os) {
160
			log_warnx("%s: closed by request", os->os_name);
161
			ofsw_close(os);
162
			ofsw_free(os);
163
		}
164
		return (0);
165
	default:
166
		break;
167
	}
168
169
	return (-1);
170
}
171
172
struct ofsw *
173
ofsw_create(const char *name, int fd)
174
{
175
	struct ofsw	*os = NULL;
176
177
	if ((os = calloc(1, sizeof(struct ofsw))) == NULL) {
178
		log_warn("%s: calloc failed", __func__);
179
		goto fail;
180
	}
181
	if ((os->os_name = strdup(name)) == NULL) {
182
		log_warn("%s: strdup failed", __func__);
183
		goto fail;
184
	}
185
	os->os_fd = fd;
186
	TAILQ_INIT(&os->os_ofcconns);
187
	TAILQ_INSERT_TAIL(&ofsw_list, os, os_next);
188
189
	event_set(&os->os_evio, os->os_fd, EV_READ|EV_WRITE, ofsw_on_io, os);
190
	event_add(&os->os_evio, NULL);
191
192
	return (os);
193
194
 fail:
195
	if (os != NULL)
196
		free(os->os_name);
197
	free(os);
198
199
	return (NULL);
200
}
201
202
void
203
ofsw_close(struct ofsw *os)
204
{
205
	struct ofcconn	*oc, *oct;
206
207
	if (os->os_fd >= 0) {
208
		close(os->os_fd);
209
		event_del(&os->os_evio);
210
		os->os_fd = -1;
211
	}
212
	TAILQ_FOREACH_SAFE(oc, &os->os_ofcconns, oc_next, oct) {
213
		ofcconn_close(oc);
214
		ofcconn_free(oc);
215
	}
216
}
217
218
void
219
ofsw_free(struct ofsw *os)
220
{
221
	if (os == NULL)
222
		return;
223
224
	TAILQ_REMOVE(&ofsw_list, os, os_next);
225
	free(os->os_name);
226
	free(os);
227
}
228
229
void
230
ofsw_on_io(int fd, short evmask, void *ctx)
231
{
232
	struct ofsw		*os = ctx;
233
	struct ofcconn		*oc, *oct;
234
	static char		 msg[65536];/* max size of OpenFlow message */
235
	ssize_t			 msgsz, sz;
236
	struct ofp_header	*hdr;
237
238
	if (evmask & EV_WRITE || os->os_write_ready) {
239
		os->os_write_ready = 1;
240
		if (ofsw_write(os, NULL) == -1)
241
			return;
242
	}
243
244
	if ((evmask & EV_READ) && ofsw_ofc_write_ready(os)) {
245
		if ((msgsz = read(os->os_fd, msg, sizeof(msg))) <= 0) {
246
			if (msgsz < 0)
247
				log_warn("%s: %s read", __func__, os->os_name);
248
			else
249
				log_warnx("%s: %s closed", __func__,
250
				    os->os_name);
251
			ofsw_close(os);
252
			ofsw_free(os);
253
			return;
254
		}
255
		hdr = (struct ofp_header *)msg;
256
		if (hdr->oh_type != OFP_T_HELLO) {
257
			TAILQ_FOREACH_SAFE(oc, &os->os_ofcconns, oc_next, oct) {
258
				if ((sz = write(oc->oc_sock, msg, msgsz))
259
				    != msgsz) {
260
					log_warn("%s: sending a message to "
261
					    "%s failed", os->os_name,
262
					    oc->oc_name);
263
					ofcconn_io_fail(oc);
264
					continue;
265
				}
266
				oc->oc_write_ready = 0;
267
				ofcconn_reset_event_handlers(oc);
268
			}
269
		}
270
	}
271
	ofsw_reset_event_handlers(os);
272
273
	return;
274
}
275
276
int
277
ofsw_write(struct ofsw *os, struct ofcconn *oc0)
278
{
279
	struct ofcconn		*oc = oc0;
280
	struct ofp_header	*hdr;
281
	u_char			*msg;
282
	ssize_t			 sz, msglen;
283
	int			 remain = 0;
284
	unsigned char		 buf[65536];
285
286
	if (!os->os_write_ready)
287
		return (0);
288
289
 again:
290
	if (oc != NULL) {
291
		hdr = ibuf_seek(oc->oc_buf, 0, sizeof(*hdr));
292
		if (hdr == NULL)
293
			return (0);
294
		msglen = ntohs(hdr->oh_length);
295
		msg = ibuf_seek(oc->oc_buf, 0, msglen);
296
		if (msg == NULL)
297
			return (0);
298
	} else {
299
		TAILQ_FOREACH(oc, &os->os_ofcconns, oc_next) {
300
			hdr = ibuf_seek(oc->oc_buf, 0, sizeof(*hdr));
301
			if (hdr == NULL)
302
				continue;
303
			msglen = ntohs(hdr->oh_length);
304
			msg = ibuf_seek(oc->oc_buf, 0, msglen);
305
			if (msg != NULL)
306
				break;
307
		}
308
		if (oc == NULL)
309
			return (0);	/* no message to write yet */
310
	}
311
	if (hdr->oh_type != OFP_T_HELLO) {
312
		if ((sz = write(os->os_fd, msg, msglen)) != msglen) {
313
			if (sz < 0)
314
				log_warn("%s: %s write failed", __func__,
315
				    os->os_name);
316
			else
317
				log_warn("%s: %s write partially", __func__,
318
				    os->os_name);
319
			ofsw_close(os);
320
			ofsw_free(os);
321
			return (-1);
322
		}
323
		os->os_write_ready = 0;
324
	}
325
326
	/* XXX preserve the remaining part */
327
	if ((remain = oc->oc_buf->wpos - msglen) > 0)
328
		memcpy(buf, (caddr_t)msg + msglen, remain);
329
	ibuf_reset(oc->oc_buf);
330
331
	/* XXX put the remaining part again */
332
	if (remain > 0)
333
		ibuf_add(oc->oc_buf, buf, remain);
334
335
	if (os->os_write_ready) {
336
		oc = NULL;
337
		goto again;
338
	}
339
340
	return (0);
341
}
342
343
int
344
ofsw_ofc_write_ready(struct ofsw *os)
345
{
346
	struct ofcconn	*oc;
347
	int		 write_ready = 0;
348
349
	TAILQ_FOREACH(oc, &os->os_ofcconns, oc_next) {
350
		if (oc->oc_write_ready)
351
			write_ready = 1;
352
		else
353
			break;
354
	}
355
	if (oc != NULL)
356
		return (0);
357
358
	return (write_ready);
359
}
360
361
void
362
ofsw_reset_event_handlers(struct ofsw *os)
363
{
364
	short	evmask = 0, oevmask;
365
366
	oevmask = event_pending(&os->os_evio, EV_READ|EV_WRITE, NULL);
367
368
	if (ofsw_ofc_write_ready(os))
369
		evmask |= EV_READ;
370
	if (!os->os_write_ready)
371
		evmask |= EV_WRITE;
372
373
	if (oevmask != evmask) {
374
		if (oevmask)
375
			event_del(&os->os_evio);
376
		event_set(&os->os_evio, os->os_fd, evmask, ofsw_on_io, os);
377
		event_add(&os->os_evio, NULL);
378
	}
379
}
380
381
int
382
ofsw_new_ofcconn(struct ofsw *os, struct switch_address *swa)
383
{
384
	struct ofcconn	*oc = NULL;
385
	char		 buf[128];
386
387
	if ((oc = calloc(1, sizeof(struct ofcconn))) == NULL) {
388
		log_warn("%s: calloc failed", __func__);
389
		goto fail;
390
	}
391
392
	if (asprintf(&oc->oc_name, "tcp:%s",
393
	    print_host(&swa->swa_addr, buf, sizeof(buf))) == -1) {
394
		log_warn("%s: strdup failed", __func__);
395
		goto fail;
396
	}
397
	if ((oc->oc_buf = ibuf_new(NULL, 0)) == NULL) {
398
		log_warn("%s: failed to get new ibuf", __func__);
399
		goto fail;
400
	}
401
	oc->oc_sw = os;
402
	oc->oc_sock = -1;
403
	memcpy(&oc->oc_peer, &swa->swa_addr, sizeof(oc->oc_peer));
404
405
	if (ntohs(((struct sockaddr_in *)&oc->oc_peer)->sin_port) == 0)
406
		((struct sockaddr_in *)&oc->oc_peer)->sin_port =
407
		    htons(SWITCHD_CTLR_PORT);
408
409
	evtimer_set(&oc->oc_evtimer, ofcconn_on_timer, oc);
410
	TAILQ_INSERT_TAIL(&os->os_ofcconns, oc, oc_next);
411
412
	return (ofcconn_connect(oc));
413
414
 fail:
415
	if (oc != NULL) {
416
		free(oc->oc_name);
417
		ibuf_release(oc->oc_buf);
418
	}
419
	free(oc);
420
421
	return (-1);
422
}
423
424
int
425
ofcconn_connect(struct ofcconn *oc)
426
{
427
	int		 sock = -1;
428
	struct timeval	 tv;
429
430
	if ((sock = socket(oc->oc_peer.ss_family, SOCK_STREAM | SOCK_NONBLOCK,
431
	    IPPROTO_TCP)) == -1) {
432
		log_warn("%s: failed to open socket for channel with %s",
433
		    oc->oc_sw->os_name, oc->oc_name);
434
		goto fail;
435
	}
436
437
	if (connect(sock, (struct sockaddr *)&oc->oc_peer,
438
	    oc->oc_peer.ss_len) == -1) {
439
		if (errno != EINPROGRESS) {
440
			log_warn("%s: failed to connect channel to %s",
441
			    oc->oc_sw->os_name, oc->oc_name);
442
			goto fail;
443
		}
444
	}
445
446
	oc->oc_sock = sock;
447
	event_set(&oc->oc_evsock, oc->oc_sock, EV_READ|EV_WRITE,
448
	    ofcconn_on_sockio, oc);
449
	event_add(&oc->oc_evsock, NULL);
450
451
	tv.tv_sec = SWITCHD_CONNECT_TIMEOUT;
452
	tv.tv_usec = 0;
453
	event_add(&oc->oc_evtimer, &tv);
454
455
	return (0);
456
457
 fail:
458
	if (sock >= 0)
459
		close(sock);
460
461
	oc->oc_conn_fails++;
462
	ofcconn_connect_again(oc);
463
464
	return (-1);
465
}
466
467
void
468
ofcconn_on_sockio(int fd, short evmask, void *ctx)
469
{
470
	struct ofcconn	*oc = ctx;
471
	ssize_t		 sz;
472
	size_t		 wpos;
473
	int		 err;
474
	socklen_t	 optlen;
475
476
	if (evmask & EV_WRITE) {
477
		if (oc->oc_connected == 0) {
478
			optlen = sizeof(err);
479
			getsockopt(oc->oc_sock, SOL_SOCKET, SO_ERROR, &err,
480
			    &optlen);
481
			if (err != 0) {
482
				log_warnx("%s: connection error with %s: %s",
483
				    oc->oc_sw->os_name, oc->oc_name,
484
				    strerror(err));
485
				oc->oc_conn_fails++;
486
				ofcconn_close(oc);
487
				ofcconn_connect_again(oc);
488
				return;
489
			}
490
			log_info("%s: OpenFlow channel to %s connected",
491
			    oc->oc_sw->os_name, oc->oc_name);
492
493
			event_del(&oc->oc_evtimer);
494
			oc->oc_connected = 1;
495
			oc->oc_conn_fails = 0;
496
			if (ofcconn_send_hello(oc) != 0)
497
				return;
498
		} else
499
			oc->oc_write_ready = 1;
500
	}
501
502
	if ((evmask & EV_READ) && ibuf_left(oc->oc_buf) > 0) {
503
		wpos = ibuf_length(oc->oc_buf);
504
505
		/* XXX temporally fix not to access unallocated area */
506
		if (wpos + ibuf_left(oc->oc_buf) > oc->oc_buf->size) {
507
			ibuf_reserve(oc->oc_buf, ibuf_left(oc->oc_buf));
508
			ibuf_setsize(oc->oc_buf, wpos);
509
		}
510
511
		if ((sz = read(oc->oc_sock, ibuf_data(oc->oc_buf) + wpos,
512
		    ibuf_left(oc->oc_buf))) <= 0) {
513
			if (sz == 0)
514
				log_warnx("%s: %s: connection closed by peer",
515
				    oc->oc_sw->os_name, oc->oc_name);
516
			else
517
				log_warn("%s: %s: connection read error",
518
				    oc->oc_sw->os_name, oc->oc_name);
519
			goto fail;
520
		}
521
		if (ibuf_setsize(oc->oc_buf, wpos + sz) == -1)
522
			goto fail;
523
524
		if (ofsw_write(oc->oc_sw, oc) == -1)
525
			return;	/* oc is already freeed */
526
	}
527
	ofcconn_reset_event_handlers(oc);
528
	ofsw_reset_event_handlers(oc->oc_sw);
529
530
	return;
531
532
 fail:
533
	ofcconn_close(oc);
534
	ofcconn_connect_again(oc);
535
}
536
537
void
538
ofcconn_connect_again(struct ofcconn *oc)
539
{
540
	struct timeval	 tv;
541
	const int	 ofcconn_backoffs[] = { 1, 2, 4, 8, 16 };
542
543
	tv.tv_sec = (oc->oc_conn_fails < (int)nitems(ofcconn_backoffs))
544
	    ? ofcconn_backoffs[oc->oc_conn_fails]
545
	    : ofcconn_backoffs[nitems(ofcconn_backoffs) - 1];
546
	tv.tv_usec = 0;
547
	event_add(&oc->oc_evtimer, &tv);
548
}
549
550
void
551
ofcconn_on_timer(int fd, short evmask, void *ctx)
552
{
553
	struct ofcconn	*oc = ctx;
554
555
	if (oc->oc_sock < 0)
556
		ofcconn_connect(oc);
557
	else if (!oc->oc_connected) {
558
		log_warnx("%s: timeout connecting channel to %s",
559
		    oc->oc_sw->os_name, oc->oc_name);
560
		ofcconn_close(oc);
561
		oc->oc_conn_fails++;
562
		ofcconn_connect_again(oc);
563
	}
564
}
565
566
void
567
ofcconn_reset_event_handlers(struct ofcconn *oc)
568
{
569
	short	evmask = 0, oevmask;
570
571
	oevmask = event_pending(&oc->oc_evsock, EV_READ|EV_WRITE, NULL);
572
573
	if (ibuf_left(oc->oc_buf) > 0)
574
		evmask |= EV_READ;
575
	if (!oc->oc_write_ready)
576
		evmask |= EV_WRITE;
577
578
	if (oevmask != evmask) {
579
		if (oevmask)
580
			event_del(&oc->oc_evsock);
581
		if (evmask) {
582
			event_set(&oc->oc_evsock, oc->oc_sock, evmask,
583
			    ofcconn_on_sockio, oc);
584
			event_add(&oc->oc_evsock, NULL);
585
		}
586
	}
587
}
588
589
void
590
ofcconn_io_fail(struct ofcconn *oc)
591
{
592
	ofcconn_close(oc);
593
	ofcconn_connect_again(oc);
594
}
595
596
void
597
ofcconn_close(struct ofcconn *oc)
598
{
599
	if (oc->oc_sock >= 0) {
600
		event_del(&oc->oc_evsock);
601
		close(oc->oc_sock);
602
		oc->oc_sock = -1;
603
		oc->oc_write_ready = 0;
604
	}
605
	event_del(&oc->oc_evtimer);
606
	oc->oc_connected = 0;
607
}
608
609
void
610
ofcconn_free(struct ofcconn *oc)
611
{
612
	if (oc == NULL)
613
		return;
614
	TAILQ_REMOVE(&oc->oc_sw->os_ofcconns, oc, oc_next);
615
	ibuf_release(oc->oc_buf);
616
	free(oc->oc_name);
617
	free(oc);
618
}
619
620
int
621
ofcconn_send_hello(struct ofcconn *oc)
622
{
623
	struct ofp_header	 hdr;
624
	ssize_t			 sz;
625
626
	hdr.oh_version = OFP_V_1_3;
627
	hdr.oh_type = OFP_T_HELLO;
628
	hdr.oh_length = htons(sizeof(hdr));
629
	hdr.oh_xid = htonl(0xffffffffUL);
630
631
	sz = sizeof(hdr);
632
	if (write(oc->oc_sock, &hdr, sz) != sz) {
633
		log_warn("%s: %s: %s; write", __func__, oc->oc_sw->os_name,
634
		    oc->oc_name);
635
		ofcconn_close(oc);
636
		ofcconn_connect_again(oc);
637
		return (-1);
638
	}
639
640
	return (0);
641
}