Commit 89482f01 authored by Nicolas Pitre's avatar Nicolas Pitre Committed by Christopher Friedt
Browse files

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: default avatarNicolas Pitre <npitre@baylibre.com>
parent d3f6b54f
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -39,6 +39,7 @@ SECTION_DATA_PROLOGUE(net_if_area,,SUBALIGN(4)) \
	_net_if_list_end = .;                                   \
} GROUP_DATA_LINK_IN(RAMABLE_REGION, ROMABLE_REGION)            \
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)

#include <arch/arm/aarch32/cortex_m/scripts/linker.ld>
+2 −1
Original line number Diff line number Diff line
@@ -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 */

+8 −0
Original line number Diff line number Diff line
@@ -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 −0
Original line number Diff line number Diff line
/** @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_ */
+19 −0
Original line number Diff line number Diff line
@@ -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