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

Line Branch Exec Source
1
/*	$OpenBSD: ofp10.c,v 1.19 2016/12/02 14:39:46 rzalamena 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/queue.h>
21
#include <sys/socket.h>
22
23
#include <net/if.h>
24
#include <net/if_arp.h>
25
#include <net/ofp.h>
26
27
#include <netinet/in.h>
28
#include <netinet/if_ether.h>
29
#include <netinet/tcp.h>
30
31
#include <stdio.h>
32
#include <stdlib.h>
33
#include <unistd.h>
34
#include <string.h>
35
#include <fcntl.h>
36
#include <imsg.h>
37
#include <event.h>
38
39
#include "ofp10.h"
40
#include "switchd.h"
41
#include "ofp_map.h"
42
43
44
int	 ofp10_packet_match(struct packet *, struct ofp10_match *, unsigned int);
45
46
int	 ofp10_features_reply(struct switchd *, struct switch_connection *,
47
	    struct ofp_header *, struct ibuf *);
48
int	 ofp10_validate_features_reply(struct switchd *,
49
	    struct sockaddr_storage *, struct sockaddr_storage *,
50
	    struct ofp_header *, struct ibuf *);
51
int	 ofp10_echo_request(struct switchd *, struct switch_connection *,
52
	    struct ofp_header *, struct ibuf *);
53
int	 ofp10_validate_error(struct switchd *,
54
	    struct sockaddr_storage *, struct sockaddr_storage *,
55
	    struct ofp_header *, struct ibuf *);
56
int	 ofp10_error(struct switchd *, struct switch_connection *,
57
	    struct ofp_header *, struct ibuf *);
58
int	 ofp10_validate_packet_in(struct switchd *,
59
	    struct sockaddr_storage *, struct sockaddr_storage *,
60
	    struct ofp_header *, struct ibuf *);
61
int	 ofp10_packet_in(struct switchd *, struct switch_connection *,
62
	    struct ofp_header *, struct ibuf *);
63
int	 ofp10_validate_packet_out(struct switchd *,
64
	    struct sockaddr_storage *, struct sockaddr_storage *,
65
	    struct ofp_header *, struct ibuf *);
66
67
struct ofp_callback ofp10_callbacks[] = {
68
	{ OFP10_T_HELLO,		ofp10_hello, ofp_validate_hello },
69
	{ OFP10_T_ERROR,		NULL, ofp10_validate_error },
70
	{ OFP10_T_ECHO_REQUEST,		ofp10_echo_request, NULL },
71
	{ OFP10_T_ECHO_REPLY,		NULL, NULL },
72
	{ OFP10_T_EXPERIMENTER,		NULL, NULL },
73
	{ OFP10_T_FEATURES_REQUEST,	NULL, NULL },
74
	{ OFP10_T_FEATURES_REPLY,	ofp10_features_reply,
75
					ofp10_validate_features_reply },
76
	{ OFP10_T_GET_CONFIG_REQUEST,	NULL, NULL },
77
	{ OFP10_T_GET_CONFIG_REPLY,	NULL, NULL },
78
	{ OFP10_T_SET_CONFIG,		NULL, NULL },
79
	{ OFP10_T_PACKET_IN,		ofp10_packet_in, ofp10_validate_packet_in },
80
	{ OFP10_T_FLOW_REMOVED,		NULL, NULL },
81
	{ OFP10_T_PORT_STATUS,		NULL, NULL },
82
	{ OFP10_T_PACKET_OUT,		NULL, ofp10_validate_packet_out },
83
	{ OFP10_T_FLOW_MOD,		NULL, NULL },
84
	{ OFP10_T_PORT_MOD,		NULL, NULL },
85
	{ OFP10_T_STATS_REQUEST,	NULL, NULL },
86
	{ OFP10_T_STATS_REPLY,		NULL, NULL },
87
	{ OFP10_T_BARRIER_REQUEST,	NULL, NULL },
88
	{ OFP10_T_BARRIER_REPLY,	NULL, NULL },
89
	{ OFP10_T_QUEUE_GET_CONFIG_REQUEST, NULL, NULL },
90
	{ OFP10_T_QUEUE_GET_CONFIG_REPLY, NULL, NULL }
91
};
92
93
int
94
ofp10_validate(struct switchd *sc,
95
    struct sockaddr_storage *src, struct sockaddr_storage *dst,
96
    struct ofp_header *oh, struct ibuf *ibuf)
97
{
98
	uint8_t	type;
99
100
	if (ofp_validate_header(sc, src, dst, oh, OFP_V_1_0) != 0) {
101
		log_debug("\tinvalid header");
102
		return (-1);
103
	}
104
	if (ibuf == NULL) {
105
		/* The response packet buffer is optional */
106
		return (0);
107
	}
108
	type = oh->oh_type;
109
	if (ofp10_callbacks[type].validate != NULL &&
110
	    ofp10_callbacks[type].validate(sc, src, dst, oh, ibuf) != 0) {
111
		log_debug("\tinvalid packet");
112
		return (-1);
113
	}
114
	return (0);
115
}
116
117
int
118
ofp10_validate_packet_in(struct switchd *sc,
119
    struct sockaddr_storage *src, struct sockaddr_storage *dst,
120
    struct ofp_header *oh, struct ibuf *ibuf)
121
{
122
	struct ofp10_packet_in	*pin;
123
	uint8_t			*p;
124
	size_t			 len, plen;
125
	off_t			 off;
126
127
	off = 0;
128
	if ((pin = ibuf_seek(ibuf, off, sizeof(*pin))) == NULL)
129
		return (-1);
130
	log_debug("\tbuffer %d port %s "
131
	    "length %u reason %u",
132
	    ntohl(pin->pin_buffer_id),
133
	    print_map(ntohs(pin->pin_port), ofp10_port_map),
134
	    ntohs(pin->pin_total_len),
135
	    pin->pin_reason);
136
	off += sizeof(*pin);
137
138
	len = ntohs(pin->pin_total_len);
139
	plen = ibuf_length(ibuf) - off;
140
141
	if (plen < len) {
142
		log_debug("\ttruncated packet %zu < %zu", plen, len);
143
144
		/* Buffered packets can be truncated */
145
		if (pin->pin_buffer_id != OFP_PKTOUT_NO_BUFFER)
146
			len = plen;
147
		else
148
			return (-1);
149
	}
150
	if ((p = ibuf_seek(ibuf, off, len)) == NULL)
151
		return (-1);
152
	if (sc->sc_tap != -1)
153
		(void)write(sc->sc_tap, p, len);
154
155
	return (0);
156
}
157
158
int
159
ofp10_validate_packet_out(struct switchd *sc,
160
    struct sockaddr_storage *src, struct sockaddr_storage *dst,
161
    struct ofp_header *oh, struct ibuf *ibuf)
162
{
163
	struct ofp10_packet_out		*pout;
164
	size_t				 len;
165
	off_t				 off;
166
	struct ofp_action_header	*ah;
167
	struct ofp10_action_output	*ao;
168
169
	off = 0;
170
	if ((pout = ibuf_seek(ibuf, off, sizeof(*pout))) == NULL) {
171
		log_debug("%s: seek failed: length %zd",
172
		    __func__, ibuf_length(ibuf));
173
		return (-1);
174
	}
175
	log_debug("\tbuffer %d port %s "
176
	    "actions length %u",
177
	    ntohl(pout->pout_buffer_id),
178
	    print_map(ntohs(pout->pout_port), ofp10_port_map),
179
	    ntohs(pout->pout_actions_len));
180
	len = ntohs(pout->pout_actions_len);
181
182
	off += sizeof(*pout);
183
	while ((ah = ibuf_seek(ibuf, off, len)) != NULL &&
184
	    ntohs(ah->ah_len) >= (uint16_t)sizeof(*ah)) {
185
		switch (ntohs(ah->ah_type)) {
186
		case OFP10_ACTION_OUTPUT:
187
			ao = (struct ofp10_action_output *)ah;
188
			log_debug("\t\taction type %s length %d "
189
			    "port %s max length %d",
190
			    print_map(ntohs(ao->ao_type), ofp10_action_map),
191
			    ntohs(ao->ao_len),
192
			    print_map(ntohs(ao->ao_port), ofp10_port_map),
193
			    ntohs(ao->ao_max_len));
194
			break;
195
		default:
196
			log_debug("\t\taction type %s length %d",
197
			    print_map(ntohs(ah->ah_type), ofp10_action_map),
198
			    ntohs(ah->ah_len));
199
			break;
200
		}
201
		if (pout->pout_buffer_id == (uint32_t)-1)
202
			break;
203
		off += ntohs(ah->ah_len);
204
	}
205
206
	return (0);
207
}
208
209
int
210
ofp10_validate_error(struct switchd *sc,
211
    struct sockaddr_storage *src, struct sockaddr_storage *dst,
212
    struct ofp_header *oh, struct ibuf *ibuf)
213
{
214
	struct ofp_error		*err;
215
	off_t				 off;
216
	const char			*code;
217
218
	off = 0;
219
	if ((err = ibuf_seek(ibuf, off, sizeof(*err))) == NULL) {
220
		log_debug("%s: seek failed: length %zd",
221
		    __func__, ibuf_length(ibuf));
222
		return (-1);
223
	}
224
225
	switch (ntohs(err->err_type)) {
226
	case OFP10_ERRTYPE_FLOW_MOD_FAILED:
227
		code = print_map(ntohs(err->err_code), ofp10_errflowmod_map);
228
		break;
229
	default:
230
		code = NULL;
231
		break;
232
	}
233
234
	log_debug("\terror type %s code %u%s%s",
235
	    print_map(ntohs(err->err_type), ofp10_errtype_map),
236
	    ntohs(err->err_code),
237
	    code == NULL ? "" : ": ",
238
	    code == NULL ? "" : code);
239
240
	return (0);
241
}
242
243
int
244
ofp10_input(struct switchd *sc, struct switch_connection *con,
245
    struct ofp_header *oh, struct ibuf *ibuf)
246
{
247
	if (ofp10_validate(sc, &con->con_peer, &con->con_local, oh, ibuf) != 0)
248
		return (-1);
249
250
	if (ofp10_callbacks[oh->oh_type].cb == NULL) {
251
		log_debug("message not supported: %s",
252
		    print_map(oh->oh_type, ofp10_t_map));
253
		return (-1);
254
	}
255
	if (ofp10_callbacks[oh->oh_type].cb(sc, con, oh, ibuf) != 0)
256
		return (-1);
257
258
	return (0);
259
}
260
261
int
262
ofp10_hello(struct switchd *sc, struct switch_connection *con,
263
    struct ofp_header *oh, struct ibuf *ibuf)
264
{
265
	if (switch_add(con) == NULL) {
266
		log_debug("%s: failed to add switch", __func__);
267
		return (-1);
268
	}
269
270
	if (ofp_recv_hello(sc, con, oh, ibuf) == -1)
271
		return (-1);
272
273
	return (ofp_nextstate(sc, con, OFP_STATE_FEATURE_WAIT));
274
}
275
276
int
277
ofp10_features_reply(struct switchd *sc, struct switch_connection *con,
278
    struct ofp_header *oh, struct ibuf *ibuf)
279
{
280
	return (ofp_nextstate(sc, con, OFP_STATE_ESTABLISHED));
281
}
282
283
int
284
ofp10_validate_features_reply(struct switchd *sc,
285
    struct sockaddr_storage *src, struct sockaddr_storage *dst,
286
    struct ofp_header *oh, struct ibuf *ibuf)
287
{
288
	struct ofp_switch_features	*swf;
289
	struct ofp10_phy_port		*swp;
290
	off_t				 poff;
291
	int				 portslen;
292
	char				*mac;
293
294
	if ((swf = ibuf_seek(ibuf, 0, sizeof(*swf))) == NULL)
295
		return (-1);
296
297
	log_debug("\tdatapath_id %#016llx nbuffers %u ntables %d "
298
	    "capabilities %#08x actions %#08x",
299
	    be64toh(swf->swf_datapath_id), ntohl(swf->swf_nbuffers),
300
	    swf->swf_ntables, ntohl(swf->swf_capabilities),
301
	    ntohl(swf->swf_actions));
302
303
	poff = sizeof(*swf);
304
	portslen = ntohs(oh->oh_length) - sizeof(*swf);
305
	if (portslen <= 0)
306
		return (0);
307
308
	while (portslen > 0) {
309
		if ((swp = ibuf_seek(ibuf, poff, sizeof(*swp))) == NULL)
310
			return (-1);
311
312
		mac = ether_ntoa((void *)swp->swp_macaddr);
313
		log_debug("no %s macaddr %s name %s config %#08x state %#08x "
314
		    "cur %#08x advertised %#08x supported %#08x peer %#08x",
315
		    print_map(ntohs(swp->swp_number), ofp10_port_map), mac,
316
		    swp->swp_name, swp->swp_config, swp->swp_state,
317
		    swp->swp_cur, swp->swp_advertised, swp->swp_supported,
318
		    swp->swp_peer);
319
320
		portslen -= sizeof(*swp);
321
		poff += sizeof(*swp);
322
	}
323
324
	return (0);
325
}
326
327
int
328
ofp10_echo_request(struct switchd *sc, struct switch_connection *con,
329
    struct ofp_header *oh, struct ibuf *ibuf)
330
{
331
	/* Echo reply */
332
	oh->oh_type = OFP10_T_ECHO_REPLY;
333
	if (ofp10_validate(sc, &con->con_local, &con->con_peer, oh, NULL) != 0)
334
		return (-1);
335
	ofp_output(con, oh, NULL);
336
337
	return (0);
338
}
339
340
int
341
ofp10_packet_match(struct packet *pkt, struct ofp10_match *m, uint32_t flags)
342
{
343
	struct ether_header	*eh = pkt->pkt_eh;
344
345
	bzero(m, sizeof(*m));
346
	m->m_wildcards = htonl(~flags);
347
348
	if ((flags & (OFP10_WILDCARD_DL_SRC|OFP10_WILDCARD_DL_DST)) &&
349
	    (eh == NULL))
350
		return (-1);
351
352
	if (flags & OFP10_WILDCARD_DL_SRC)
353
		memcpy(m->m_dl_src, eh->ether_shost, ETHER_ADDR_LEN);
354
	if (flags & OFP10_WILDCARD_DL_DST)
355
		memcpy(m->m_dl_dst, eh->ether_dhost, ETHER_ADDR_LEN);
356
357
	return (0);
358
}
359
360
int
361
ofp10_packet_in(struct switchd *sc, struct switch_connection *con,
362
    struct ofp_header *ih, struct ibuf *ibuf)
363
{
364
	struct ofp10_packet_in		*pin;
365
	struct ofp10_packet_out		*pout;
366
	struct ofp10_action_output	*ao;
367
	struct ofp10_flow_mod		*fm;
368
	struct ofp_header		*oh;
369
	struct packet			 pkt;
370
	struct ibuf			*obuf = NULL;
371
	int				 ret = -1;
372
	size_t				 len;
373
	uint32_t			 srcport, dstport;
374
	int				 addflow = 0;
375
	int				 addpacket = 0;
376
377
	if ((pin = ibuf_getdata(ibuf, sizeof(*pin))) == NULL)
378
		return (-1);
379
380
	bzero(&pkt, sizeof(pkt));
381
	len = ntohs(pin->pin_total_len);
382
	srcport = ntohs(pin->pin_port);
383
384
	if (packet_input(sc, con->con_switch,
385
	    srcport, &dstport, ibuf, len, &pkt) == -1 ||
386
	    (dstport > OFP10_PORT_MAX &&
387
	    dstport != OFP10_PORT_LOCAL &&
388
	    dstport != OFP10_PORT_CONTROLLER)) {
389
		/* fallback to flooding */
390
		dstport = OFP10_PORT_FLOOD;
391
	} else if (srcport == dstport) {
392
		/*
393
		 * silently drop looping packet
394
		 * (don't use OFP10_PORT_INPUT here)
395
		 */
396
		dstport = OFP10_PORT_ANY;
397
	} else {
398
		addflow = 1;
399
	}
400
401
	if ((obuf = ibuf_static()) == NULL)
402
		goto done;
403
404
 again:
405
	if (addflow) {
406
		if ((fm = ibuf_advance(obuf, sizeof(*fm))) == NULL)
407
			goto done;
408
409
		ofp10_packet_match(&pkt, &fm->fm_match, OFP10_WILDCARD_DL_DST);
410
411
		oh = &fm->fm_oh;
412
		fm->fm_cookie = 0; /* XXX should we set a cookie? */
413
		fm->fm_command = htons(OFP_FLOWCMD_ADD);
414
		fm->fm_idle_timeout = htons(sc->sc_cache_timeout);
415
		fm->fm_hard_timeout = 0; /* permanent */
416
		fm->fm_priority = 0;
417
		fm->fm_buffer_id = pin->pin_buffer_id;
418
		fm->fm_flags = htons(OFP_FLOWFLAG_SEND_FLOW_REMOVED);
419
		if (pin->pin_buffer_id == htonl(OFP_PKTOUT_NO_BUFFER))
420
			addpacket = 1;
421
	} else {
422
		if ((pout = ibuf_advance(obuf, sizeof(*pout))) == NULL)
423
			goto done;
424
425
		oh = &pout->pout_oh;
426
		pout->pout_buffer_id = pin->pin_buffer_id;
427
		pout->pout_port = pin->pin_port;
428
		pout->pout_actions_len = htons(sizeof(*ao));
429
430
		if (pin->pin_buffer_id == htonl(OFP_PKTOUT_NO_BUFFER))
431
			addpacket = 1;
432
	}
433
434
	if ((ao = ibuf_advance(obuf, sizeof(*ao))) == NULL)
435
		goto done;
436
	ao->ao_type = htons(OFP_ACTION_OUTPUT);
437
	ao->ao_len =  htons(sizeof(*ao));
438
	ao->ao_port = htons((uint16_t)dstport);
439
	ao->ao_max_len = 0;
440
441
	/* Add optional packet payload to packet-out. */
442
	if (addflow == 0 && addpacket &&
443
	    imsg_add(obuf, pkt.pkt_buf, pkt.pkt_len) == -1)
444
		goto done;
445
446
	/* Set output header */
447
	memcpy(oh, ih, sizeof(*oh));
448
	oh->oh_length = htons(ibuf_length(obuf));
449
	oh->oh_type = addflow ? OFP10_T_FLOW_MOD : OFP10_T_PACKET_OUT;
450
	oh->oh_xid = htonl(con->con_xidnxt++);
451
452
	if (ofp10_validate(sc, &con->con_local, &con->con_peer, oh, obuf) != 0)
453
		goto done;
454
455
	ofp_output(con, NULL, obuf);
456
457
	if (addflow && addpacket) {
458
		/* loop to output the packet again */
459
		addflow = 0;
460
		if ((obuf = ibuf_static()) == NULL)
461
			goto done;
462
		goto again;
463
	}
464
465
	ret = 0;
466
 done:
467
	ibuf_release(obuf);
468
	return (ret);
469
}