ankohuu opened a new pull request, #18738:
URL: https://github.com/apache/nuttx/pull/18738
## Summary
NuttX currently supports forwarding of IP multicast packets. This
forwarding is not comparable to multicast-capable switches or routers, which
rely on `IGMP/MLD` snooping or multicast routing protocols. Instead, it
performs a simple
flooding mechanism based on locally joined `IGMP` groups, effectively
behaving maybe like a relay.
This PR does not change the existing behavior. It only blocks cases that
should not be forwarded under the current model.
- `224.0.0.0/24` for link-local IPv4 multicast scope
- IPv6 interface-local and link-local multicast destinations
## Impact
IP multicast packets behavior changed in above cases.
## Testing
The issue was identified while I try to set up a NAT environment using the
NuttX simulator. The test setup consists of a sim instance with two network
interfaces, both backed by TAP devices.
### Env
```
os: Linux ankohuu-pb16250 6.17.0-1017-oem #17-Ubuntu SMP PREEMPT_DYNAMIC
Fri Mar 27 13:48:03 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux
./tools/configure.sh sim:nsh
```
### Config
<details>
<summary>.config file</summary>
```
> CONFIG_BASE_DEFCONFIG="sim:nsh-dirty"
> CONFIG_SIM_NETDEV=y
> CONFIG_SIM_NETDEV_TAP=y
> CONFIG_SIM_NETDEV_MTU=1500
> CONFIG_SIM_NETDEV_NUMBER=2
> CONFIG_SIM_NET_HOST_ROUTE=y
> CONFIG_SCHED_LPWORK=y
> CONFIG_SCHED_LPNTHREADS=1
> CONFIG_SCHED_LPWORKPRIORITY=100
> CONFIG_SCHED_LPWORKSTACKSIZE=2048
> CONFIG_SCHED_LPWORKSTACKSECTION=""
> CONFIG_NETDEVICES=y
> CONFIG_MDIO_BUS=y
> CONFIG_ARCH_HAVE_NET=y
> CONFIG_ARCH_HAVE_NETDEV_STATISTICS=y
> CONFIG_NET_MCASTGROUP=y
> CONFIG_NET=y
> CONFIG_NET_DEFAULT_MIN_PORT=4096
> CONFIG_NET_DEFAULT_MAX_PORT=32000
> CONFIG_NET_ETH_PKTSIZE=590
> CONFIG_NET_GUARDSIZE=2
> CONFIG_NET_LL_GUARDSIZE=14
> CONFIG_NET_RECV_BUFSIZE=0
> CONFIG_NET_ETHERNET=y
> CONFIG_NETDEV_IFINDEX=y
> CONFIG_NET_IPv4=y
> CONFIG_NET_IPFORWARD=y
> CONFIG_NET_IPFORWARD_BROADCAST=y
> CONFIG_NET_IPFORWARD_NSTRUCT=4
> CONFIG_NET_IPFORWARD_ALLOC_STRUCT=1
> CONFIG_NET_NAT=y
> CONFIG_NET_NAT44=y
> CONFIG_NET_NAT44_FULL_CONE=y
> CONFIG_NET_NAT_HASH_BITS=5
> CONFIG_NET_NAT_TCP_EXPIRE_SEC=86400
> CONFIG_NET_NAT_UDP_EXPIRE_SEC=240
> CONFIG_NET_NAT_ICMP_EXPIRE_SEC=60
> CONFIG_NET_NAT_ICMPv6_EXPIRE_SEC=60
> CONFIG_NET_NAT_ENTRY_RECLAIM_SEC=3600
> CONFIG_NET_IPFILTER=y
> CONFIG_NET_IPTABLES=y
> CONFIG_NET_PREALLOC_DEVIF_CALLBACKS=16
> CONFIG_NET_ALLOC_DEVIF_CALLBACKS=0
> CONFIG_NET_SOCKOPTS=y
> CONFIG_NET_IPV4_CHECKSUMS=y
> CONFIG_NET_ICMP=y
> CONFIG_NET_ICMP_PMTU_ENTRIES=0
> CONFIG_NET_ICMP_CHECKSUMS=y
> CONFIG_NET_IGMP=y
> CONFIG_NET_IGMP_CHECKSUMS=y
> CONFIG_NET_ARP=y
> CONFIG_NET_ARPTAB_SIZE=16
> CONFIG_NET_ARP_MAXAGE=120
> CONFIG_NET_ARP_MAXAGE_UNREACHABLE=1
> CONFIG_NET_ARP_SEND=y
> CONFIG_ARP_SEND_MAXTRIES=5
> CONFIG_ARP_SEND_DELAYMSEC=20
> CONFIG_NET_SNOOP_BUFSIZE=4096
> CONFIG_NET_RECV_PACK=y
> CONFIG_NET_ROUTE=y
> CONFIG_ROUTE_IPv4_RAMROUTE=y
> CONFIG_ROUTE_MAX_IPv4_RAMROUTES=4
> CONFIG_ROUTE_LONGEST_MATCH=y
> CONFIG_MM_IOB=y
> CONFIG_IOB_NBUFFERS=8
> CONFIG_IOB_BUFSIZE=196
> CONFIG_IOB_ALIGNMENT=4
> CONFIG_IOB_SECTION=""
> CONFIG_IOB_NCHAINS=0
> CONFIG_IOB_THROTTLE=0
> CONFIG_NETUTILS_IPTABLES=y
> CONFIG_NETUTILS_NETINIT=y
> CONFIG_NETINIT_IPADDR=0x0a000002
> CONFIG_NETINIT_DRIPADDR=0x0a000001
> CONFIG_NETINIT_NETMASK=0xffffff00
> CONFIG_NETUTILS_NETLIB=y
> CONFIG_NETUTILS_PING=y
> CONFIG_NETUTILS_PING6=y
> CONFIG_NSH_NETINIT=y
> CONFIG_NSH_WGET_BUFF_SIZE=512
> CONFIG_SYSTEM_IPTABLES=y
> CONFIG_SYSTEM_IPTABLES_PRIORITY=100
> CONFIG_SYSTEM_IPTABLES_STACKSIZE=2048
> CONFIG_SYSTEM_PING=y
> CONFIG_SYSTEM_PING_PROGNAME="ping"
> CONFIG_SYSTEM_PING_PRIORITY=100
> CONFIG_SYSTEM_PING_STACKSIZE=2048
> CONFIG_SYSTEM_PING6=y
> CONFIG_SYSTEM_PING6_PROGNAME="ping6"
> CONFIG_SYSTEM_PING6_PRIORITY=100
> CONFIG_SYSTEM_PING6_STACKSIZE=2048
```
</details>
### Test step
- Use scapy to inject packets to tap0
```
def main() -> None:
pkt = (
Ether(src="c2:ad:53:d1:33:d4", dst="01:00:5e:00:00:01")
/ IP(src="10.0.0.1", dst="224.0.0.1", ttl=5)
/ UDP(sport=12345, dport=5000)
/ Raw(b"patch1-linklocal")
)
sendp(pkt, iface="tap0", verbose=False)
print("sent 224.0.0.1 via tap0")
if __name__ == "__main__":
main()
```
- gdb ./nuttx with breakpoints
```
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep y 0x00000000400cd12b in ipv4_forward_broadcast
at ipforward/ipv4_forward.c:623
breakpoint already hit 1 time
silent
printf "ENTER ipv4_forward_broadcast ttl=%u\n", (unsigned
int)ipv4->ttl
continue
2 breakpoint keep y 0x00000000400cd1ae in ipv4_forward_broadcast
at ipforward/ipv4_forward.c:646
breakpoint already hit 1 time
silent
printf "DROP_LINKLOCAL ttl=%u\n", (unsigned int)ipv4->ttl
continue
3 breakpoint keep y 0x00000000400cd1ce in ipv4_forward_broadcast
at ipforward/ipv4_forward.c:657
silent
printf "FOREACH ttl=%u\n", (unsigned int)ipv4->ttl
continue
```
Log:
```
Linux:
~/P/nuttx [fix/non-forwardable-multicast] > sudo ./.venv/bin/python
./verify-linklocal-mcast-send.py
sent 224.0.0.1 via tap0
~/P/nuttx [fix/non-forwardable-multicast] > sudo ./.venv/bin/python
./verify-linklocal-mcast-send.py
sent 224.0.0.1 via tap0
~/P/nuttx [fix/non-forwardable-multicast] > sudo ./.venv/bin/python
./verify-linklocal-mcast-send.py
sent 224.0.0.1 via tap0
Nuttx:
NuttShell (NSH) NuttX-12.13.0
nsh> ifconfig
eth0 Link encap:Ethernet HWaddr 42:b3:0d:d6:82:81 at RUNNING mtu 1500
inet addr:10.0.0.2 DRaddr:10.0.0.1 Mask:255.255.255.0
eth1 Link encap:Ethernet HWaddr 42:8a:90:eb:ef:03 at DOWN mtu 1500
inet addr:0.0.0.0 DRaddr:0.0.0.0 Mask:0.0.0.0
nsh> ENTER ipv4_forward_broadcast ttl=5
DROP_LINKLOCAL ttl=5
ENTER ipv4_forward_broadcast ttl=5
DROP_LINKLOCAL ttl=5
ENTER ipv4_forward_broadcast ttl=5
DROP_LINKLOCAL ttl=5
```
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]