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 <[email protected]> 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 */ 564