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

Line Branch Exec Source
1
/*	$OpenBSD: switch.c,v 1.3 2016/09/18 13:17:40 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/tree.h>
22
#include <sys/socket.h>
23
#include <sys/uio.h>
24
25
#include <stdio.h>
26
#include <stdlib.h>
27
#include <string.h>
28
#include <imsg.h>
29
#include <event.h>
30
31
#include "switchd.h"
32
33
void	 switch_timer(struct switchd *, void *);
34
35
static __inline int
36
	 switch_cmp(struct switch_control *, struct switch_control *);
37
static __inline int
38
	 switch_maccmp(struct macaddr *, struct macaddr *);
39
40
void
41
switch_init(struct switchd *sc)
42
{
43
	RB_INIT(&sc->sc_switches);
44
}
45
46
int
47
switch_dispatch_control(int fd, struct privsep_proc *p, struct imsg *imsg)
48
{
49
	struct privsep		*ps = p->p_ps;
50
	struct switchd		*sc = ps->ps_env;
51
	struct switch_control	*sw;
52
	struct macaddr		*mac;
53
	struct iovec		 iov[2];
54
55
	switch (imsg->hdr.type) {
56
	case IMSG_CTL_SHOW_SUM:
57
		IMSG_SIZE_CHECK(imsg, &fd);
58
59
		RB_FOREACH(sw, switch_head, &sc->sc_switches) {
60
			iov[0].iov_base = imsg->data;
61
			iov[0].iov_len = IMSG_DATA_SIZE(imsg);
62
			iov[1].iov_base = sw;
63
			iov[1].iov_len = sizeof(*sw);
64
65
			proc_composev(ps, PROC_CONTROL,
66
			    IMSG_CTL_SWITCH, iov, 2);
67
68
			RB_FOREACH(mac, macaddr_head, &sw->sw_addrcache) {
69
				iov[0].iov_base = imsg->data;
70
				iov[0].iov_len = IMSG_DATA_SIZE(imsg);
71
				iov[1].iov_base = mac;
72
				iov[1].iov_len = sizeof(*mac);
73
74
				proc_composev(ps, PROC_CONTROL,
75
				    IMSG_CTL_MAC, iov, 2);
76
			}
77
		}
78
79
		proc_compose(ps, PROC_CONTROL,
80
		    IMSG_CTL_END, imsg->data, IMSG_DATA_SIZE(imsg));
81
		return (0);
82
	default:
83
		break;
84
	}
85
86
	return (-1);
87
}
88
89
struct switch_control *
90
switch_get(struct switch_connection *con)
91
{
92
	struct switchd		*sc = con->con_sc;
93
	struct switch_control	 key;
94
95
	memcpy(&key.sw_addr, &con->con_peer, sizeof(key.sw_addr));
96
97
	con->con_switch = RB_FIND(switch_head, &sc->sc_switches, &key);
98
	return (con->con_switch);
99
}
100
101
struct switch_control *
102
switch_add(struct switch_connection *con)
103
{
104
	struct switchd		*sc = con->con_sc;
105
	struct switch_control	*sw, *oldsw;
106
	static unsigned int	 id = 0;
107
108
	/* Connection already has an associated switch */
109
	if (con->con_switch != NULL)
110
		return (NULL);
111
112
	if ((sw = calloc(1, sizeof(*sw))) == NULL)
113
		return (NULL);
114
115
	memcpy(&sw->sw_addr, &con->con_peer, sizeof(sw->sw_addr));
116
	sw->sw_id = ++id;
117
	RB_INIT(&sw->sw_addrcache);
118
119
	if ((oldsw =
120
	    RB_INSERT(switch_head, &sc->sc_switches, sw)) != NULL) {
121
		free(sw);
122
		sw = oldsw;
123
	} else {
124
		timer_set(sc, &sw->sw_timer, switch_timer, sw);
125
		timer_add(sc, &sw->sw_timer, sc->sc_cache_timeout);
126
	}
127
128
	con->con_switch = sw;
129
	return (con->con_switch);
130
}
131
132
void
133
switch_timer(struct switchd *sc, void *arg)
134
{
135
	struct switch_control	*sw = arg;
136
	struct macaddr		*mac, *next;
137
	struct timeval		 tv;
138
	unsigned int		 cnt = 0;
139
140
	getmonotime(&tv);
141
142
	for (mac = RB_MIN(macaddr_head, &sw->sw_addrcache);
143
	    mac != NULL; mac = next) {
144
		next = RB_NEXT(macaddr_head, &sw->sw_addrcache, mac);
145
146
		/* Simple monotonic timeout */
147
		if ((tv.tv_sec - mac->mac_age) >= sc->sc_cache_timeout) {
148
			RB_REMOVE(macaddr_head, &sw->sw_addrcache, mac);
149
			sw->sw_cachesize--;
150
			free(mac);
151
			cnt++;
152
		}
153
	}
154
	if (cnt)
155
		log_debug("%s: flushed %d mac from switch %u after timeout",
156
		    __func__, cnt, sw->sw_id);
157
158
	timer_add(sc, &sw->sw_timer, sc->sc_cache_timeout);
159
}
160
161
void
162
switch_remove(struct switchd *sc, struct switch_control *sw)
163
{
164
	struct macaddr	*mac, *next;
165
166
	if (sw == NULL)
167
		return;
168
169
	timer_del(sc, &sw->sw_timer);
170
171
	for (mac = RB_MIN(macaddr_head, &sw->sw_addrcache);
172
	    mac != NULL; mac = next) {
173
		next = RB_NEXT(macaddr_head, &sw->sw_addrcache, mac);
174
		RB_REMOVE(macaddr_head, &sw->sw_addrcache, mac);
175
		sw->sw_cachesize--;
176
		free(mac);
177
	}
178
	RB_REMOVE(switch_head, &sc->sc_switches, sw);
179
180
	log_debug("%s: switch %u removed", __func__, sw->sw_id);
181
182
	free(sw);
183
}
184
185
struct macaddr *
186
switch_learn(struct switchd *sc, struct switch_control *sw,
187
    uint8_t *ea, uint32_t port)
188
{
189
	struct macaddr	*mac, *oldmac = NULL;
190
	struct timeval	 tv;
191
192
	if ((mac = oldmac = switch_cached(sw, ea)) != NULL)
193
		goto update;
194
195
	if (sw->sw_cachesize >= sc->sc_cache_max)
196
		return (NULL);
197
198
	if ((mac = calloc(1, sizeof(*mac))) == NULL)
199
		return (NULL);
200
201
	memcpy(&mac->mac_addr, ea, sizeof(mac->mac_addr));
202
203
	if (RB_INSERT(macaddr_head, &sw->sw_addrcache, mac) != NULL)
204
		fatalx("cache corrupted");
205
	sw->sw_cachesize++;
206
207
 update:
208
	getmonotime(&tv);
209
	mac->mac_port = port;
210
	mac->mac_age = tv.tv_sec;
211
212
	log_debug("%s: %s mac %s on switch %u port %u",
213
	    __func__, oldmac == NULL ? "learned new" : "updated",
214
	    print_ether(ea), sw->sw_id, port);
215
216
	return (mac);
217
}
218
219
struct macaddr *
220
switch_cached(struct switch_control *sw, uint8_t *ea)
221
{
222
	struct macaddr	 key;
223
	memcpy(&key.mac_addr, ea, sizeof(key.mac_addr));
224
	return (RB_FIND(macaddr_head, &sw->sw_addrcache, &key));
225
}
226
227
static __inline int
228
switch_cmp(struct switch_control *a, struct switch_control *b)
229
{
230
	int		diff = 0;
231
232
	diff = sockaddr_cmp((struct sockaddr *)&a->sw_addr,
233
	    (struct sockaddr *)&b->sw_addr, 128);
234
	if (!diff)
235
		diff = socket_getport(&a->sw_addr) -
236
		    socket_getport(&b->sw_addr);
237
238
	return (diff);
239
}
240
241
static __inline int
242
switch_maccmp(struct macaddr *a, struct macaddr *b)
243
{
244
	return (memcmp(a->mac_addr, b->mac_addr, sizeof(a->mac_addr)));
245
}
246
247
RB_GENERATE(switch_head, switch_control, sw_entry, switch_cmp);
248
RB_GENERATE(macaddr_head, macaddr, mac_entry, switch_maccmp);