BadVPN – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | /** |
2 | * @file |
||
3 | * lwIP netif implementing an IEEE 802.1D MAC Bridge |
||
4 | */ |
||
5 | |||
6 | /* |
||
7 | * Copyright (c) 2017 Simon Goldschmidt. |
||
8 | * All rights reserved. |
||
9 | * |
||
10 | * Redistribution and use in source and binary forms, with or without modification, |
||
11 | * are permitted provided that the following conditions are met: |
||
12 | * |
||
13 | * 1. Redistributions of source code must retain the above copyright notice, |
||
14 | * this list of conditions and the following disclaimer. |
||
15 | * 2. Redistributions in binary form must reproduce the above copyright notice, |
||
16 | * this list of conditions and the following disclaimer in the documentation |
||
17 | * and/or other materials provided with the distribution. |
||
18 | * 3. The name of the author may not be used to endorse or promote products |
||
19 | * derived from this software without specific prior written permission. |
||
20 | * |
||
21 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED |
||
22 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
||
23 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT |
||
24 | * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||
25 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT |
||
26 | * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
||
27 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
||
28 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING |
||
29 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY |
||
30 | * OF SUCH DAMAGE. |
||
31 | * |
||
32 | * This file is part of the lwIP TCP/IP stack. |
||
33 | * |
||
34 | * Author: Simon Goldschmidt <goldsimon@gmx.de> |
||
35 | * |
||
36 | */ |
||
37 | |||
38 | /** |
||
39 | * @defgroup bridgeif IEEE 802.1D bridge |
||
40 | * @ingroup netifs |
||
41 | * This file implements an IEEE 802.1D bridge by using a multilayer netif approach |
||
42 | * (one hardware-independent netif for the bridge that uses hardware netifs for its ports). |
||
43 | * On transmit, the bridge selects the outgoing port(s). |
||
44 | * On receive, the port netif calls into the bridge (via its netif->input function) and |
||
45 | * the bridge selects the port(s) (and/or its netif->input function) to pass the received pbuf to. |
||
46 | * |
||
47 | * Usage: |
||
48 | * - add the port netifs just like you would when using them as dedicated netif without a bridge |
||
49 | * - only NETIF_FLAG_ETHARP/NETIF_FLAG_ETHERNET netifs are supported as bridge ports |
||
50 | * - add the bridge port netifs without IPv4 addresses (i.e. pass 'NULL, NULL, NULL') |
||
51 | * - don't add IPv6 addresses to the port netifs! |
||
52 | * - set up the bridge configuration in a global variable of type 'bridgeif_initdata_t' that contains |
||
53 | * - the MAC address of the bridge |
||
54 | * - some configuration options controlling the memory consumption (maximum number of ports |
||
55 | * and FDB entries) |
||
56 | * - e.g. for a bridge MAC address 00-01-02-03-04-05, 2 bridge ports, 1024 FDB entries + 16 static MAC entries: |
||
57 | * bridgeif_initdata_t mybridge_initdata = BRIDGEIF_INITDATA1(2, 1024, 16, ETH_ADDR(0, 1, 2, 3, 4, 5)); |
||
58 | * - add the bridge netif (with IPv4 config): |
||
59 | * struct netif bridge_netif; |
||
60 | * netif_add(&bridge_netif, &my_ip, &my_netmask, &my_gw, &mybridge_initdata, bridgeif_init, tcpip_input); |
||
61 | * NOTE: the passed 'input' function depends on BRIDGEIF_PORT_NETIFS_OUTPUT_DIRECT setting, |
||
62 | * which controls where the forwarding is done (netif low level input context vs. tcpip_thread) |
||
63 | * - set up all ports netifs and the bridge netif |
||
64 | * |
||
65 | * - When adding a port netif, NETIF_FLAG_ETHARP flag will be removed from a port |
||
66 | * to prevent ETHARP working on that port netif (we only want one IP per bridge not per port). |
||
67 | * - When adding a port netif, its input function is changed to call into the bridge. |
||
68 | * |
||
69 | * |
||
70 | * @todo: |
||
71 | * - compact static FDB entries (instead of walking the whole array) |
||
72 | * - add FDB query/read access |
||
73 | * - add FDB change callback (when learning or dropping auto-learned entries) |
||
74 | * - prefill FDB with MAC classes that should never be forwarded |
||
75 | * - multicast snooping? (and only forward group addresses to interested ports) |
||
76 | * - support removing ports |
||
77 | * - check SNMP integration |
||
78 | * - VLAN handling / trunk ports |
||
79 | * - priority handling? (although that largely depends on TX queue limitations and lwIP doesn't provide tx-done handling) |
||
80 | */ |
||
81 | |||
82 | #include "netif/bridgeif.h" |
||
83 | #include "lwip/netif.h" |
||
84 | #include "lwip/sys.h" |
||
85 | #include "lwip/etharp.h" |
||
86 | #include "lwip/ethip6.h" |
||
87 | #include "lwip/snmp.h" |
||
88 | #include "lwip/timeouts.h" |
||
89 | #include <string.h> |
||
90 | |||
91 | #if LWIP_NUM_NETIF_CLIENT_DATA |
||
92 | |||
93 | #define BRIDGEIF_AGE_TIMER_MS 1000 |
||
94 | |||
95 | #if BRIDGEIF_PORT_NETIFS_OUTPUT_DIRECT |
||
96 | #ifndef BRIDGEIF_DECL_PROTECT |
||
97 | /* define bridgeif protection to sys_arch_protect... */ |
||
98 | #include "lwip/sys.h" |
||
99 | #define BRIDGEIF_DECL_PROTECT(lev) SYS_ARCH_DECL_PROTECT(lev) |
||
100 | #define BRIDGEIF_READ_PROTECT(lev) SYS_ARCH_PROTECT(lev) |
||
101 | #define BRIDGEIF_READ_UNPROTECT(lev) SYS_ARCH_UNPROTECT(lev) |
||
102 | #define BRIDGEIF_WRITE_PROTECT(lev) |
||
103 | #define BRIDGEIF_WRITE_UNPROTECT(lev) |
||
104 | #endif |
||
105 | #else /* BRIDGEIF_PORT_NETIFS_OUTPUT_DIRECT */ |
||
106 | #include "lwip/tcpip.h" |
||
107 | #define BRIDGEIF_DECL_PROTECT(lev) |
||
108 | #define BRIDGEIF_READ_PROTECT(lev) |
||
109 | #define BRIDGEIF_READ_UNPROTECT(lev) |
||
110 | #define BRIDGEIF_WRITE_PROTECT(lev) |
||
111 | #define BRIDGEIF_WRITE_UNPROTECT(lev) |
||
112 | #endif /* BRIDGEIF_PORT_NETIFS_OUTPUT_DIRECT */ |
||
113 | |||
114 | /* Define those to better describe your network interface. */ |
||
115 | #define IFNAME0 'b' |
||
116 | #define IFNAME1 'r' |
||
117 | |||
118 | #define BR_FDB_TIMEOUT_SEC (60*5) /* 5 minutes FDB timeout */ |
||
119 | |||
120 | #if !BRIDGEIF_EXTERNAL_FDB |
||
121 | typedef struct bridgeif_dfdb_entry_s { |
||
122 | u8_t used; |
||
123 | u8_t port; |
||
124 | u32_t ts; |
||
125 | struct eth_addr addr; |
||
126 | } bridgeif_dfdb_entry_t; |
||
127 | |||
128 | typedef struct bridgeif_dfdb_s { |
||
129 | u16_t max_fdb_entries; |
||
130 | bridgeif_dfdb_entry_t *fdb; |
||
131 | } bridgeif_dfdb_t; |
||
132 | #endif /* BRIDGEIF_EXTERNAL_FDB */ |
||
133 | |||
134 | struct bridgeif_private_s; |
||
135 | typedef struct bridgeif_port_private_s { |
||
136 | struct bridgeif_private_s *bridge; |
||
137 | struct netif *port_netif; |
||
138 | u8_t port_num; |
||
139 | } bridgeif_port_t; |
||
140 | |||
141 | typedef struct bridgeif_fdb_static_entry_s { |
||
142 | u8_t used; |
||
143 | bridgeif_portmask_t dst_ports; |
||
144 | struct eth_addr addr; |
||
145 | } bridgeif_fdb_static_entry_t; |
||
146 | |||
147 | typedef struct bridgeif_private_s { |
||
148 | struct netif *netif; |
||
149 | struct eth_addr ethaddr; |
||
150 | u8_t max_ports; |
||
151 | u8_t num_ports; |
||
152 | bridgeif_port_t *ports; |
||
153 | u16_t max_fdbs_entries; |
||
154 | bridgeif_fdb_static_entry_t *fdbs; |
||
155 | u16_t max_fdbd_entries; |
||
156 | void *fdbd; |
||
157 | } bridgeif_private_t; |
||
158 | |||
159 | /* netif data index to get the bridge on input */ |
||
160 | u8_t bridgeif_netif_client_id = 0xff; |
||
161 | |||
162 | #if !BRIDGEIF_EXTERNAL_FDB |
||
163 | /** A real simple and slow implementation of an auto-learning forwarding database that |
||
164 | * remembers known src mac addresses to know which port to send frames destined for that |
||
165 | * mac address. |
||
166 | * |
||
167 | * ATTENTION: This is meant as an example only, in real-world use, you should override this |
||
168 | * by setting BRIDGEIF_EXTERNAL_FDB==1 and providing a better implementation :-) |
||
169 | */ |
||
170 | static void |
||
171 | bridgeif_fdb_update_src(void *fdb_ptr, struct eth_addr *src_addr, u8_t port_idx) |
||
172 | { |
||
173 | int i; |
||
174 | bridgeif_dfdb_t *fdb = (bridgeif_dfdb_t*)fdb_ptr; |
||
175 | BRIDGEIF_DECL_PROTECT(lev); |
||
176 | BRIDGEIF_READ_PROTECT(lev); |
||
177 | for (i = 0; i < fdb->max_fdb_entries; i++) { |
||
178 | bridgeif_dfdb_entry_t *e = &fdb->fdb[i]; |
||
179 | if (e->used && e->ts) { |
||
180 | if (!memcmp(&e->addr, src_addr, sizeof(struct eth_addr))) { |
||
181 | LWIP_DEBUGF(BRIDGEIF_FDB_DEBUG, ("br: update src %02x:%02x:%02x:%02x:%02x:%02x (from %d) @ idx %d\n", |
||
182 | src_addr->addr[0], src_addr->addr[1], src_addr->addr[2], src_addr->addr[3], src_addr->addr[4], src_addr->addr[5], |
||
183 | port_idx, i)); |
||
184 | BRIDGEIF_WRITE_PROTECT(lev); |
||
185 | e->ts = BR_FDB_TIMEOUT_SEC; |
||
186 | e->port = port_idx; |
||
187 | BRIDGEIF_WRITE_UNPROTECT(lev); |
||
188 | BRIDGEIF_READ_UNPROTECT(lev); |
||
189 | return; |
||
190 | } |
||
191 | } |
||
192 | } |
||
193 | /* not found, allocate new entry from free */ |
||
194 | for (i = 0; i < fdb->max_fdb_entries; i++) { |
||
195 | bridgeif_dfdb_entry_t *e = &fdb->fdb[i]; |
||
196 | if (!e->used || !e->ts) { |
||
197 | BRIDGEIF_WRITE_PROTECT(lev); |
||
198 | /* check again when protected */ |
||
199 | if (!e->used || !e->ts) { |
||
200 | LWIP_DEBUGF(BRIDGEIF_FDB_DEBUG, ("br: create src %02x:%02x:%02x:%02x:%02x:%02x (from %d) @ idx %d\n", |
||
201 | src_addr->addr[0], src_addr->addr[1], src_addr->addr[2], src_addr->addr[3], src_addr->addr[4], src_addr->addr[5], |
||
202 | port_idx, i)); |
||
203 | memcpy(&e->addr, src_addr, sizeof(struct eth_addr)); |
||
204 | e->ts = BR_FDB_TIMEOUT_SEC; |
||
205 | e->port = port_idx; |
||
206 | e->used = 1; |
||
207 | BRIDGEIF_WRITE_UNPROTECT(lev); |
||
208 | BRIDGEIF_READ_UNPROTECT(lev); |
||
209 | return; |
||
210 | } |
||
211 | BRIDGEIF_WRITE_UNPROTECT(lev); |
||
212 | } |
||
213 | } |
||
214 | BRIDGEIF_READ_UNPROTECT(lev); |
||
215 | /* not found, no free entry -> flood */ |
||
216 | } |
||
217 | |||
218 | /** Walk our list of auto-learnt fdb entries and return a port to forward or BR_FLOOD if unknown */ |
||
219 | static bridgeif_portmask_t |
||
220 | bridgeif_fdb_get_dst_ports(void *fdb_ptr, struct eth_addr *dst_addr) |
||
221 | { |
||
222 | int i; |
||
223 | bridgeif_dfdb_t *fdb = (bridgeif_dfdb_t*)fdb_ptr; |
||
224 | BRIDGEIF_DECL_PROTECT(lev); |
||
225 | BRIDGEIF_READ_PROTECT(lev); |
||
226 | for (i = 0; i < fdb->max_fdb_entries; i++) { |
||
227 | bridgeif_dfdb_entry_t *e = &fdb->fdb[i]; |
||
228 | if (e->used && e->ts) { |
||
229 | if (!memcmp(&e->addr, dst_addr, sizeof(struct eth_addr))) { |
||
230 | bridgeif_portmask_t ret = (bridgeif_portmask_t)(1 << e->port); |
||
231 | BRIDGEIF_READ_UNPROTECT(lev); |
||
232 | return ret; |
||
233 | } |
||
234 | } |
||
235 | } |
||
236 | BRIDGEIF_READ_UNPROTECT(lev); |
||
237 | return BR_FLOOD; |
||
238 | } |
||
239 | |||
240 | /** Init our simple fdb list */ |
||
241 | static void* |
||
242 | bridgeif_fdb_init(u16_t max_fdb_entries) |
||
243 | { |
||
244 | bridgeif_dfdb_t *fdb; |
||
245 | size_t alloc_len_sizet = sizeof(bridgeif_dfdb_t) + (max_fdb_entries*sizeof(bridgeif_dfdb_entry_t)); |
||
246 | mem_size_t alloc_len = (mem_size_t)alloc_len_sizet; |
||
247 | LWIP_ASSERT("alloc_len == alloc_len_sizet", alloc_len == alloc_len_sizet); |
||
248 | LWIP_DEBUGF(BRIDGEIF_DEBUG, ("bridgeif_fdb_init: allocating %d bytes for private FDB data\n", (int)alloc_len)); |
||
249 | fdb = (bridgeif_dfdb_t*)mem_calloc(1, alloc_len); |
||
250 | if (fdb == NULL) { |
||
251 | return NULL; |
||
252 | } |
||
253 | fdb->max_fdb_entries = max_fdb_entries; |
||
254 | fdb->fdb = (bridgeif_dfdb_entry_t *)(fdb + 1); |
||
255 | return fdb; |
||
256 | } |
||
257 | |||
258 | /** Aging implementation of our simple fdb */ |
||
259 | static void |
||
260 | bridgeif_fdb_age_one_second(void *fdb_ptr) |
||
261 | { |
||
262 | int i; |
||
263 | bridgeif_dfdb_t *fdb; |
||
264 | BRIDGEIF_DECL_PROTECT(lev); |
||
265 | |||
266 | fdb = (bridgeif_dfdb_t *)fdb_ptr; |
||
267 | BRIDGEIF_READ_PROTECT(lev); |
||
268 | |||
269 | for (i = 0; i < fdb->max_fdb_entries; i++) { |
||
270 | bridgeif_dfdb_entry_t *e = &fdb->fdb[i]; |
||
271 | if (e->used && e->ts) { |
||
272 | BRIDGEIF_WRITE_PROTECT(lev); |
||
273 | /* check again when protected */ |
||
274 | if (e->used && e->ts) { |
||
275 | if (--e->ts == 0) { |
||
276 | e->used = 0; |
||
277 | } |
||
278 | } |
||
279 | BRIDGEIF_WRITE_UNPROTECT(lev); |
||
280 | } |
||
281 | } |
||
282 | BRIDGEIF_READ_UNPROTECT(lev); |
||
283 | } |
||
284 | |||
285 | #endif /* !BRIDGEIF_EXTERNAL_FDB */ |
||
286 | |||
287 | /** Timer callback for fdb aging, called once per second */ |
||
288 | static void |
||
289 | bridgeif_age_tmr(void *arg) |
||
290 | { |
||
291 | struct netif *bridgeif; |
||
292 | bridgeif_private_t *br; |
||
293 | LWIP_ASSERT("invalid arg", arg != NULL); |
||
294 | bridgeif = (struct netif *)arg; |
||
295 | LWIP_ASSERT("invalid netif state", bridgeif->state != NULL); |
||
296 | |||
297 | br = (bridgeif_private_t *)bridgeif->state; |
||
298 | bridgeif_fdb_age_one_second(br->fdbd); |
||
299 | sys_timeout(BRIDGEIF_AGE_TIMER_MS, bridgeif_age_tmr, arg); |
||
300 | } |
||
301 | |||
302 | /** |
||
303 | * @ingroup bridgeif |
||
304 | * Add a static entry to the forwarding database. |
||
305 | * A static entry marks where frames to a specific eth address (unicast or group address) are |
||
306 | * forwarded. |
||
307 | * bits [0..(BRIDGEIF_MAX_PORTS-1)]: hw ports |
||
308 | * bit [BRIDGEIF_MAX_PORTS]: cpu port |
||
309 | * 0: drop |
||
310 | */ |
||
311 | err_t |
||
312 | bridgeif_fdb_add(struct netif *bridgeif, const struct eth_addr *addr, bridgeif_portmask_t ports) |
||
313 | { |
||
314 | int i; |
||
315 | bridgeif_private_t *br; |
||
316 | BRIDGEIF_DECL_PROTECT(lev); |
||
317 | LWIP_ASSERT("invalid netif", bridgeif != NULL); |
||
318 | br = (bridgeif_private_t*)bridgeif->state; |
||
319 | LWIP_ASSERT("invalid state", br != NULL); |
||
320 | |||
321 | BRIDGEIF_READ_PROTECT(lev); |
||
322 | for (i = 0; i < br->max_fdbs_entries; i++) { |
||
323 | if (!br->fdbs[i].used) { |
||
324 | BRIDGEIF_WRITE_PROTECT(lev); |
||
325 | if (!br->fdbs[i].used) { |
||
326 | br->fdbs[i].used = 1; |
||
327 | br->fdbs[i].dst_ports = ports; |
||
328 | memcpy(&br->fdbs[i].addr, addr, sizeof(struct eth_addr)); |
||
329 | BRIDGEIF_WRITE_UNPROTECT(lev); |
||
330 | BRIDGEIF_READ_UNPROTECT(lev); |
||
331 | return ERR_OK; |
||
332 | } |
||
333 | BRIDGEIF_WRITE_UNPROTECT(lev); |
||
334 | } |
||
335 | } |
||
336 | BRIDGEIF_READ_UNPROTECT(lev); |
||
337 | return ERR_MEM; |
||
338 | } |
||
339 | |||
340 | /** |
||
341 | * @ingroup bridgeif |
||
342 | * Remove a static entry from the forwarding database |
||
343 | */ |
||
344 | err_t |
||
345 | bridgeif_fdb_remove(struct netif *bridgeif, const struct eth_addr *addr) |
||
346 | { |
||
347 | int i; |
||
348 | bridgeif_private_t *br; |
||
349 | BRIDGEIF_DECL_PROTECT(lev); |
||
350 | LWIP_ASSERT("invalid netif", bridgeif != NULL); |
||
351 | br = (bridgeif_private_t*)bridgeif->state; |
||
352 | LWIP_ASSERT("invalid state", br != NULL); |
||
353 | |||
354 | BRIDGEIF_READ_PROTECT(lev); |
||
355 | for (i = 0; i < br->max_fdbs_entries; i++) { |
||
356 | if (br->fdbs[i].used && !memcmp(&br->fdbs[i].addr, addr, sizeof(struct eth_addr))) { |
||
357 | BRIDGEIF_WRITE_PROTECT(lev); |
||
358 | if (br->fdbs[i].used && !memcmp(&br->fdbs[i].addr, addr, sizeof(struct eth_addr))) { |
||
359 | memset(&br->fdbs[i], 0, sizeof(bridgeif_fdb_static_entry_t)); |
||
360 | BRIDGEIF_WRITE_UNPROTECT(lev); |
||
361 | BRIDGEIF_READ_UNPROTECT(lev); |
||
362 | return ERR_OK; |
||
363 | } |
||
364 | BRIDGEIF_WRITE_UNPROTECT(lev); |
||
365 | } |
||
366 | } |
||
367 | BRIDGEIF_READ_UNPROTECT(lev); |
||
368 | return ERR_VAL; |
||
369 | } |
||
370 | |||
371 | /** Get the forwarding port(s) (as bit mask) for the specified destination mac address */ |
||
372 | static bridgeif_portmask_t |
||
373 | bridgeif_find_dst_ports(bridgeif_private_t *br, struct eth_addr *dst_addr) |
||
374 | { |
||
375 | int i; |
||
376 | BRIDGEIF_DECL_PROTECT(lev); |
||
377 | BRIDGEIF_READ_PROTECT(lev); |
||
378 | /* first check for static entries */ |
||
379 | for (i = 0; i < br->max_fdbs_entries; i++) { |
||
380 | if (br->fdbs[i].used) { |
||
381 | if (!memcmp(&br->fdbs[i].addr, dst_addr, sizeof(struct eth_addr))) { |
||
382 | bridgeif_portmask_t ret = br->fdbs[i].dst_ports; |
||
383 | BRIDGEIF_READ_UNPROTECT(lev); |
||
384 | return ret; |
||
385 | } |
||
386 | } |
||
387 | } |
||
388 | if (dst_addr->addr[0] & 1) { |
||
389 | /* no match found: flood remaining group address */ |
||
390 | BRIDGEIF_READ_UNPROTECT(lev); |
||
391 | return BR_FLOOD; |
||
392 | } |
||
393 | BRIDGEIF_READ_UNPROTECT(lev); |
||
394 | /* no match found: check dynamic fdb for port or fall back to flooding */ |
||
395 | return bridgeif_fdb_get_dst_ports(br->fdbd, dst_addr); |
||
396 | } |
||
397 | |||
398 | /** Helper function to see if a destination mac belongs to the bridge |
||
399 | * (bridge netif or one of the port netifs), in which case the frame |
||
400 | * is sent to the cpu only. |
||
401 | */ |
||
402 | static int |
||
403 | bridgeif_is_local_mac(bridgeif_private_t *br, struct eth_addr *addr) |
||
404 | { |
||
405 | int i; |
||
406 | BRIDGEIF_DECL_PROTECT(lev); |
||
407 | if (!memcmp(br->netif->hwaddr, addr, sizeof(struct eth_addr))) { |
||
408 | return 1; |
||
409 | } |
||
410 | BRIDGEIF_READ_PROTECT(lev); |
||
411 | for (i = 0; i < br->num_ports; i++) { |
||
412 | struct netif *portif = br->ports[i].port_netif; |
||
413 | if (portif != NULL) { |
||
414 | if (!memcmp(portif->hwaddr, addr, sizeof(struct eth_addr))) { |
||
415 | BRIDGEIF_READ_UNPROTECT(lev); |
||
416 | return 1; |
||
417 | } |
||
418 | } |
||
419 | } |
||
420 | BRIDGEIF_READ_UNPROTECT(lev); |
||
421 | return 0; |
||
422 | } |
||
423 | |||
424 | /* Output helper function */ |
||
425 | static err_t |
||
426 | bridgeif_send_to_port(bridgeif_private_t *br, struct pbuf *p, u8_t dstport_idx) |
||
427 | { |
||
428 | if (dstport_idx < BRIDGEIF_MAX_PORTS) { |
||
429 | /* possibly an external port */ |
||
430 | if (dstport_idx < br->max_ports) { |
||
431 | struct netif *portif = br->ports[dstport_idx].port_netif; |
||
432 | if (br->ports[dstport_idx].port_netif != NULL) { |
||
433 | if ((portif != NULL) && (portif->linkoutput != NULL)) { |
||
434 | /* prevent sending out to rx port */ |
||
435 | if (netif_get_index(portif) != p->if_idx) { |
||
436 | if (netif_is_link_up(portif)) { |
||
437 | LWIP_DEBUGF(BRIDGEIF_FW_DEBUG, ("br -> flood(%p:%d) -> %d\n", (void*)p, p->if_idx, netif_get_index(portif))); |
||
438 | return portif->linkoutput(portif, p); |
||
439 | } |
||
440 | } |
||
441 | } |
||
442 | } |
||
443 | } |
||
444 | } else { |
||
445 | LWIP_ASSERT("invalid port index", dstport_idx == BRIDGEIF_MAX_PORTS); |
||
446 | } |
||
447 | return ERR_OK; |
||
448 | } |
||
449 | |||
450 | /** Helper function to pass a pbuf to all ports marked in 'dstports' |
||
451 | */ |
||
452 | static err_t |
||
453 | bridgeif_send_to_ports(bridgeif_private_t *br, struct pbuf *p, bridgeif_portmask_t dstports) |
||
454 | { |
||
455 | err_t err, ret_err = ERR_OK; |
||
456 | u8_t i; |
||
457 | bridgeif_portmask_t mask = 1; |
||
458 | BRIDGEIF_DECL_PROTECT(lev); |
||
459 | BRIDGEIF_READ_PROTECT(lev); |
||
460 | for (i = 0; i < BRIDGEIF_MAX_PORTS; i++, mask = (bridgeif_portmask_t)(mask << 1)) { |
||
461 | if (dstports & mask) { |
||
462 | err = bridgeif_send_to_port(br, p, i); |
||
463 | if (err != ERR_OK) { |
||
464 | ret_err = err; |
||
465 | } |
||
466 | } |
||
467 | } |
||
468 | BRIDGEIF_READ_UNPROTECT(lev); |
||
469 | return ret_err; |
||
470 | } |
||
471 | |||
472 | /** Output function of the application port of the bridge (the one with an ip address). |
||
473 | * The forwarding port(s) where this pbuf is sent on is/are automatically selected |
||
474 | * from the FDB. |
||
475 | */ |
||
476 | static err_t |
||
477 | bridgeif_output(struct netif *netif, struct pbuf *p) |
||
478 | { |
||
479 | err_t err; |
||
480 | bridgeif_private_t *br = (bridgeif_private_t*)netif->state; |
||
481 | struct eth_addr *dst = (struct eth_addr *)(p->payload); |
||
482 | |||
483 | bridgeif_portmask_t dstports = bridgeif_find_dst_ports(br, dst); |
||
484 | err = bridgeif_send_to_ports(br, p, dstports); |
||
485 | |||
486 | MIB2_STATS_NETIF_ADD(netif, ifoutoctets, p->tot_len); |
||
487 | if (((u8_t*)p->payload)[0] & 1) { |
||
488 | /* broadcast or multicast packet*/ |
||
489 | MIB2_STATS_NETIF_INC(netif, ifoutnucastpkts); |
||
490 | } else { |
||
491 | /* unicast packet */ |
||
492 | MIB2_STATS_NETIF_INC(netif, ifoutucastpkts); |
||
493 | } |
||
494 | /* increase ifoutdiscards or ifouterrors on error */ |
||
495 | |||
496 | LINK_STATS_INC(link.xmit); |
||
497 | |||
498 | return err; |
||
499 | } |
||
500 | |||
501 | /** The actual bridge input function. Port netif's input is changed to call |
||
502 | * here. This function decides where the frame is forwarded. |
||
503 | */ |
||
504 | static err_t |
||
505 | bridgeif_input(struct pbuf *p, struct netif *netif) |
||
506 | { |
||
507 | u8_t rx_idx; |
||
508 | bridgeif_portmask_t dstports; |
||
509 | struct eth_addr *src, *dst; |
||
510 | bridgeif_private_t *br; |
||
511 | bridgeif_port_t *port; |
||
512 | if (p == NULL || netif == NULL) { |
||
513 | return ERR_VAL; |
||
514 | } |
||
515 | port = (bridgeif_port_t *)netif_get_client_data(netif, bridgeif_netif_client_id); |
||
516 | LWIP_ASSERT("port data not set", port != NULL); |
||
517 | if (port == NULL || port->bridge == NULL) { |
||
518 | return ERR_VAL; |
||
519 | } |
||
520 | br = (bridgeif_private_t *)port->bridge; |
||
521 | rx_idx = netif_get_index(netif); |
||
522 | /* store receive index in pbuf */ |
||
523 | p->if_idx = rx_idx; |
||
524 | |||
525 | dst = (struct eth_addr*)p->payload; |
||
526 | src = (struct eth_addr*)(((u8_t*)p->payload) + sizeof(struct eth_addr)); |
||
527 | |||
528 | if ((src->addr[0] & 1) == 0) { |
||
529 | /* update src for all non-group addresses */ |
||
530 | bridgeif_fdb_update_src(br->fdbd, src, port->port_num); |
||
531 | } |
||
532 | |||
533 | if (dst->addr[0] & 1) { |
||
534 | /* group address -> flood + cpu? */ |
||
535 | dstports = bridgeif_find_dst_ports(br, dst); |
||
536 | bridgeif_send_to_ports(br, p, dstports); |
||
537 | if (dstports & (1 << BRIDGEIF_MAX_PORTS)) { |
||
538 | /* we pass the reference to ->input or have to free it */ |
||
539 | LWIP_DEBUGF(BRIDGEIF_FW_DEBUG, ("br -> input(%p)\n", (void*)p)); |
||
540 | if (br->netif->input(p, br->netif) != ERR_OK) { |
||
541 | pbuf_free(p); |
||
542 | } |
||
543 | } else { |
||
544 | /* all references done */ |
||
545 | pbuf_free(p); |
||
546 | } |
||
547 | /* always return ERR_OK here to prevent the caller freeing the pbuf */ |
||
548 | return ERR_OK; |
||
549 | } else { |
||
550 | /* is this for one of the local ports? */ |
||
551 | if (bridgeif_is_local_mac(br, dst)) { |
||
552 | /* yes, send to cpu port only */ |
||
553 | LWIP_DEBUGF(BRIDGEIF_FW_DEBUG, ("br -> input(%p)\n", (void*)p)); |
||
554 | return br->netif->input(p, br->netif); |
||
555 | } |
||
556 | |||
557 | /* get dst port */ |
||
558 | dstports = bridgeif_find_dst_ports(br, dst); |
||
559 | bridgeif_send_to_ports(br, p, dstports); |
||
560 | /* no need to send to cpu, flooding is for external ports only */ |
||
561 | /* by this, we consumed the pbuf */ |
||
562 | pbuf_free(p); |
||
563 | /* always return ERR_OK here to prevent the caller freeing the pbuf */ |
||
564 | return ERR_OK; |
||
565 | } |
||
566 | } |
||
567 | |||
568 | #if !BRIDGEIF_PORT_NETIFS_OUTPUT_DIRECT |
||
569 | /** Input function for port netifs used to synchronize into tcpip_thread. |
||
570 | */ |
||
571 | static err_t |
||
572 | bridgeif_tcpip_input(struct pbuf *p, struct netif *netif) |
||
573 | { |
||
574 | return tcpip_inpkt(p, netif, bridgeif_input); |
||
575 | } |
||
576 | #endif /* BRIDGEIF_PORT_NETIFS_OUTPUT_DIRECT */ |
||
577 | |||
578 | /** |
||
579 | * @ingroup bridgeif |
||
580 | * Initialization function passed to netif_add(). |
||
581 | * |
||
582 | * ATTENTION: A pointer to a @ref bridgeif_initdata_t must be passed as 'state' |
||
583 | * to @ref netif_add when adding the bridge. I supplies MAC address |
||
584 | * and controls memory allocation (number of ports, FDB size). |
||
585 | * |
||
586 | * @param netif the lwip network interface structure for this ethernetif |
||
587 | * @return ERR_OK if the loopif is initialized |
||
588 | * ERR_MEM if private data couldn't be allocated |
||
589 | * any other err_t on error |
||
590 | */ |
||
591 | err_t |
||
592 | bridgeif_init(struct netif *netif) |
||
593 | { |
||
594 | bridgeif_initdata_t *init_data; |
||
595 | bridgeif_private_t *br; |
||
596 | size_t alloc_len_sizet; |
||
597 | mem_size_t alloc_len; |
||
598 | |||
599 | LWIP_ASSERT("netif != NULL", (netif != NULL)); |
||
600 | #if !BRIDGEIF_PORT_NETIFS_OUTPUT_DIRECT |
||
601 | if (netif->input == tcpip_input) { |
||
602 | LWIP_DEBUGF(BRIDGEIF_DEBUG|LWIP_DBG_ON, ("bridgeif does not need tcpip_input, use netif_input/ethernet_input instead")); |
||
603 | } |
||
604 | #endif |
||
605 | |||
606 | if (bridgeif_netif_client_id == 0xFF) { |
||
607 | bridgeif_netif_client_id = netif_alloc_client_data_id(); |
||
608 | } |
||
609 | |||
610 | init_data = (bridgeif_initdata_t *)netif->state; |
||
611 | LWIP_ASSERT("init_data != NULL", (init_data != NULL)); |
||
612 | LWIP_ASSERT("init_data->max_ports <= BRIDGEIF_MAX_PORTS", |
||
613 | init_data->max_ports <= BRIDGEIF_MAX_PORTS); |
||
614 | |||
615 | alloc_len_sizet = sizeof(bridgeif_private_t) + (init_data->max_ports*sizeof(bridgeif_port_t) + (init_data->max_fdb_static_entries*sizeof(bridgeif_fdb_static_entry_t))); |
||
616 | alloc_len = (mem_size_t)alloc_len_sizet; |
||
617 | LWIP_ASSERT("alloc_len == alloc_len_sizet", alloc_len == alloc_len_sizet); |
||
618 | LWIP_DEBUGF(BRIDGEIF_DEBUG, ("bridgeif_init: allocating %d bytes for private data\n", (int)alloc_len)); |
||
619 | br = (bridgeif_private_t*)mem_calloc(1, alloc_len); |
||
620 | if (br == NULL) { |
||
621 | LWIP_DEBUGF(NETIF_DEBUG, ("bridgeif_init: out of memory\n")); |
||
622 | return ERR_MEM; |
||
623 | } |
||
624 | memcpy(&br->ethaddr, &init_data->ethaddr, sizeof(br->ethaddr)); |
||
625 | br->netif = netif; |
||
626 | |||
627 | br->max_ports = init_data->max_ports; |
||
628 | br->ports = (bridgeif_port_t*)(br + 1); |
||
629 | |||
630 | br->max_fdbs_entries = init_data->max_fdb_static_entries; |
||
631 | br->fdbs = (bridgeif_fdb_static_entry_t*)(((u8_t*)(br + 1)) + (init_data->max_ports*sizeof(bridgeif_port_t))); |
||
632 | |||
633 | br->max_fdbd_entries = init_data->max_fdb_dynamic_entries; |
||
634 | br->fdbd = bridgeif_fdb_init(init_data->max_fdb_dynamic_entries); |
||
635 | if (br->fdbd == NULL) { |
||
636 | LWIP_DEBUGF(NETIF_DEBUG, ("bridgeif_init: out of memory in fdb_init\n")); |
||
637 | mem_free(br); |
||
638 | return ERR_MEM; |
||
639 | } |
||
640 | |||
641 | #if LWIP_NETIF_HOSTNAME |
||
642 | /* Initialize interface hostname */ |
||
643 | netif->hostname = "lwip"; |
||
644 | #endif /* LWIP_NETIF_HOSTNAME */ |
||
645 | |||
646 | /* |
||
647 | * Initialize the snmp variables and counters inside the struct netif. |
||
648 | * The last argument should be replaced with your link speed, in units |
||
649 | * of bits per second. |
||
650 | */ |
||
651 | MIB2_INIT_NETIF(netif, snmp_ifType_ethernet_csmacd, 0); |
||
652 | |||
653 | netif->state = br; |
||
654 | netif->name[0] = IFNAME0; |
||
655 | netif->name[1] = IFNAME1; |
||
656 | /* We directly use etharp_output() here to save a function call. |
||
657 | * You can instead declare your own function an call etharp_output() |
||
658 | * from it if you have to do some checks before sending (e.g. if link |
||
659 | * is available...) */ |
||
660 | #if LWIP_IPV4 |
||
661 | netif->output = etharp_output; |
||
662 | #endif /* LWIP_IPV4 */ |
||
663 | #if LWIP_IPV6 |
||
664 | netif->output_ip6 = ethip6_output; |
||
665 | #endif /* LWIP_IPV6 */ |
||
666 | netif->linkoutput = bridgeif_output; |
||
667 | |||
668 | /* set MAC hardware address length */ |
||
669 | netif->hwaddr_len = ETH_HWADDR_LEN; |
||
670 | |||
671 | /* set MAC hardware address */ |
||
672 | memcpy(netif->hwaddr, &br->ethaddr, ETH_HWADDR_LEN); |
||
673 | |||
674 | /* maximum transfer unit */ |
||
675 | netif->mtu = 1500; |
||
676 | |||
677 | /* device capabilities */ |
||
678 | /* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */ |
||
679 | netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET | NETIF_FLAG_IGMP | NETIF_FLAG_MLD6 |NETIF_FLAG_LINK_UP; |
||
680 | |||
681 | #if LWIP_IPV6 && LWIP_IPV6_MLD |
||
682 | /* |
||
683 | * For hardware/netifs that implement MAC filtering. |
||
684 | * All-nodes link-local is handled by default, so we must let the hardware know |
||
685 | * to allow multicast packets in. |
||
686 | * Should set mld_mac_filter previously. */ |
||
687 | if (netif->mld_mac_filter != NULL) { |
||
688 | ip6_addr_t ip6_allnodes_ll; |
||
689 | ip6_addr_set_allnodes_linklocal(&ip6_allnodes_ll); |
||
690 | netif->mld_mac_filter(netif, &ip6_allnodes_ll, NETIF_ADD_MAC_FILTER); |
||
691 | } |
||
692 | #endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ |
||
693 | |||
694 | sys_timeout(BRIDGEIF_AGE_TIMER_MS, bridgeif_age_tmr, netif); |
||
695 | |||
696 | return ERR_OK; |
||
697 | } |
||
698 | |||
699 | /** |
||
700 | * @ingroup bridgeif |
||
701 | * Add a port to the bridge |
||
702 | */ |
||
703 | err_t |
||
704 | bridgeif_add_port(struct netif *bridgeif, struct netif *portif) |
||
705 | { |
||
706 | bridgeif_private_t *br; |
||
707 | bridgeif_port_t *port; |
||
708 | |||
709 | LWIP_ASSERT("bridgeif != NULL", bridgeif != NULL); |
||
710 | LWIP_ASSERT("bridgeif->state != NULL", bridgeif->state != NULL); |
||
711 | LWIP_ASSERT("portif != NULL", portif != NULL); |
||
712 | |||
713 | if (!(portif->flags & NETIF_FLAG_ETHARP) || !(portif->flags & NETIF_FLAG_ETHERNET)) { |
||
714 | /* can only add ETHERNET/ETHARP interfaces */ |
||
715 | return ERR_VAL; |
||
716 | } |
||
717 | |||
718 | br = (bridgeif_private_t *)bridgeif->state; |
||
719 | |||
720 | if (br->num_ports >= br->max_ports) { |
||
721 | return ERR_VAL; |
||
722 | } |
||
723 | port = &br->ports[br->num_ports]; |
||
724 | port->port_netif = portif; |
||
725 | port->port_num = br->num_ports; |
||
726 | port->bridge = br; |
||
727 | br->num_ports++; |
||
728 | |||
729 | /* let the port call us on input */ |
||
730 | #if BRIDGEIF_PORT_NETIFS_OUTPUT_DIRECT |
||
731 | portif->input = bridgeif_input; |
||
732 | #else |
||
733 | portif->input = bridgeif_tcpip_input; |
||
734 | #endif |
||
735 | /* store pointer to bridge in netif */ |
||
736 | netif_set_client_data(portif, bridgeif_netif_client_id, port); |
||
737 | /* remove ETHARP flag to prevent sending report events on netif-up */ |
||
738 | netif_clear_flags(portif, NETIF_FLAG_ETHARP); |
||
739 | |||
740 | return ERR_OK; |
||
741 | } |
||
742 | |||
743 | #endif /* LWIP_NUM_NETIF_CLIENT_DATA */ |