diff --git a/src/core/net/ip6.cpp b/src/core/net/ip6.cpp index f202fc0a4..d028ca2de 100644 --- a/src/core/net/ip6.cpp +++ b/src/core/net/ip6.cpp @@ -911,7 +911,8 @@ Error Ip6::HandleExtensionHeaders(Message & aMessage, case kProtoFragment: // Always forward IPv6 fragments to the Host. - IgnoreError(ProcessReceiveCallback(aMessage, aMessageInfo, aNextHeader, aFromNcpHost, Message::kCopyToUse)); + IgnoreError(ProcessReceiveCallback(aMessage, aMessageInfo, aNextHeader, aFromNcpHost, + /* aAllowReceiveFilter */ false, Message::kCopyToUse)); SuccessOrExit(error = HandleFragment(aMessage, aNetif, aMessageInfo, aFromNcpHost)); break; @@ -1000,6 +1001,7 @@ Error Ip6::ProcessReceiveCallback(Message & aMessage, const MessageInfo &aMessageInfo, uint8_t aIpProto, bool aFromNcpHost, + bool aAllowReceiveFilter, Message::Ownership aMessageOwnership) { Error error = kErrorNone; @@ -1011,7 +1013,7 @@ Error Ip6::ProcessReceiveCallback(Message & aMessage, // Do not forward reassembled IPv6 packets. VerifyOrExit(aMessage.GetLength() <= kMinimalMtu, error = kErrorDrop); - if (mIsReceiveIp6FilterEnabled) + if (mIsReceiveIp6FilterEnabled && aAllowReceiveFilter) { #if !OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE // do not pass messages sent to an RLOC/ALOC, except Service Locator @@ -1122,16 +1124,14 @@ Error Ip6::HandleDatagram(Message &aMessage, Netif *aNetif, const void *aLinkMes bool receive; bool forwardThread; bool forwardHost; - bool multicastPromiscuous; bool shouldFreeMessage; uint8_t nextHeader; start: - receive = false; - forwardThread = false; - forwardHost = false; - multicastPromiscuous = false; - shouldFreeMessage = true; + receive = false; + forwardThread = false; + forwardHost = false; + shouldFreeMessage = true; SuccessOrExit(error = header.Init(aMessage)); @@ -1173,7 +1173,7 @@ start: } else if (netif->IsMulticastPromiscuousEnabled()) { - multicastPromiscuous = true; + forwardHost = true; } } else @@ -1232,7 +1232,8 @@ start: } #endif - error = ProcessReceiveCallback(aMessage, messageInfo, nextHeader, aFromNcpHost, Message::kCopyToUse); + error = ProcessReceiveCallback(aMessage, messageInfo, nextHeader, aFromNcpHost, + /* aAllowReceiveFilter */ !forwardHost, Message::kCopyToUse); if ((error == kErrorNone || error == kErrorNoRoute) && forwardHost) { @@ -1243,15 +1244,11 @@ start: (forwardThread || forwardHost ? Message::kCopyToUse : Message::kTakeCustody)); shouldFreeMessage = forwardThread || forwardHost; } - else if (multicastPromiscuous) - { - IgnoreError(ProcessReceiveCallback(aMessage, messageInfo, nextHeader, aFromNcpHost, Message::kCopyToUse)); - } if (forwardHost) { // try passing to host - error = ProcessReceiveCallback(aMessage, messageInfo, nextHeader, aFromNcpHost, + error = ProcessReceiveCallback(aMessage, messageInfo, nextHeader, aFromNcpHost, /* aAllowReceiveFilter */ false, forwardThread ? Message::kCopyToUse : Message::kTakeCustody); shouldFreeMessage = forwardThread; } diff --git a/src/core/net/ip6.hpp b/src/core/net/ip6.hpp index 6ff3f861f..d538021bb 100644 --- a/src/core/net/ip6.hpp +++ b/src/core/net/ip6.hpp @@ -326,6 +326,7 @@ private: const MessageInfo &aMessageInfo, uint8_t aIpProto, bool aFromNcpHost, + bool aAllowReceiveFilter, Message::Ownership aMessageOwnership); Error HandleExtensionHeaders(Message & aMessage, Netif * aNetif, diff --git a/tests/scripts/thread-cert/border_router/test_end_device_udp_reachability.py b/tests/scripts/thread-cert/border_router/test_end_device_udp_reachability.py new file mode 100644 index 000000000..96455c70c --- /dev/null +++ b/tests/scripts/thread-cert/border_router/test_end_device_udp_reachability.py @@ -0,0 +1,152 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2021, The OpenThread Authors. +# 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. Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS '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 COPYRIGHT HOLDER OR CONTRIBUTORS 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. +# +import ipaddress +import json +import logging +import unittest + +import config +import pktverify.verify +import thread_cert + +# Test description: +# This test verifies Thread end-devices have UDP reachability to the infrastructure link via a Thread BR. +# +# Topology: +# ----------------(eth)-------------------- +# | | +# BR1 HOST +# / +# FED1 +from pktverify.packet_verifier import PacketVerifier + +BR1 = 1 +FED1 = 2 +HOST = 3 + +PORT = 11111 +UDP_PAYLOAD_LEN = 17 + + +class TestEndDeviceUdpReachability(thread_cert.TestCase): + USE_MESSAGE_FACTORY = False + + TOPOLOGY = { + BR1: { + 'name': 'BR1', + 'is_otbr': True, + 'version': '1.2', + }, + FED1: { + 'name': 'FED1', + 'router_eligible': False, + }, + HOST: { + 'name': 'HOST', + 'is_host': True + }, + } + + def test(self): + br1 = self.nodes[BR1] + fed1 = self.nodes[FED1] + host = self.nodes[HOST] + + host.start(start_radvd=False) + self.simulator.go(5) + + br1.start() + self.simulator.go(5) + self.assertEqual('leader', br1.get_state()) + br1.udp_start("::", PORT, bind_unspecified=True) + + fed1.start() + self.simulator.go(5) + self.assertEqual('child', fed1.get_state()) + + self.simulator.go(20) + + fed1_omr_addr = fed1.get_ip6_address(config.ADDRESS_TYPE.OMR)[0] + host_addr = host.get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)[0] + print("Host address:", host_addr) + print('FED address:', fed1_omr_addr) + + self.assertTrue(fed1.ping(host_addr)) + self.assertTrue(host.ping(fed1_omr_addr, backbone=True)) + + fed1.udp_start("::", PORT) + + fed1.udp_send(UDP_PAYLOAD_LEN, host_addr, PORT) + self.simulator.go(5) + + host.udp_send_host(data='A' * UDP_PAYLOAD_LEN, ipaddr=fed1_omr_addr, port=PORT) + self.simulator.go(5) + + self.collect_ipaddrs() + self.collect_extra_vars(FED1_OMR=fed1_omr_addr, HOST_ONLINK_ULA=host_addr) + + def verify(self, pv: PacketVerifier): + pkts = pv.pkts + pv.summary.show() + + BR1 = pv.vars['BR1'] + FED1 = pv.vars['FED1'] + BR1_ETH = pv.vars['BR1_ETH'] + HOST_ETH = pv.vars['HOST_ETH'] + + udp_pkts = pkts.filter(""" + udp.srcport == {PORT} and + udp.dstport == {PORT} and + udp.length == {UDP_LENGTH} + """, + PORT=PORT, + UDP_LENGTH=UDP_PAYLOAD_LEN + 8) + + # FED1 should send a UDP message to BR1 + udp_pkts.filter_wpan_src64(FED1).must_next() + + # BR1 should forward the UDP message to the infrastructure link + udp_pkts.filter_eth_src(BR1_ETH).must_next() + + udp_pkts = pkts.filter(""" + udp.dstport == {PORT} and + udp.length == {UDP_LENGTH} + """, + PORT=PORT, + UDP_LENGTH=UDP_PAYLOAD_LEN + 8) + + # Host should send a UDP message to BR1 + udp_pkts.filter_eth_src(HOST_ETH).must_next() + + # BR1 should forward the UDP message to FED1 + udp_pkts.filter_wpan_src64(BR1).must_next() + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/scripts/thread-cert/node.py b/tests/scripts/thread-cert/node.py index bf9e8e14a..7b53bfaa1 100755 --- a/tests/scripts/thread-cert/node.py +++ b/tests/scripts/thread-cert/node.py @@ -2513,12 +2513,12 @@ class NodeImpl: payload += tlv.to_hex() self.commissioner_mgmtset(self.bytes_to_hex_str(payload)) - def udp_start(self, local_ipaddr, local_port): + def udp_start(self, local_ipaddr, local_port, bind_unspecified=False): cmd = 'udp open' self.send_command(cmd) self._expect_done() - cmd = 'udp bind %s %s' % (local_ipaddr, local_port) + cmd = 'udp bind %s %s %s' % ("-u" if bind_unspecified else "", local_ipaddr, local_port) self.send_command(cmd) self._expect_done()