Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F171577268
D59350.id185732.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
5 KB
Referenced Files
None
Subscribers
None
D59350.id185732.diff
View Options
diff --git a/share/man/man4/ng_bridge.4 b/share/man/man4/ng_bridge.4
--- a/share/man/man4/ng_bridge.4
+++ b/share/man/man4/ng_bridge.4
@@ -32,7 +32,7 @@
.\"
.\" Author: Archie Cobbs <archie@FreeBSD.org>
.\"
-.Dd August 31, 2026
+.Dd September 3, 2026
.Dt NG_BRIDGE 4
.Os
.Sh NAME
@@ -69,6 +69,14 @@
.Pp
A looped back link will be temporarily muted, i.e., all traffic
received on that link is ignored.
+.Pp
+If a packet is received on a hook that this node recently used to
+transmit that source address, it is treated as a reflected transmit
+and is dropped without moving the host and without muting the hook.
+Promiscuous receive on
+.Xr ng_ether 4
+.Ar lower
+is enough to produce that reflection.
.Sh IPFW PROCESSING
Processing of IP packets via the
.Xr ipfirewall 4
diff --git a/sys/netgraph/ng_bridge.c b/sys/netgraph/ng_bridge.c
--- a/sys/netgraph/ng_bridge.c
+++ b/sys/netgraph/ng_bridge.c
@@ -41,7 +41,10 @@
* each of its connected hooks, or links. A simple loop detection
* algorithm is included which disables a link for priv->conf.loopTimeout
* seconds when a host is seen to have jumped from one link to
- * another within priv->conf.minStableAge seconds.
+ * another within priv->conf.minStableAge seconds. A packet received
+ * on a hook this node recently used to transmit that source is
+ * treated as a reflected transmit, not a move, and is dropped
+ * without muting the hook.
*
* We keep a hashtable that maps Ethernet addresses to host info,
* which is contained in struct ng_bridge_host's. These structures
@@ -134,8 +137,11 @@
struct ng_bridge_host {
u_char addr[6]; /* ethernet address */
link_p link; /* link where addr can be found */
+ link_cp xmitLink; /* last hook we sent this source out */
u_int16_t age; /* seconds ago entry was created */
u_int16_t staleness; /* seconds ago host last heard from */
+ u_int16_t xmitAge; /* seconds since that send */
+ u_int16_t floodAge; /* seconds since flooded this source */
SLIST_ENTRY(ng_bridge_host) next; /* next entry in bucket */
};
@@ -715,9 +721,19 @@
*/
static inline int
ng_bridge_send_data(link_cp dst, int manycast, struct mbuf *m, item_p item) {
+ const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(dst->hook));
+ struct ng_bridge_host *host;
+ u_char shost[ETHER_ADDR_LEN];
+ int have_shost = 0;
int error = 0;
size_t len = m->m_pkthdr.len;
+ if (m->m_len >= ETHER_HDR_LEN) {
+ bcopy(mtod(m, struct ether_header *)->ether_shost,
+ shost, ETHER_ADDR_LEN);
+ have_shost = 1;
+ }
+
if(item != NULL)
NG_FWD_NEW_DATA(error, item, dst->hook, m);
else
@@ -734,6 +750,20 @@
return (error);
}
+ /*
+ * Remember the hook we last sent this source out. Written
+ * during shared access: the stores are pointer-sized or
+ * smaller, and a race costs at most one missed or spurious
+ * reflected-transmit drop within minStableAge.
+ */
+ if (have_shost) {
+ host = ng_bridge_get(priv, shost);
+ if (host != NULL) {
+ host->xmitLink = dst;
+ host->xmitAge = 0;
+ }
+ }
+
counter_u64_add(dst->stats.xmitPackets, 1);
counter_u64_add(dst->stats.xmitOctets, len);
switch (manycast) {
@@ -852,12 +882,30 @@
/* Look up packet's source Ethernet address in hashtable */
if ((host = ng_bridge_get(priv, eh->ether_shost)) != NULL)
/* Update time since last heard from this host.
- * This is safe without locking, because it's
- * the only operation during shared access.
+ * This is safe without locking: the writes done
+ * during shared access are this reset, the floodAge
+ * reset below, and the xmitLink/xmitAge record in
+ * ng_bridge_send_data().
*/
if (__predict_false(host->staleness > 0))
host->staleness = 0;
+ /*
+ * Packet received on a hook we recently used to transmit
+ * this source, or the source was recently flooded out every
+ * hook: our own transmit coming back. Do not move the host
+ * and do not mute the hook.
+ */
+ if (host != NULL && host->link != ctx.incoming &&
+ ((host->xmitLink == ctx.incoming &&
+ host->xmitAge <= priv->conf.minStableAge) ||
+ host->floodAge <= priv->conf.minStableAge)) {
+ counter_u64_add(ctx.incoming->stats.loopDrops, 1);
+ NG_FREE_ITEM(item);
+ NG_FREE_M(ctx.m);
+ return (0);
+ }
+
/*
* learnMac is 0 on uplink: neither insert a new host nor
* move an existing one from packets received there.
@@ -920,6 +968,14 @@
counter_u64_add(ctx.incoming->stats.recvUnknown, 1);
}
+ /*
+ * Every other hook is about to get a copy, so a reflection
+ * can return on any of them; xmitLink alone cannot record
+ * that. Mark the source as recently flooded.
+ */
+ if ((host = ng_bridge_get(priv, eh->ether_shost)) != NULL)
+ host->floodAge = 0;
+
/* Distribute unknown, multicast, broadcast pkts to all other links */
NG_NODE_FOREACH_HOOK(node, ng_bridge_send_ctx, &ctx);
@@ -1069,8 +1125,11 @@
return (ENOMEM);
bcopy(addr, host->addr, ETHER_ADDR_LEN);
host->link = link;
+ host->xmitLink = NULL;
host->staleness = 0;
host->age = 0;
+ host->xmitAge = 0xffff;
+ host->floodAge = 0xffff;
/* Add new element to hash bucket */
SLIST_INSERT_HEAD(&priv->tab[bucket], host, next);
@@ -1176,8 +1235,17 @@
*hptr = SLIST_NEXT(host, next);
free(host, M_NETGRAPH_BRIDGE);
priv->numHosts--;
- } else
+ } else {
+ /*
+ * The link is about to be freed; a
+ * reconnect could reuse the address.
+ */
+ if (host->xmitLink == link) {
+ host->xmitLink = NULL;
+ host->xmitAge = 0xffff;
+ }
hptr = &SLIST_NEXT(host, next);
+ }
}
}
}
@@ -1230,6 +1298,10 @@
} else {
if (host->age < 0xffff)
host->age++;
+ if (host->xmitAge < 0xffff)
+ host->xmitAge++;
+ if (host->floodAge < 0xffff)
+ host->floodAge++;
hptr = &SLIST_NEXT(host, next);
counter++;
}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, Sep 12, 9:05 PM (57 m, 30 s)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
38819100
Default Alt Text
D59350.id185732.diff (5 KB)
Attached To
Mode
D59350: ng_bridge(4): do not move a host on a reflected transmit
Attached
Detach File
Event Timeline
Log In to Comment