diff --git a/WHATSNEW b/WHATSNEW index 03828369a2a..72f21cdcc4e 100644 --- a/WHATSNEW +++ b/WHATSNEW @@ -293,6 +293,18 @@ Notable backward incompatible changes are the following: negotiation. The MacQosWithBlockAck example fingerprints are updated; the QoS and NonQos showcase fingerprint controls remain unchanged. +17. IPv6 takes a global source when the outgoing interface is link-local only + + A datagram an IPv6 node originated into an Ipv6TunnelInterface, with no + source address bound, carried the tunnel interface's link-local address + fe80:: as its source, even to a global destination. A receiver beyond the + tunnel exit could not answer it. For a global unicast destination, IPv6 now + takes the source address from another interface when the outgoing interface + has only a link-local one, as Linux also does. This changes the packets, and + so the fingerprints, of simulations that send traffic with no bound source + to a global destination through an interface that has only a link-local + address, such as a tunnel interface. + Notable backward compatible changes are the following: 1. IEEE 802.11 per-station rate statistics diff --git a/src/inet/networklayer/ipv6/Ipv6.cc b/src/inet/networklayer/ipv6/Ipv6.cc index f3f518f1616..23b4eadc5f3 100644 --- a/src/inet/networklayer/ipv6/Ipv6.cc +++ b/src/inet/networklayer/ipv6/Ipv6.cc @@ -1006,9 +1006,33 @@ void Ipv6::fragmentPostRouting(Packet *packet, const NetworkInterface *ie, const !ipv6Header->getDestAddress().isSolicitedNodeMulticastAddress()) { // source address can be unspecified during DAD - const Ipv6Address& srcAddr = ie->getProtocolData()->getPreferredAddress(); + Ipv6Address srcAddr = ie->getProtocolData()->getPreferredAddress(); ASSERT(!srcAddr.isUnspecified()); // FIXME what if we don't have an address yet? + // A link-local source must not leave its link (RFC 4291 Section 2.5.6), but an + // outgoing interface with only a link-local address, such as an + // Ipv6TunnelInterface, has no other address. For a global destination, take the + // source from another interface, as Linux also does: RFC 6724 Section 4 only + // recommends the outgoing interface's addresses as the candidates, and its Rule + // 2 prefers a source whose scope is at least the destination's. A tentative + // address is not assigned yet (RFC 4862 Section 5.4), unless Optimistic + // Duplicate Address Detection (RFC 4429) allows its use. + const Ipv6Address& destAddr = ipv6Header->getDestAddress(); + if (srcAddr.isLinkLocal() && destAddr.getScope() == Ipv6Address::GLOBAL) { + for (int i = 0; i < ift->getNumInterfaces(); i++) { + const NetworkInterface *candidateIE = ift->getInterface(i); + auto candidateData = candidateIE->findProtocolData(); + if (candidateIE == ie || !candidateIE->isUp() || candidateData == nullptr) + continue; + const Ipv6Address& candidate = candidateData->getPreferredAddress(); + bool tentative = candidateData->isTentativeAddress(candidate) && !candidateData->isOptimisticDad(); + if (candidate.getScope() == Ipv6Address::GLOBAL && !tentative) { + srcAddr = candidate; + break; + } + } + } + // TODO factor out ipv6Header = nullptr; auto newIpv6Header = removeNetworkProtocolHeader(packet); diff --git a/tests/module/IPv6_tunnel.test b/tests/module/IPv6_tunnel.test index c0f0fdda4d7..ba5275296af 100644 --- a/tests/module/IPv6_tunnel.test +++ b/tests/module/IPv6_tunnel.test @@ -8,7 +8,10 @@ so packets A->H are encapsulated (outer A->B), forwarded to B, decapsulated ther We verify that B receives an IPv6-in-IPv6 ("Tunnelled IP datagram") packet, i.e. the new tunnel interface encapsulated correctly, and that the UDP traffic -reaches H. +reaches H. The application binds no source address, and the tunnel interface +has only its link-local address fe80::, so A must take its global address on +eth0 as the inner source: a link-local source must not leave its link +(RFC 4291 Section 2.5.6). %#-------------------------------------------------------------------------------------------------------------- %file: test.ned @@ -85,6 +88,10 @@ Tunnelled IP datagram %contains: stdout received 8 packets %#-------------------------------------------------------------------------------------------------------------- +%# the inner source is A's global address, not the tunnel interface's fe80:: +%contains: stdout +2001:db8:1:0:8aa:ff:fe00:1:1025 --> 2001:db8:2:0:8aa:ff:fe00:4:5000 +%#-------------------------------------------------------------------------------------------------------------- %postrun-command: grep "undisposed object:" test.out > test_undisposed.out || true %not-contains: test_undisposed.out undisposed object: (