GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/switchd/ofrelay.c Lines: 0 190 0.0 %
Date: 2017-11-07 Branches: 0 96 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: ofrelay.c,v 1.10 2016/12/22 15:31:43 rzalamena Exp $	*/
2
3
/*
4
 * Copyright (c) 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 <netinet/in.h>
26
#include <netinet/tcp.h>
27
28
#include <stdio.h>
29
#include <stdlib.h>
30
#include <unistd.h>
31
#include <string.h>
32
#include <fcntl.h>
33
#include <pwd.h>
34
#include <errno.h>
35
#include <event.h>
36
37
#include "switchd.h"
38
39
void	 ofrelay_close(struct switch_connection *);
40
void	 ofrelay_event(int, short, void *);
41
int	 ofrelay_input(int, short, void *);
42
int	 ofrelay_output(int, short, void *);
43
void	 ofrelay_accept(int, short, void *);
44
void	 ofrelay_inflight_dec(struct switch_connection *, const char *);
45
46
void	*ofrelay_input_open(struct switch_connection *,
47
	    struct ibuf *, ssize_t *);
48
ssize_t	 ofrelay_input_close(struct switch_connection *,
49
	    struct ibuf *, ssize_t);
50
int	 ofrelay_input_done(struct switch_connection *, struct ibuf *);
51
int	 ofrelay_bufget(struct switch_connection *, struct ibuf *);
52
void	 ofrelay_bufput(struct switch_connection *, struct ibuf *);
53
54
volatile int	 ofrelay_sessions;
55
volatile int	 ofrelay_inflight;
56
static uint32_t	 ofrelay_conid;
57
58
void
59
ofrelay(struct privsep *ps, struct privsep_proc *p)
60
{
61
	struct switchd		*sc = ps->ps_env;
62
	struct switch_server	*srv = &sc->sc_server;
63
64
	log_info("listen on %s", print_host(&srv->srv_addr, NULL, 0));
65
66
	if ((srv->srv_fd = switchd_listen((struct sockaddr *)
67
	    &srv->srv_addr)) == -1)
68
		fatal("listen");
69
}
70
71
void
72
ofrelay_run(struct privsep *ps, struct privsep_proc *p, void *arg)
73
{
74
	struct switchd		*sc = ps->ps_env;
75
	struct switch_server	*srv = &sc->sc_server;
76
77
	TAILQ_INIT(&sc->sc_conns);
78
	TAILQ_INIT(&sc->sc_clients);
79
80
	srv->srv_sc = sc;
81
	event_set(&srv->srv_ev, srv->srv_fd, EV_READ, ofrelay_accept, srv);
82
	event_add(&srv->srv_ev, NULL);
83
	evtimer_set(&srv->srv_evt, ofrelay_accept, srv);
84
}
85
86
void
87
ofrelay_close(struct switch_connection *con)
88
{
89
	struct switchd		*sc = con->con_sc;
90
	struct switch_server	*srv = con->con_srv;
91
92
	log_info("%s: connection %u.%u closed", __func__,
93
	    con->con_id, con->con_instance);
94
95
	if (event_initialized(&con->con_ev))
96
		event_del(&con->con_ev);
97
98
	TAILQ_REMOVE(&sc->sc_conns, con, con_entry);
99
	ofrelay_sessions--;
100
101
	switch_freetables(con);
102
	ofp_multipart_clear(con);
103
	switch_remove(con->con_sc, con->con_switch);
104
	msgbuf_clear(&con->con_wbuf);
105
	ibuf_release(con->con_rbuf);
106
	close(con->con_fd);
107
108
	ofrelay_inflight_dec(con, __func__);
109
110
	/* Some file descriptors are available again. */
111
	if (evtimer_pending(&srv->srv_evt, NULL)) {
112
		DPRINTF("%s: accepting again", __func__);
113
		evtimer_del(&srv->srv_evt);
114
		event_add(&srv->srv_ev, NULL);
115
	}
116
117
	free(con);
118
}
119
120
void
121
ofrelay_event(int fd, short event, void *arg)
122
{
123
	struct switch_connection	*con = arg;
124
	const char			*error = NULL;
125
126
	event_add(&con->con_ev, NULL);
127
	if (event & EV_TIMEOUT) {
128
		error = "timeout";
129
		goto fail;
130
	}
131
	if (event & EV_WRITE) {
132
		if (ofrelay_output(fd, event, arg) == -1) {
133
			error = "write";
134
			goto fail;
135
		}
136
	}
137
	if (event & EV_READ) {
138
		if (ofrelay_input(fd, event, arg) == -1) {
139
			error = "input";
140
			goto fail;
141
		}
142
	}
143
144
 fail:
145
	if (error != NULL) {
146
		DPRINTF("%s: %s error", __func__, error);
147
		ofrelay_close(con);
148
	}
149
}
150
151
int
152
ofrelay_input(int fd, short event, void *arg)
153
{
154
	struct switch_connection	*con = arg;
155
	struct ibuf		*ibuf = con->con_rbuf;
156
	ssize_t			 len, rlen;
157
	size_t			 hlen;
158
	void			*buf;
159
	struct ofp_header	*oh = NULL;
160
161
	if ((buf = ofrelay_input_open(con, ibuf, &len)) == NULL) {
162
		log_warn("%s: fd %d, failed to get buffer", __func__, fd);
163
		return (-1);
164
	}
165
166
	/* If we got a new buffer, read the complete openflow header first */
167
	if ((hlen = ibuf_length(ibuf)) < sizeof(struct ofp_header)) {
168
		oh = (struct ofp_header *)ibuf_data(ibuf);
169
		len = sizeof(*oh) - hlen;
170
	}
171
172
	if ((rlen = read(fd, buf, len)) == -1) {
173
		if (errno == EINTR || errno == EAGAIN)
174
			return (0);
175
		log_warn("%s: fd %d, failed to read", __func__, fd);
176
		return (-1);
177
	}
178
179
	print_hex(buf, 0, rlen);
180
181
	DPRINTF("%s: connection %u.%u read %zd bytes", __func__,
182
	    con->con_id, con->con_instance, rlen);
183
184
	if ((len = ofrelay_input_close(con, ibuf, rlen)) == -1)
185
		return (-1);
186
	else if (rlen == 0) {
187
		/* connection closed */
188
		ofrelay_input_done(con, ibuf);
189
		return (-1);
190
	}
191
192
	/* After we verified the openflow header, set the size accordingly */
193
	if (oh != NULL && (hlen + rlen) == sizeof(*oh)) {
194
		switch (oh->oh_version) {
195
		case OFP_V_1_0:
196
		case OFP_V_1_1:
197
		case OFP_V_1_2:
198
		case OFP_V_1_3:
199
			break;
200
		default:
201
			DPRINTF("%s: fd %d, openflow version 0x%02x length %d"
202
			    " not supported", __func__, fd, oh->oh_version,
203
			    ntohs(oh->oh_length));
204
			return (-1);
205
		}
206
207
		len = ntohs(oh->oh_length);
208
		if (len == sizeof(*oh)) {
209
			len = 0;
210
		} else if (len > (ssize_t)ibuf->size) {
211
			log_debug("%s: buffer too big: %zu > %zd", __func__,
212
			    len, ibuf->size);
213
			return (-1);
214
		} else
215
			ibuf_setmax(ibuf, len);
216
	}
217
218
	if (len > 0)
219
		return (len);
220
221
	return (ofrelay_input_done(con, ibuf));
222
}
223
224
void
225
ofrelay_write(struct switch_connection *con, struct ibuf *buf)
226
{
227
	ibuf_close(&con->con_wbuf, buf);
228
229
	event_del(&con->con_ev);
230
	event_set(&con->con_ev, con->con_fd, EV_READ|EV_WRITE,
231
	    ofrelay_event, con);
232
	event_add(&con->con_ev, NULL);
233
}
234
235
void *
236
ofrelay_input_open(struct switch_connection *con,
237
    struct ibuf *buf, ssize_t *len)
238
{
239
	ssize_t	 left;
240
241
	left = buf->max - buf->wpos;
242
	if (left == 0) {
243
		ofrelay_bufget(con, buf);
244
		left = buf->max;
245
	}
246
	if (len)
247
		*len = left;
248
249
	return (buf->buf + buf->wpos);
250
}
251
252
ssize_t
253
ofrelay_input_close(struct switch_connection *con,
254
    struct ibuf *buf, ssize_t len)
255
{
256
	if (len <= 0)
257
		return (0);
258
	if (buf->wpos + len > buf->max)
259
		return (-1);
260
	buf->wpos += len;
261
262
	return (buf->max - buf->wpos);
263
}
264
265
int
266
ofrelay_input_done(struct switch_connection *con, struct ibuf *buf)
267
{
268
	struct switch_control	*sw;
269
270
	if (buf->wpos == 0) {
271
		ofrelay_bufput(con, buf);
272
		return (0);
273
	}
274
275
	sw = con->con_switch;
276
	log_debug("%s: connection %u.%u: %ld bytes from switch %u", __func__,
277
	    con->con_id, con->con_instance, buf->wpos,
278
	    sw == NULL ? 0 : sw->sw_id);
279
280
	print_hex(buf->buf, 0, buf->wpos);
281
282
	if (ofp_input(con, buf) == -1)
283
		return (-1);
284
285
	/* Update read buffer */
286
	if (ofrelay_bufget(con, buf) == -1)
287
		return (-1);
288
289
	return (0);
290
}
291
292
int
293
ofrelay_bufget(struct switch_connection *con, struct ibuf *buf)
294
{
295
	ibuf_reset(buf);
296
297
	/* Should be a static buffer with maximum size */
298
	if (ibuf_setmax(buf, SWITCHD_MSGBUF_MAX) == -1)
299
		fatalx("%s: invalid buffer", __func__);
300
301
	return (0);
302
}
303
304
void
305
ofrelay_bufput(struct switch_connection *con, struct ibuf *buf)
306
{
307
	/* Just reset the buffer */
308
	ofrelay_bufget(con, buf);
309
}
310
311
int
312
ofrelay_output(int fd, short event, void *arg)
313
{
314
	struct switch_connection	*con = arg;
315
	struct msgbuf			*wbuf = &con->con_wbuf;
316
317
	if (!wbuf->queued) {
318
		event_del(&con->con_ev);
319
		event_set(&con->con_ev, con->con_fd, EV_READ,
320
		    ofrelay_event, con);
321
		event_add(&con->con_ev, NULL);
322
		return (0);
323
	}
324
325
	if (ibuf_write(wbuf) <= 0 && errno != EAGAIN)
326
		return (-1);
327
328
	return (0);
329
}
330
331
void
332
ofrelay_accept(int fd, short event, void *arg)
333
{
334
	struct switch_server	*srv = arg;
335
	struct sockaddr_storage	 ss;
336
	int			 s;
337
	socklen_t		 slen;
338
339
	event_add(&srv->srv_ev, NULL);
340
	if (event & EV_TIMEOUT)
341
		return;
342
343
	slen = sizeof(ss);
344
	if ((s = accept4_reserve(fd, (struct sockaddr *)&ss, &slen,
345
	    SOCK_NONBLOCK|SOCK_CLOEXEC, SWITCHD_FD_RESERVE,
346
	    &ofrelay_inflight)) == -1) {
347
		/*
348
		 * Pause accept if we are out of file descriptors, or
349
		 * libevent will haunt us here too.
350
		 */
351
		if (errno == ENFILE || errno == EMFILE) {
352
			struct timeval evtpause = { 1, 0 };
353
354
			event_del(&srv->srv_ev);
355
			evtimer_add(&srv->srv_evt, &evtpause);
356
			log_warn("%s: deferring connections", __func__);
357
		} else
358
			log_warn("%s accept", __func__);
359
		return;
360
	}
361
	ss.ss_len = slen;
362
363
	(void)ofrelay_attach(srv, s, (struct sockaddr *)&ss);
364
}
365
366
void
367
ofrelay_inflight_dec(struct switch_connection *con, const char *why)
368
{
369
	if (con != NULL) {
370
		/* the flight already left inflight mode. */
371
		if (con->con_inflight == 0)
372
			return;
373
		con->con_inflight = 0;
374
	}
375
376
	/* the file was never opened, thus this was an inflight client. */
377
	ofrelay_inflight--;
378
	DPRINTF("%s: inflight decremented, now %d, %s",
379
	    __func__, ofrelay_inflight, why);
380
}
381
382
int
383
ofrelay_attach(struct switch_server *srv, int s, struct sockaddr *sa)
384
{
385
	struct switchd			*sc = srv->srv_sc;
386
	struct privsep			*ps = &sc->sc_ps;
387
	struct switch_connection	*con = NULL;
388
	socklen_t			 slen;
389
	int				 ret = -1;
390
391
	if (ofrelay_sessions >= SWITCHD_MAX_SESSIONS) {
392
		log_warn("too many sessions");
393
		goto done;
394
	}
395
396
	if ((con = calloc(1, sizeof(*con))) == NULL) {
397
		log_warn("calloc");
398
		goto done;
399
	}
400
401
	con->con_fd = s;
402
	con->con_inflight = 1;
403
	con->con_sc = sc;
404
	con->con_id = ++ofrelay_conid;
405
	con->con_instance = ps->ps_instance + 1;
406
	con->con_srv = srv;
407
	con->con_state = OFP_STATE_CLOSED;
408
	SLIST_INIT(&con->con_mmlist);
409
	TAILQ_INIT(&con->con_stlist);
410
411
	memcpy(&con->con_peer, sa, sa->sa_len);
412
	con->con_port = htons(socket_getport(&con->con_peer));
413
414
	if (getsockname(s, (struct sockaddr *)&con->con_local, &slen) == -1) {
415
		/* Set local sockaddr to AF_UNSPEC */
416
		memset(&con->con_local, 0, sizeof(con->con_local));
417
	}
418
419
	log_info("%s: new connection %u.%u",
420
	    __func__, con->con_id, con->con_instance);
421
422
	ofrelay_sessions++;
423
	TAILQ_INSERT_TAIL(&sc->sc_conns, con, con_entry);
424
425
	if ((con->con_rbuf = ibuf_static()) == NULL ||
426
	    ofrelay_bufget(con, con->con_rbuf) == -1) {
427
		log_warn("ibuf");
428
		goto done;
429
	}
430
	msgbuf_init(&con->con_wbuf);
431
	con->con_wbuf.fd = s;
432
433
	memset(&con->con_ev, 0, sizeof(con->con_ev));
434
	event_set(&con->con_ev, con->con_fd, EV_READ, ofrelay_event, con);
435
	event_add(&con->con_ev, NULL);
436
437
	ret = ofp_open(ps, con);
438
439
 done:
440
	if (con == NULL)
441
		close(s);
442
	ofrelay_inflight_dec(con, __func__);
443
	if (ret != 0)
444
		ofrelay_close(con);
445
446
	return (ret);
447
}