| /** |
| * @file |
| * lwIP netif implementing an FDB for IEEE 802.1D MAC Bridge |
| */ |
| |
| /* |
| * Copyright (c) 2017 Simon Goldschmidt. |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without modification, |
| * are permitted provided that the following conditions are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright notice, |
| * this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright notice, |
| * this list of conditions and the following disclaimer in the documentation |
| * and/or other materials provided with the distribution. |
| * 3. The name of the author may not be used to endorse or promote products |
| * derived from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED |
| * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
| * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT |
| * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT |
| * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING |
| * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY |
| * OF SUCH DAMAGE. |
| * |
| * This file is part of the lwIP TCP/IP stack. |
| * |
| * Author: Simon Goldschmidt <goldsimon@gmx.de> |
| * |
| */ |
| |
| /** |
| * @defgroup bridgeif_fdb FDB example code |
| * @ingroup bridgeif |
| * This file implements an example for an FDB (Forwarding DataBase) |
| */ |
| |
| #include "netif/bridgeif.h" |
| #include "lwip/sys.h" |
| #include "lwip/mem.h" |
| #include "lwip/timeouts.h" |
| #include <string.h> |
| |
| #define BRIDGEIF_AGE_TIMER_MS 1000 |
| |
| #define BR_FDB_TIMEOUT_SEC (60*5) /* 5 minutes FDB timeout */ |
| |
| typedef struct bridgeif_dfdb_entry_s { |
| u8_t used; |
| u8_t port; |
| u32_t ts; |
| struct eth_addr addr; |
| } bridgeif_dfdb_entry_t; |
| |
| typedef struct bridgeif_dfdb_s { |
| u16_t max_fdb_entries; |
| bridgeif_dfdb_entry_t *fdb; |
| } bridgeif_dfdb_t; |
| |
| /** |
| * @ingroup bridgeif_fdb |
| * A real simple and slow implementation of an auto-learning forwarding database that |
| * remembers known src mac addresses to know which port to send frames destined for that |
| * mac address. |
| * |
| * ATTENTION: This is meant as an example only, in real-world use, you should |
| * provide a better implementation :-) |
| */ |
| void |
| bridgeif_fdb_update_src(void *fdb_ptr, struct eth_addr *src_addr, u8_t port_idx) |
| { |
| int i; |
| bridgeif_dfdb_t *fdb = (bridgeif_dfdb_t *)fdb_ptr; |
| BRIDGEIF_DECL_PROTECT(lev); |
| BRIDGEIF_READ_PROTECT(lev); |
| for (i = 0; i < fdb->max_fdb_entries; i++) { |
| bridgeif_dfdb_entry_t *e = &fdb->fdb[i]; |
| if (e->used && e->ts) { |
| if (!memcmp(&e->addr, src_addr, sizeof(struct eth_addr))) { |
| LWIP_DEBUGF(BRIDGEIF_FDB_DEBUG, ("br: update src %02x:%02x:%02x:%02x:%02x:%02x (from %d) @ idx %d\n", |
| src_addr->addr[0], src_addr->addr[1], src_addr->addr[2], src_addr->addr[3], src_addr->addr[4], src_addr->addr[5], |
| port_idx, i)); |
| BRIDGEIF_WRITE_PROTECT(lev); |
| e->ts = BR_FDB_TIMEOUT_SEC; |
| e->port = port_idx; |
| BRIDGEIF_WRITE_UNPROTECT(lev); |
| BRIDGEIF_READ_UNPROTECT(lev); |
| return; |
| } |
| } |
| } |
| /* not found, allocate new entry from free */ |
| for (i = 0; i < fdb->max_fdb_entries; i++) { |
| bridgeif_dfdb_entry_t *e = &fdb->fdb[i]; |
| if (!e->used || !e->ts) { |
| BRIDGEIF_WRITE_PROTECT(lev); |
| /* check again when protected */ |
| if (!e->used || !e->ts) { |
| LWIP_DEBUGF(BRIDGEIF_FDB_DEBUG, ("br: create src %02x:%02x:%02x:%02x:%02x:%02x (from %d) @ idx %d\n", |
| src_addr->addr[0], src_addr->addr[1], src_addr->addr[2], src_addr->addr[3], src_addr->addr[4], src_addr->addr[5], |
| port_idx, i)); |
| memcpy(&e->addr, src_addr, sizeof(struct eth_addr)); |
| e->ts = BR_FDB_TIMEOUT_SEC; |
| e->port = port_idx; |
| e->used = 1; |
| BRIDGEIF_WRITE_UNPROTECT(lev); |
| BRIDGEIF_READ_UNPROTECT(lev); |
| return; |
| } |
| BRIDGEIF_WRITE_UNPROTECT(lev); |
| } |
| } |
| BRIDGEIF_READ_UNPROTECT(lev); |
| /* not found, no free entry -> flood */ |
| } |
| |
| /** |
| * @ingroup bridgeif_fdb |
| * Walk our list of auto-learnt fdb entries and return a port to forward or BR_FLOOD if unknown |
| */ |
| bridgeif_portmask_t |
| bridgeif_fdb_get_dst_ports(void *fdb_ptr, struct eth_addr *dst_addr) |
| { |
| int i; |
| bridgeif_dfdb_t *fdb = (bridgeif_dfdb_t *)fdb_ptr; |
| BRIDGEIF_DECL_PROTECT(lev); |
| BRIDGEIF_READ_PROTECT(lev); |
| for (i = 0; i < fdb->max_fdb_entries; i++) { |
| bridgeif_dfdb_entry_t *e = &fdb->fdb[i]; |
| if (e->used && e->ts) { |
| if (!memcmp(&e->addr, dst_addr, sizeof(struct eth_addr))) { |
| bridgeif_portmask_t ret = (bridgeif_portmask_t)(1 << e->port); |
| BRIDGEIF_READ_UNPROTECT(lev); |
| return ret; |
| } |
| } |
| } |
| BRIDGEIF_READ_UNPROTECT(lev); |
| return BR_FLOOD; |
| } |
| |
| /** |
| * @ingroup bridgeif_fdb |
| * Aging implementation of our simple fdb |
| */ |
| static void |
| bridgeif_fdb_age_one_second(void *fdb_ptr) |
| { |
| int i; |
| bridgeif_dfdb_t *fdb; |
| BRIDGEIF_DECL_PROTECT(lev); |
| |
| fdb = (bridgeif_dfdb_t *)fdb_ptr; |
| BRIDGEIF_READ_PROTECT(lev); |
| |
| for (i = 0; i < fdb->max_fdb_entries; i++) { |
| bridgeif_dfdb_entry_t *e = &fdb->fdb[i]; |
| if (e->used && e->ts) { |
| BRIDGEIF_WRITE_PROTECT(lev); |
| /* check again when protected */ |
| if (e->used && e->ts) { |
| if (--e->ts == 0) { |
| e->used = 0; |
| } |
| } |
| BRIDGEIF_WRITE_UNPROTECT(lev); |
| } |
| } |
| BRIDGEIF_READ_UNPROTECT(lev); |
| } |
| |
| /** Timer callback for fdb aging, called once per second */ |
| static void |
| bridgeif_age_tmr(void *arg) |
| { |
| bridgeif_dfdb_t *fdb = (bridgeif_dfdb_t *)arg; |
| |
| LWIP_ASSERT("invalid arg", arg != NULL); |
| |
| bridgeif_fdb_age_one_second(fdb); |
| sys_timeout(BRIDGEIF_AGE_TIMER_MS, bridgeif_age_tmr, arg); |
| } |
| |
| /** |
| * @ingroup bridgeif_fdb |
| * Init our simple fdb list |
| */ |
| void * |
| bridgeif_fdb_init(u16_t max_fdb_entries) |
| { |
| bridgeif_dfdb_t *fdb; |
| size_t alloc_len_sizet = sizeof(bridgeif_dfdb_t) + (max_fdb_entries * sizeof(bridgeif_dfdb_entry_t)); |
| mem_size_t alloc_len = (mem_size_t)alloc_len_sizet; |
| LWIP_ASSERT("alloc_len == alloc_len_sizet", alloc_len == alloc_len_sizet); |
| LWIP_DEBUGF(BRIDGEIF_DEBUG, ("bridgeif_fdb_init: allocating %d bytes for private FDB data\n", (int)alloc_len)); |
| fdb = (bridgeif_dfdb_t *)mem_calloc(1, alloc_len); |
| if (fdb == NULL) { |
| return NULL; |
| } |
| fdb->max_fdb_entries = max_fdb_entries; |
| fdb->fdb = (bridgeif_dfdb_entry_t *)(fdb + 1); |
| |
| sys_timeout(BRIDGEIF_AGE_TIMER_MS, bridgeif_age_tmr, fdb); |
| |
| return fdb; |
| } |