[457] | 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 those to better describe your network interface. */
|
---|
| 94 | #define IFNAME0 'b'
|
---|
| 95 | #define IFNAME1 'r'
|
---|
| 96 |
|
---|
| 97 | struct bridgeif_private_s;
|
---|
| 98 | typedef struct bridgeif_port_private_s {
|
---|
| 99 | struct bridgeif_private_s *bridge;
|
---|
| 100 | struct netif *port_netif;
|
---|
| 101 | u8_t port_num;
|
---|
| 102 | } bridgeif_port_t;
|
---|
| 103 |
|
---|
| 104 | typedef struct bridgeif_fdb_static_entry_s {
|
---|
| 105 | u8_t used;
|
---|
| 106 | bridgeif_portmask_t dst_ports;
|
---|
| 107 | struct eth_addr addr;
|
---|
| 108 | } bridgeif_fdb_static_entry_t;
|
---|
| 109 |
|
---|
| 110 | typedef struct bridgeif_private_s {
|
---|
| 111 | struct netif *netif;
|
---|
| 112 | struct eth_addr ethaddr;
|
---|
| 113 | u8_t max_ports;
|
---|
| 114 | u8_t num_ports;
|
---|
| 115 | bridgeif_port_t *ports;
|
---|
| 116 | u16_t max_fdbs_entries;
|
---|
| 117 | bridgeif_fdb_static_entry_t *fdbs;
|
---|
| 118 | u16_t max_fdbd_entries;
|
---|
| 119 | void *fdbd;
|
---|
| 120 | } bridgeif_private_t;
|
---|
| 121 |
|
---|
| 122 | /* netif data index to get the bridge on input */
|
---|
| 123 | u8_t bridgeif_netif_client_id = 0xff;
|
---|
| 124 |
|
---|
| 125 | /**
|
---|
| 126 | * @ingroup bridgeif
|
---|
| 127 | * Add a static entry to the forwarding database.
|
---|
| 128 | * A static entry marks where frames to a specific eth address (unicast or group address) are
|
---|
| 129 | * forwarded.
|
---|
| 130 | * bits [0..(BRIDGEIF_MAX_PORTS-1)]: hw ports
|
---|
| 131 | * bit [BRIDGEIF_MAX_PORTS]: cpu port
|
---|
| 132 | * 0: drop
|
---|
| 133 | */
|
---|
| 134 | err_t
|
---|
| 135 | bridgeif_fdb_add(struct netif *bridgeif, const struct eth_addr *addr, bridgeif_portmask_t ports)
|
---|
| 136 | {
|
---|
| 137 | int i;
|
---|
| 138 | bridgeif_private_t *br;
|
---|
| 139 | BRIDGEIF_DECL_PROTECT(lev);
|
---|
| 140 | LWIP_ASSERT("invalid netif", bridgeif != NULL);
|
---|
| 141 | br = (bridgeif_private_t *)bridgeif->state;
|
---|
| 142 | LWIP_ASSERT("invalid state", br != NULL);
|
---|
| 143 |
|
---|
| 144 | BRIDGEIF_READ_PROTECT(lev);
|
---|
| 145 | for (i = 0; i < br->max_fdbs_entries; i++) {
|
---|
| 146 | if (!br->fdbs[i].used) {
|
---|
| 147 | BRIDGEIF_WRITE_PROTECT(lev);
|
---|
| 148 | if (!br->fdbs[i].used) {
|
---|
| 149 | br->fdbs[i].used = 1;
|
---|
| 150 | br->fdbs[i].dst_ports = ports;
|
---|
| 151 | memcpy(&br->fdbs[i].addr, addr, sizeof(struct eth_addr));
|
---|
| 152 | BRIDGEIF_WRITE_UNPROTECT(lev);
|
---|
| 153 | BRIDGEIF_READ_UNPROTECT(lev);
|
---|
| 154 | return ERR_OK;
|
---|
| 155 | }
|
---|
| 156 | BRIDGEIF_WRITE_UNPROTECT(lev);
|
---|
| 157 | }
|
---|
| 158 | }
|
---|
| 159 | BRIDGEIF_READ_UNPROTECT(lev);
|
---|
| 160 | return ERR_MEM;
|
---|
| 161 | }
|
---|
| 162 |
|
---|
| 163 | /**
|
---|
| 164 | * @ingroup bridgeif
|
---|
| 165 | * Remove a static entry from the forwarding database
|
---|
| 166 | */
|
---|
| 167 | err_t
|
---|
| 168 | bridgeif_fdb_remove(struct netif *bridgeif, const struct eth_addr *addr)
|
---|
| 169 | {
|
---|
| 170 | int i;
|
---|
| 171 | bridgeif_private_t *br;
|
---|
| 172 | BRIDGEIF_DECL_PROTECT(lev);
|
---|
| 173 | LWIP_ASSERT("invalid netif", bridgeif != NULL);
|
---|
| 174 | br = (bridgeif_private_t *)bridgeif->state;
|
---|
| 175 | LWIP_ASSERT("invalid state", br != NULL);
|
---|
| 176 |
|
---|
| 177 | BRIDGEIF_READ_PROTECT(lev);
|
---|
| 178 | for (i = 0; i < br->max_fdbs_entries; i++) {
|
---|
| 179 | if (br->fdbs[i].used && !memcmp(&br->fdbs[i].addr, addr, sizeof(struct eth_addr))) {
|
---|
| 180 | BRIDGEIF_WRITE_PROTECT(lev);
|
---|
| 181 | if (br->fdbs[i].used && !memcmp(&br->fdbs[i].addr, addr, sizeof(struct eth_addr))) {
|
---|
| 182 | memset(&br->fdbs[i], 0, sizeof(bridgeif_fdb_static_entry_t));
|
---|
| 183 | BRIDGEIF_WRITE_UNPROTECT(lev);
|
---|
| 184 | BRIDGEIF_READ_UNPROTECT(lev);
|
---|
| 185 | return ERR_OK;
|
---|
| 186 | }
|
---|
| 187 | BRIDGEIF_WRITE_UNPROTECT(lev);
|
---|
| 188 | }
|
---|
| 189 | }
|
---|
| 190 | BRIDGEIF_READ_UNPROTECT(lev);
|
---|
| 191 | return ERR_VAL;
|
---|
| 192 | }
|
---|
| 193 |
|
---|
| 194 | /** Get the forwarding port(s) (as bit mask) for the specified destination mac address */
|
---|
| 195 | static bridgeif_portmask_t
|
---|
| 196 | bridgeif_find_dst_ports(bridgeif_private_t *br, struct eth_addr *dst_addr)
|
---|
| 197 | {
|
---|
| 198 | int i;
|
---|
| 199 | BRIDGEIF_DECL_PROTECT(lev);
|
---|
| 200 | BRIDGEIF_READ_PROTECT(lev);
|
---|
| 201 | /* first check for static entries */
|
---|
| 202 | for (i = 0; i < br->max_fdbs_entries; i++) {
|
---|
| 203 | if (br->fdbs[i].used) {
|
---|
| 204 | if (!memcmp(&br->fdbs[i].addr, dst_addr, sizeof(struct eth_addr))) {
|
---|
| 205 | bridgeif_portmask_t ret = br->fdbs[i].dst_ports;
|
---|
| 206 | BRIDGEIF_READ_UNPROTECT(lev);
|
---|
| 207 | return ret;
|
---|
| 208 | }
|
---|
| 209 | }
|
---|
| 210 | }
|
---|
| 211 | if (dst_addr->addr[0] & 1) {
|
---|
| 212 | /* no match found: flood remaining group address */
|
---|
| 213 | BRIDGEIF_READ_UNPROTECT(lev);
|
---|
| 214 | return BR_FLOOD;
|
---|
| 215 | }
|
---|
| 216 | BRIDGEIF_READ_UNPROTECT(lev);
|
---|
| 217 | /* no match found: check dynamic fdb for port or fall back to flooding */
|
---|
| 218 | return bridgeif_fdb_get_dst_ports(br->fdbd, dst_addr);
|
---|
| 219 | }
|
---|
| 220 |
|
---|
| 221 | /** Helper function to see if a destination mac belongs to the bridge
|
---|
| 222 | * (bridge netif or one of the port netifs), in which case the frame
|
---|
| 223 | * is sent to the cpu only.
|
---|
| 224 | */
|
---|
| 225 | static int
|
---|
| 226 | bridgeif_is_local_mac(bridgeif_private_t *br, struct eth_addr *addr)
|
---|
| 227 | {
|
---|
| 228 | int i;
|
---|
| 229 | BRIDGEIF_DECL_PROTECT(lev);
|
---|
| 230 | if (!memcmp(br->netif->hwaddr, addr, sizeof(struct eth_addr))) {
|
---|
| 231 | return 1;
|
---|
| 232 | }
|
---|
| 233 | BRIDGEIF_READ_PROTECT(lev);
|
---|
| 234 | for (i = 0; i < br->num_ports; i++) {
|
---|
| 235 | struct netif *portif = br->ports[i].port_netif;
|
---|
| 236 | if (portif != NULL) {
|
---|
| 237 | if (!memcmp(portif->hwaddr, addr, sizeof(struct eth_addr))) {
|
---|
| 238 | BRIDGEIF_READ_UNPROTECT(lev);
|
---|
| 239 | return 1;
|
---|
| 240 | }
|
---|
| 241 | }
|
---|
| 242 | }
|
---|
| 243 | BRIDGEIF_READ_UNPROTECT(lev);
|
---|
| 244 | return 0;
|
---|
| 245 | }
|
---|
| 246 |
|
---|
| 247 | /* Output helper function */
|
---|
| 248 | static err_t
|
---|
| 249 | bridgeif_send_to_port(bridgeif_private_t *br, struct pbuf *p, u8_t dstport_idx)
|
---|
| 250 | {
|
---|
| 251 | if (dstport_idx < BRIDGEIF_MAX_PORTS) {
|
---|
| 252 | /* possibly an external port */
|
---|
| 253 | if (dstport_idx < br->max_ports) {
|
---|
| 254 | struct netif *portif = br->ports[dstport_idx].port_netif;
|
---|
| 255 | if ((portif != NULL) && (portif->linkoutput != NULL)) {
|
---|
| 256 | /* prevent sending out to rx port */
|
---|
| 257 | if (netif_get_index(portif) != p->if_idx) {
|
---|
| 258 | if (netif_is_link_up(portif)) {
|
---|
| 259 | LWIP_DEBUGF(BRIDGEIF_FW_DEBUG, ("br -> flood(%p:%d) -> %d\n", (void *)p, p->if_idx, netif_get_index(portif)));
|
---|
| 260 | return portif->linkoutput(portif, p);
|
---|
| 261 | }
|
---|
| 262 | }
|
---|
| 263 | }
|
---|
| 264 | }
|
---|
| 265 | } else {
|
---|
| 266 | LWIP_ASSERT("invalid port index", dstport_idx == BRIDGEIF_MAX_PORTS);
|
---|
| 267 | }
|
---|
| 268 | return ERR_OK;
|
---|
| 269 | }
|
---|
| 270 |
|
---|
| 271 | /** Helper function to pass a pbuf to all ports marked in 'dstports'
|
---|
| 272 | */
|
---|
| 273 | static err_t
|
---|
| 274 | bridgeif_send_to_ports(bridgeif_private_t *br, struct pbuf *p, bridgeif_portmask_t dstports)
|
---|
| 275 | {
|
---|
| 276 | err_t err, ret_err = ERR_OK;
|
---|
| 277 | u8_t i;
|
---|
| 278 | bridgeif_portmask_t mask = 1;
|
---|
| 279 | BRIDGEIF_DECL_PROTECT(lev);
|
---|
| 280 | BRIDGEIF_READ_PROTECT(lev);
|
---|
| 281 | for (i = 0; i < BRIDGEIF_MAX_PORTS; i++, mask = (bridgeif_portmask_t)(mask << 1)) {
|
---|
| 282 | if (dstports & mask) {
|
---|
| 283 | err = bridgeif_send_to_port(br, p, i);
|
---|
| 284 | if (err != ERR_OK) {
|
---|
| 285 | ret_err = err;
|
---|
| 286 | }
|
---|
| 287 | }
|
---|
| 288 | }
|
---|
| 289 | BRIDGEIF_READ_UNPROTECT(lev);
|
---|
| 290 | return ret_err;
|
---|
| 291 | }
|
---|
| 292 |
|
---|
| 293 | /** Output function of the application port of the bridge (the one with an ip address).
|
---|
| 294 | * The forwarding port(s) where this pbuf is sent on is/are automatically selected
|
---|
| 295 | * from the FDB.
|
---|
| 296 | */
|
---|
| 297 | static err_t
|
---|
| 298 | bridgeif_output(struct netif *netif, struct pbuf *p)
|
---|
| 299 | {
|
---|
| 300 | err_t err;
|
---|
| 301 | bridgeif_private_t *br = (bridgeif_private_t *)netif->state;
|
---|
| 302 | struct eth_addr *dst = (struct eth_addr *)(p->payload);
|
---|
| 303 |
|
---|
| 304 | bridgeif_portmask_t dstports = bridgeif_find_dst_ports(br, dst);
|
---|
| 305 | err = bridgeif_send_to_ports(br, p, dstports);
|
---|
| 306 |
|
---|
| 307 | MIB2_STATS_NETIF_ADD(netif, ifoutoctets, p->tot_len);
|
---|
| 308 | if (((u8_t *)p->payload)[0] & 1) {
|
---|
| 309 | /* broadcast or multicast packet*/
|
---|
| 310 | MIB2_STATS_NETIF_INC(netif, ifoutnucastpkts);
|
---|
| 311 | } else {
|
---|
| 312 | /* unicast packet */
|
---|
| 313 | MIB2_STATS_NETIF_INC(netif, ifoutucastpkts);
|
---|
| 314 | }
|
---|
| 315 | /* increase ifoutdiscards or ifouterrors on error */
|
---|
| 316 |
|
---|
| 317 | LINK_STATS_INC(link.xmit);
|
---|
| 318 |
|
---|
| 319 | return err;
|
---|
| 320 | }
|
---|
| 321 |
|
---|
| 322 | /** The actual bridge input function. Port netif's input is changed to call
|
---|
| 323 | * here. This function decides where the frame is forwarded.
|
---|
| 324 | */
|
---|
| 325 | static err_t
|
---|
| 326 | bridgeif_input(struct pbuf *p, struct netif *netif)
|
---|
| 327 | {
|
---|
| 328 | u8_t rx_idx;
|
---|
| 329 | bridgeif_portmask_t dstports;
|
---|
| 330 | struct eth_addr *src, *dst;
|
---|
| 331 | bridgeif_private_t *br;
|
---|
| 332 | bridgeif_port_t *port;
|
---|
| 333 | if (p == NULL || netif == NULL) {
|
---|
| 334 | return ERR_VAL;
|
---|
| 335 | }
|
---|
| 336 | port = (bridgeif_port_t *)netif_get_client_data(netif, bridgeif_netif_client_id);
|
---|
| 337 | LWIP_ASSERT("port data not set", port != NULL);
|
---|
| 338 | if (port == NULL || port->bridge == NULL) {
|
---|
| 339 | return ERR_VAL;
|
---|
| 340 | }
|
---|
| 341 | br = (bridgeif_private_t *)port->bridge;
|
---|
| 342 | rx_idx = netif_get_index(netif);
|
---|
| 343 | /* store receive index in pbuf */
|
---|
| 344 | p->if_idx = rx_idx;
|
---|
| 345 |
|
---|
| 346 | dst = (struct eth_addr *)p->payload;
|
---|
| 347 | src = (struct eth_addr *)(((u8_t *)p->payload) + sizeof(struct eth_addr));
|
---|
| 348 |
|
---|
| 349 | if ((src->addr[0] & 1) == 0) {
|
---|
| 350 | /* update src for all non-group addresses */
|
---|
| 351 | bridgeif_fdb_update_src(br->fdbd, src, port->port_num);
|
---|
| 352 | }
|
---|
| 353 |
|
---|
| 354 | if (dst->addr[0] & 1) {
|
---|
| 355 | /* group address -> flood + cpu? */
|
---|
| 356 | dstports = bridgeif_find_dst_ports(br, dst);
|
---|
| 357 | bridgeif_send_to_ports(br, p, dstports);
|
---|
| 358 | if (dstports & (1 << BRIDGEIF_MAX_PORTS)) {
|
---|
| 359 | /* we pass the reference to ->input or have to free it */
|
---|
| 360 | LWIP_DEBUGF(BRIDGEIF_FW_DEBUG, ("br -> input(%p)\n", (void *)p));
|
---|
| 361 | if (br->netif->input(p, br->netif) != ERR_OK) {
|
---|
| 362 | pbuf_free(p);
|
---|
| 363 | }
|
---|
| 364 | } else {
|
---|
| 365 | /* all references done */
|
---|
| 366 | pbuf_free(p);
|
---|
| 367 | }
|
---|
| 368 | /* always return ERR_OK here to prevent the caller freeing the pbuf */
|
---|
| 369 | return ERR_OK;
|
---|
| 370 | } else {
|
---|
| 371 | /* is this for one of the local ports? */
|
---|
| 372 | if (bridgeif_is_local_mac(br, dst)) {
|
---|
| 373 | /* yes, send to cpu port only */
|
---|
| 374 | LWIP_DEBUGF(BRIDGEIF_FW_DEBUG, ("br -> input(%p)\n", (void *)p));
|
---|
| 375 | return br->netif->input(p, br->netif);
|
---|
| 376 | }
|
---|
| 377 |
|
---|
| 378 | /* get dst port */
|
---|
| 379 | dstports = bridgeif_find_dst_ports(br, dst);
|
---|
| 380 | bridgeif_send_to_ports(br, p, dstports);
|
---|
| 381 | /* no need to send to cpu, flooding is for external ports only */
|
---|
| 382 | /* by this, we consumed the pbuf */
|
---|
| 383 | pbuf_free(p);
|
---|
| 384 | /* always return ERR_OK here to prevent the caller freeing the pbuf */
|
---|
| 385 | return ERR_OK;
|
---|
| 386 | }
|
---|
| 387 | }
|
---|
| 388 |
|
---|
| 389 | #if !BRIDGEIF_PORT_NETIFS_OUTPUT_DIRECT
|
---|
| 390 | /** Input function for port netifs used to synchronize into tcpip_thread.
|
---|
| 391 | */
|
---|
| 392 | static err_t
|
---|
| 393 | bridgeif_tcpip_input(struct pbuf *p, struct netif *netif)
|
---|
| 394 | {
|
---|
| 395 | return tcpip_inpkt(p, netif, bridgeif_input);
|
---|
| 396 | }
|
---|
| 397 | #endif /* BRIDGEIF_PORT_NETIFS_OUTPUT_DIRECT */
|
---|
| 398 |
|
---|
| 399 | /**
|
---|
| 400 | * @ingroup bridgeif
|
---|
| 401 | * Initialization function passed to netif_add().
|
---|
| 402 | *
|
---|
| 403 | * ATTENTION: A pointer to a @ref bridgeif_initdata_t must be passed as 'state'
|
---|
| 404 | * to @ref netif_add when adding the bridge. I supplies MAC address
|
---|
| 405 | * and controls memory allocation (number of ports, FDB size).
|
---|
| 406 | *
|
---|
| 407 | * @param netif the lwip network interface structure for this ethernetif
|
---|
| 408 | * @return ERR_OK if the loopif is initialized
|
---|
| 409 | * ERR_MEM if private data couldn't be allocated
|
---|
| 410 | * any other err_t on error
|
---|
| 411 | */
|
---|
| 412 | err_t
|
---|
| 413 | bridgeif_init(struct netif *netif)
|
---|
| 414 | {
|
---|
| 415 | bridgeif_initdata_t *init_data;
|
---|
| 416 | bridgeif_private_t *br;
|
---|
| 417 | size_t alloc_len_sizet;
|
---|
| 418 | mem_size_t alloc_len;
|
---|
| 419 |
|
---|
| 420 | LWIP_ASSERT("netif != NULL", (netif != NULL));
|
---|
| 421 | LWIP_ASSERT("bridgeif needs an input callback", (netif->input != NULL));
|
---|
| 422 | #if !BRIDGEIF_PORT_NETIFS_OUTPUT_DIRECT
|
---|
| 423 | if (netif->input == tcpip_input) {
|
---|
| 424 | LWIP_DEBUGF(BRIDGEIF_DEBUG | LWIP_DBG_ON, ("bridgeif does not need tcpip_input, use netif_input/ethernet_input instead"));
|
---|
| 425 | }
|
---|
| 426 | #endif
|
---|
| 427 |
|
---|
| 428 | if (bridgeif_netif_client_id == 0xFF) {
|
---|
| 429 | bridgeif_netif_client_id = netif_alloc_client_data_id();
|
---|
| 430 | }
|
---|
| 431 |
|
---|
| 432 | init_data = (bridgeif_initdata_t *)netif->state;
|
---|
| 433 | LWIP_ASSERT("init_data != NULL", (init_data != NULL));
|
---|
| 434 | LWIP_ASSERT("init_data->max_ports <= BRIDGEIF_MAX_PORTS",
|
---|
| 435 | init_data->max_ports <= BRIDGEIF_MAX_PORTS);
|
---|
| 436 |
|
---|
| 437 | 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)));
|
---|
| 438 | alloc_len = (mem_size_t)alloc_len_sizet;
|
---|
| 439 | LWIP_ASSERT("alloc_len == alloc_len_sizet", alloc_len == alloc_len_sizet);
|
---|
| 440 | LWIP_DEBUGF(BRIDGEIF_DEBUG, ("bridgeif_init: allocating %d bytes for private data\n", (int)alloc_len));
|
---|
| 441 | br = (bridgeif_private_t *)mem_calloc(1, alloc_len);
|
---|
| 442 | if (br == NULL) {
|
---|
| 443 | LWIP_DEBUGF(NETIF_DEBUG, ("bridgeif_init: out of memory\n"));
|
---|
| 444 | return ERR_MEM;
|
---|
| 445 | }
|
---|
| 446 | memcpy(&br->ethaddr, &init_data->ethaddr, sizeof(br->ethaddr));
|
---|
| 447 | br->netif = netif;
|
---|
| 448 |
|
---|
| 449 | br->max_ports = init_data->max_ports;
|
---|
| 450 | br->ports = (bridgeif_port_t *)(br + 1);
|
---|
| 451 |
|
---|
| 452 | br->max_fdbs_entries = init_data->max_fdb_static_entries;
|
---|
| 453 | br->fdbs = (bridgeif_fdb_static_entry_t *)(((u8_t *)(br + 1)) + (init_data->max_ports * sizeof(bridgeif_port_t)));
|
---|
| 454 |
|
---|
| 455 | br->max_fdbd_entries = init_data->max_fdb_dynamic_entries;
|
---|
| 456 | br->fdbd = bridgeif_fdb_init(init_data->max_fdb_dynamic_entries);
|
---|
| 457 | if (br->fdbd == NULL) {
|
---|
| 458 | LWIP_DEBUGF(NETIF_DEBUG, ("bridgeif_init: out of memory in fdb_init\n"));
|
---|
| 459 | mem_free(br);
|
---|
| 460 | return ERR_MEM;
|
---|
| 461 | }
|
---|
| 462 |
|
---|
| 463 | #if LWIP_NETIF_HOSTNAME
|
---|
| 464 | /* Initialize interface hostname */
|
---|
| 465 | netif->hostname = "lwip";
|
---|
| 466 | #endif /* LWIP_NETIF_HOSTNAME */
|
---|
| 467 |
|
---|
| 468 | /*
|
---|
| 469 | * Initialize the snmp variables and counters inside the struct netif.
|
---|
| 470 | * The last argument should be replaced with your link speed, in units
|
---|
| 471 | * of bits per second.
|
---|
| 472 | */
|
---|
| 473 | MIB2_INIT_NETIF(netif, snmp_ifType_ethernet_csmacd, 0);
|
---|
| 474 |
|
---|
| 475 | netif->state = br;
|
---|
| 476 | netif->name[0] = IFNAME0;
|
---|
| 477 | netif->name[1] = IFNAME1;
|
---|
| 478 | /* We directly use etharp_output() here to save a function call.
|
---|
| 479 | * You can instead declare your own function an call etharp_output()
|
---|
| 480 | * from it if you have to do some checks before sending (e.g. if link
|
---|
| 481 | * is available...) */
|
---|
| 482 | #if LWIP_IPV4
|
---|
| 483 | netif->output = etharp_output;
|
---|
| 484 | #endif /* LWIP_IPV4 */
|
---|
| 485 | #if LWIP_IPV6
|
---|
| 486 | netif->output_ip6 = ethip6_output;
|
---|
| 487 | #endif /* LWIP_IPV6 */
|
---|
| 488 | netif->linkoutput = bridgeif_output;
|
---|
| 489 |
|
---|
| 490 | /* set MAC hardware address length */
|
---|
| 491 | netif->hwaddr_len = ETH_HWADDR_LEN;
|
---|
| 492 |
|
---|
| 493 | /* set MAC hardware address */
|
---|
| 494 | memcpy(netif->hwaddr, &br->ethaddr, ETH_HWADDR_LEN);
|
---|
| 495 |
|
---|
| 496 | /* maximum transfer unit */
|
---|
| 497 | netif->mtu = 1500;
|
---|
| 498 |
|
---|
| 499 | /* device capabilities */
|
---|
| 500 | /* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */
|
---|
| 501 | netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET | NETIF_FLAG_IGMP | NETIF_FLAG_MLD6 | NETIF_FLAG_LINK_UP;
|
---|
| 502 |
|
---|
| 503 | #if LWIP_IPV6 && LWIP_IPV6_MLD
|
---|
| 504 | /*
|
---|
| 505 | * For hardware/netifs that implement MAC filtering.
|
---|
| 506 | * All-nodes link-local is handled by default, so we must let the hardware know
|
---|
| 507 | * to allow multicast packets in.
|
---|
| 508 | * Should set mld_mac_filter previously. */
|
---|
| 509 | if (netif->mld_mac_filter != NULL) {
|
---|
| 510 | ip6_addr_t ip6_allnodes_ll;
|
---|
| 511 | ip6_addr_set_allnodes_linklocal(&ip6_allnodes_ll);
|
---|
| 512 | netif->mld_mac_filter(netif, &ip6_allnodes_ll, NETIF_ADD_MAC_FILTER);
|
---|
| 513 | }
|
---|
| 514 | #endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
|
---|
| 515 |
|
---|
| 516 | return ERR_OK;
|
---|
| 517 | }
|
---|
| 518 |
|
---|
| 519 | /**
|
---|
| 520 | * @ingroup bridgeif
|
---|
| 521 | * Add a port to the bridge
|
---|
| 522 | */
|
---|
| 523 | err_t
|
---|
| 524 | bridgeif_add_port(struct netif *bridgeif, struct netif *portif)
|
---|
| 525 | {
|
---|
| 526 | bridgeif_private_t *br;
|
---|
| 527 | bridgeif_port_t *port;
|
---|
| 528 |
|
---|
| 529 | LWIP_ASSERT("bridgeif != NULL", bridgeif != NULL);
|
---|
| 530 | LWIP_ASSERT("bridgeif->state != NULL", bridgeif->state != NULL);
|
---|
| 531 | LWIP_ASSERT("portif != NULL", portif != NULL);
|
---|
| 532 |
|
---|
| 533 | if (!(portif->flags & NETIF_FLAG_ETHARP) || !(portif->flags & NETIF_FLAG_ETHERNET)) {
|
---|
| 534 | /* can only add ETHERNET/ETHARP interfaces */
|
---|
| 535 | return ERR_VAL;
|
---|
| 536 | }
|
---|
| 537 |
|
---|
| 538 | br = (bridgeif_private_t *)bridgeif->state;
|
---|
| 539 |
|
---|
| 540 | if (br->num_ports >= br->max_ports) {
|
---|
| 541 | return ERR_VAL;
|
---|
| 542 | }
|
---|
| 543 | port = &br->ports[br->num_ports];
|
---|
| 544 | port->port_netif = portif;
|
---|
| 545 | port->port_num = br->num_ports;
|
---|
| 546 | port->bridge = br;
|
---|
| 547 | br->num_ports++;
|
---|
| 548 |
|
---|
| 549 | /* let the port call us on input */
|
---|
| 550 | #if BRIDGEIF_PORT_NETIFS_OUTPUT_DIRECT
|
---|
| 551 | portif->input = bridgeif_input;
|
---|
| 552 | #else
|
---|
| 553 | portif->input = bridgeif_tcpip_input;
|
---|
| 554 | #endif
|
---|
| 555 | /* store pointer to bridge in netif */
|
---|
| 556 | netif_set_client_data(portif, bridgeif_netif_client_id, port);
|
---|
| 557 | /* remove ETHARP flag to prevent sending report events on netif-up */
|
---|
| 558 | netif_clear_flags(portif, NETIF_FLAG_ETHARP);
|
---|
| 559 |
|
---|
| 560 | return ERR_OK;
|
---|
| 561 | }
|
---|
| 562 |
|
---|
| 563 | #endif /* LWIP_NUM_NETIF_CLIENT_DATA */
|
---|