net: ethernet: bridging support
This adds the ability to create Ethernet bridges for connecting separate Ethernet segments together to appear as a single Ethernet network. This mimics the Linux functionality of the same name. Signed-off-by: Nicolas Pitre <npitre@baylibre.com>
This commit is contained in:
parent
d3f6b54f8e
commit
89482f0119
11 changed files with 519 additions and 3 deletions
|
@ -5,7 +5,8 @@
|
|||
#define NETWORK_RAM_SECTIONS \
|
||||
Z_ITERABLE_SECTION_RAM(net_if, 4) \
|
||||
Z_ITERABLE_SECTION_RAM(net_if_dev, 4) \
|
||||
Z_ITERABLE_SECTION_RAM(net_l2, 4)
|
||||
Z_ITERABLE_SECTION_RAM(net_l2, 4) \
|
||||
Z_ITERABLE_SECTION_RAM(eth_bridge, 4)
|
||||
#endif
|
||||
#endif /* NETWORKING */
|
||||
|
||||
|
|
|
@ -34,6 +34,10 @@
|
|||
#include <net/dsa.h>
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_NET_ETHERNET_BRIDGE)
|
||||
#include <net/ethernet_bridge.h>
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
@ -521,6 +525,10 @@ struct ethernet_context {
|
|||
ATOMIC_DEFINE(interfaces, NET_VLAN_MAX_COUNT);
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_NET_ETHERNET_BRIDGE)
|
||||
struct eth_bridge_iface_context bridge;
|
||||
#endif
|
||||
|
||||
/** Carrier ON/OFF handler worker. This is used to create
|
||||
* network interface UP/DOWN event when ethernet L2 driver
|
||||
* notices carrier ON/OFF situation. We must not create another
|
||||
|
|
189
include/net/ethernet_bridge.h
Normal file
189
include/net/ethernet_bridge.h
Normal file
|
@ -0,0 +1,189 @@
|
|||
/** @file
|
||||
* @brief Ethernet Bridge public header file
|
||||
*
|
||||
* Ethernet Bridges connect two or more Ethernet networks together and
|
||||
* transparently forward packets from one network to the others as if
|
||||
* they were part of the same network.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2021 BayLibre SAS
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_INCLUDE_NET_ETHERNET_BRIDGE_H_
|
||||
#define ZEPHYR_INCLUDE_NET_ETHERNET_BRIDGE_H_
|
||||
|
||||
#include <sys/slist.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Ethernet Bridging API
|
||||
* @defgroup eth_bridge Ethernet Bridging API
|
||||
* @ingroup networking
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** @cond INTERNAL_HIDDEN */
|
||||
|
||||
struct eth_bridge {
|
||||
struct k_mutex lock;
|
||||
sys_slist_t interfaces;
|
||||
sys_slist_t listeners;
|
||||
};
|
||||
|
||||
#define ETH_BRIDGE_INITIALIZER(obj) \
|
||||
{ \
|
||||
.lock = { }, \
|
||||
.interfaces = SYS_SLIST_STATIC_INIT(&obj.interfaces), \
|
||||
.listeners = SYS_SLIST_STATIC_INIT(&obj.listeners), \
|
||||
}
|
||||
|
||||
/** @endcond */
|
||||
|
||||
/**
|
||||
* @brief Statically define and initialize a bridge instance.
|
||||
*
|
||||
* @param name Name of the bridge object
|
||||
*/
|
||||
#define ETH_BRIDGE_INIT(name) \
|
||||
Z_STRUCT_SECTION_ITERABLE(eth_bridge, name) = \
|
||||
ETH_BRIDGE_INITIALIZER(name)
|
||||
|
||||
struct eth_bridge_iface_context {
|
||||
sys_snode_t node;
|
||||
struct eth_bridge *instance;
|
||||
bool allow_tx;
|
||||
};
|
||||
|
||||
struct eth_bridge_listener {
|
||||
sys_snode_t node;
|
||||
struct k_fifo pkt_queue;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Add an Ethernet network interface to a bridge
|
||||
*
|
||||
* This adds a network interface to a bridge. The interface is then put
|
||||
* into promiscuous mode, all packets received by this interface are sent
|
||||
* to the bridge, and any other packets sent to the bridge (with some
|
||||
* exceptions) are transmitted via this interface.
|
||||
*
|
||||
* For transmission from the bridge to occur via this interface, it is
|
||||
* necessary to enable TX mode with eth_bridge_iface_tx(). TX mode is
|
||||
* initially disabled.
|
||||
*
|
||||
* Once an interface is added to a bridge, all its incoming traffic is
|
||||
* diverted to the bridge. However, packets sent out with net_if_queue_tx()
|
||||
* via this interface are not subjected to the bridge.
|
||||
*
|
||||
* @param br A pointer to an initialized bridge object
|
||||
* @param iface Interface to add
|
||||
*
|
||||
* @return 0 if OK, negative error code otherwise.
|
||||
*/
|
||||
int eth_bridge_iface_add(struct eth_bridge *br, struct net_if *iface);
|
||||
|
||||
/**
|
||||
* @brief Remove an Ethernet network interface from a bridge
|
||||
*
|
||||
* @param br A pointer to an initialized bridge object
|
||||
* @param iface Interface to remove
|
||||
*
|
||||
* @return 0 if OK, negative error code otherwise.
|
||||
*/
|
||||
int eth_bridge_iface_remove(struct eth_bridge *br, struct net_if *iface);
|
||||
|
||||
/**
|
||||
* @brief Enable/disable transmission mode for a bridged interface
|
||||
*
|
||||
* When TX mode is off, the interface may receive packets and send them to
|
||||
* the bridge but no packets coming from the bridge will be sent through this
|
||||
* interface. When TX mode is on, both incoming and outgoing packets are
|
||||
* allowed.
|
||||
*
|
||||
* @param iface Interface to configure
|
||||
* @param allow true to activate TX mode, false otherwise
|
||||
*
|
||||
* @return 0 if OK, negative error code otherwise.
|
||||
*/
|
||||
int eth_bridge_iface_allow_tx(struct net_if *iface, bool allow);
|
||||
|
||||
/**
|
||||
* @brief Add (register) a listener to the bridge
|
||||
*
|
||||
* This lets a software listener register a pointer to a provided FIFO for
|
||||
* receiving packets sent to the bridge. The listener is responsible for
|
||||
* emptying the FIFO with k_fifo_get() which will return a struct net_pkt
|
||||
* pointer, and releasing the packet with net_pkt_unref() when done with it.
|
||||
*
|
||||
* The listener wishing not to receive any more packets should simply
|
||||
* unregister itself with eth_bridge_listener_remove().
|
||||
*
|
||||
* @param br A pointer to an initialized bridge object
|
||||
* @param l A pointer to an initialized listener instance.
|
||||
*
|
||||
* @return 0 if OK, negative error code otherwise.
|
||||
*/
|
||||
int eth_bridge_listener_add(struct eth_bridge *br, struct eth_bridge_listener *l);
|
||||
|
||||
/**
|
||||
* @brief Remove (unregister) a listener from the bridge
|
||||
*
|
||||
* @param br A pointer to an initialized bridge object
|
||||
* @param l A pointer to the listener instance to be removed.
|
||||
*
|
||||
* @return 0 if OK, negative error code otherwise.
|
||||
*/
|
||||
int eth_bridge_listener_remove(struct eth_bridge *br, struct eth_bridge_listener *l);
|
||||
|
||||
/**
|
||||
* @brief Get bridge index according to pointer
|
||||
*
|
||||
* @param br Pointer to bridge instance
|
||||
*
|
||||
* @return Bridge index
|
||||
*/
|
||||
int eth_bridge_get_index(struct eth_bridge *br);
|
||||
|
||||
/**
|
||||
* @brief Get bridge instance according to index
|
||||
*
|
||||
* @param index Bridge instance index
|
||||
*
|
||||
* @return Pointer to bridge instance or NULL if not found.
|
||||
*/
|
||||
struct eth_bridge *eth_bridge_get_by_index(int index);
|
||||
|
||||
/**
|
||||
* @typedef eth_bridge_cb_t
|
||||
* @brief Callback used while iterating over bridge instances
|
||||
*
|
||||
* @param br Pointer to bridge instance
|
||||
* @param user_data User supplied data
|
||||
*/
|
||||
typedef void (*eth_bridge_cb_t)(struct eth_bridge *br, void *user_data);
|
||||
|
||||
/**
|
||||
* @brief Go through all the bridge instances in order to get
|
||||
* information about them. This is mainly useful in
|
||||
* net-shell to print data about currently active bridges.
|
||||
*
|
||||
* @param cb Callback to call for each bridge instance
|
||||
* @param user_data User supplied data
|
||||
*/
|
||||
void net_eth_bridge_foreach(eth_bridge_cb_t cb, void *user_data);
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_NET_ETHERNET_BRIDGE_H_ */
|
|
@ -181,10 +181,17 @@ struct net_pkt {
|
|||
* segment.
|
||||
*/
|
||||
#endif
|
||||
|
||||
uint8_t captured : 1; /* Set to 1 if this packet is already being
|
||||
* captured
|
||||
*/
|
||||
|
||||
uint8_t l2_bridged : 1; /* set to 1 if this packet comes from a bridge
|
||||
* and already contains its L2 header to be
|
||||
* preserved. Useful only if
|
||||
* defined(CONFIG_NET_ETHERNET_BRIDGE).
|
||||
*/
|
||||
|
||||
union {
|
||||
/* IPv6 hop limit or IPv4 ttl for this network packet.
|
||||
* The value is shared between IPv6 and IPv4.
|
||||
|
@ -349,6 +356,18 @@ static inline void net_pkt_set_captured(struct net_pkt *pkt, bool is_captured)
|
|||
pkt->captured = is_captured;
|
||||
}
|
||||
|
||||
static inline bool net_pkt_is_l2_bridged(struct net_pkt *pkt)
|
||||
{
|
||||
return IS_ENABLED(CONFIG_NET_ETHERNET_BRIDGE) ? !!(pkt->l2_bridged) : 0;
|
||||
}
|
||||
|
||||
static inline void net_pkt_set_l2_bridged(struct net_pkt *pkt, bool is_l2_bridged)
|
||||
{
|
||||
if (IS_ENABLED(CONFIG_NET_ETHERNET_BRIDGE)) {
|
||||
pkt->l2_bridged = is_l2_bridged;
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint8_t net_pkt_ip_hdr_len(struct net_pkt *pkt)
|
||||
{
|
||||
return pkt->ip_hdr_len;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue