So far, the UDP core module implemented only IPv4 multicast support.
This patch extends the module with the features necessary for socket
layers on top to implement IPv6 multicast support as well:
o If a UDP PCB is bound to an IPv6 multicast address, a unicast source
address is selected and used to send the packet instead, as is
required (and was the case for IPv4 multicast already).
o Unlike IPv4's IP_MULTICAST_IF socket option, which takes a source
IPv4 address, the IPV6_MULTICAST_IF socket option (from RFC 3493)
takes an interface identifier to denote the interface to use for
outgoing multicast-destined packets. A new pair of UDP PCB API
calls, udp_[gs]et_multicast_netif_index(), are added to support
this. The new definition "NETIF_NO_INDEX" may be used to indicate
that lwIP should pick an interface instead.
IPv4 socket implementations may now also choose to map the given
source address to an interface index immediately and use the new
facility instead of the old udp_[gs]et_multicast_netif_addr() one.
A side effect of limiting the old facility to IPv4 is that for dual-
stack configurations with multicast support, the UDP PCB size is
reduced by (up to) 16 bytes.
o For configurations that enable loopback interface support, the IPv6
code now also supports multicast loopback (IPV6_MULTICAST_LOOP).
o The LWIP_MULTICAST_TX_OPTIONS opt.h setting now covers both IPv4
and IPv6, and as such is no longer strictly linked to IGMP. It is
therefore placed in its own lwIP options subgroup in opt.h.
The IPV6_MULTICAST_HOPS socket option can already be implemented using
the existing IP_MULTICAST_TTL support, and thus requires no additional
changes. Overall, this patch should not break any existing code.
This patch adds full support for IPv6 address scopes, thereby aiming
to be compliant with IPv6 standards in general and RFC 4007 in
particular. The high-level summary is that link-local addresses are
now meaningful only in the context of their own link, guaranteeing
full isolation between links (and their addresses) in this respect.
This isolation even allows multiple interfaces to have the same
link-local addresses locally assigned.
The implementation achieves this by extending the lwIP IPv6 address
structure with a zone field that, for addresses that have a scope,
carries the scope's zone in which that address has meaning. The zone
maps to one or more interfaces. By default, lwIP uses a policy that
provides a 1:1 mapping between links and interfaces, and considers
all other addresses unscoped, corresponding to the default policy
sketched in RFC 4007 Sec. 6. The implementation allows for replacing
the default policy with a custom policy if desired, though.
The lwIP core implementation has been changed to provide somewhat of
a balance between correctness and efficiency on on side, and backward
compatibility on the other. In particular, while the application would
ideally always provide a zone for a scoped address, putting this in as
a requirement would likely break many applications. Instead, the API
accepts both "properly zoned" IPv6 addresses and addresses that, while
scoped, "lack" a zone. lwIP will try to add a zone as soon as possible
for efficiency reasons, in particular from TCP/UDP/RAW PCB bind and
connect calls, but this may fail, and sendto calls may bypass that
anyway. Ultimately, a zone is always added when an IP packet is sent
when needed, because the link-layer lwIP code (and ND6 in particualar)
requires that all addresses be properly zoned for correctness: for
example, to provide isolation between links in the ND6 destination
cache. All this applies to packet output only, because on packet
input, all scoped addresses will be given a zone automatically.
It is also worth remarking that on output, no attempt is made to stop
outgoing packets with addresses for a zone not matching the outgoing
interface. However, unless the application explicitly provides
addresses that will result in such zone violations, the core API
implementation (and the IPv6 routing algorithm in particular) itself
will never take decisions that result in zone violations itself.
This patch adds a new header file, ip6_zone.h, which contains comments
that explain several implementation aspects in a bit more detail.
For now, it is possible to disable scope support by changing the new
LWIP_IPV6_SCOPES configuration option. For users of the core API, it
is important to note that scoped addresses that are locally assigned
to a netif must always have a zone set; the standard netif address
assignment functions always do this on behalf of the caller, though.
Also, core API users will want to enable LWIP_IPV6_SCOPES_DEBUG at
least initially when upgrading, to ensure that all addresses are
properly initialized.
This patch aims to fix three closely related issues.
o The implementation of IPV6_FRAG_COPYHEADER was fundamentally
incompatible with the presence of extension headers between the
IPv6 header and the Fragment Header. This patch changes the
implementation to support such extension headers as well, with
pretty much the same memory requirements. As a result, we can
remove the check that prevented such packets from being reassembled
in all cases, even with IPV6_FRAG_COPYHEADER off.
o Given that temporary data is stored in the Fragment Header of
packets saved for the purpose of reassembly, but ICMPv6 "Fragment
Reassembly Time Exceeded" packets contain part of the original
packet, such ICMPv6 packets could actually end up containing part
of the temporary data, which may even include a pointer value. The
ICMPv6 packet should contain the original, unchanged packet, so
save the original header data before overwriting it even if
IPV6_FRAG_COPYHEADER is disabled. This does add some extra memory
consumption.
o Previously, the reassembly would leave the fragment header in the
reassembled packet, which is not permitted by RFC 2460 and prevents
reassembly of particularly large packets (close to 65535 bytes
after reassembly). This patch gets rid of the fragment header. It
does require an implementation of memmove() for that purpose.
Note that this patch aims to improve correctness. Future changes
might restore some of the previous functionality in order to regain
optimal performance for certain cases (at the cost of more code).
The introduction of address lifetimes also means that lwIP correctly
supports transitions between PREFERRED and DEPRECATED address states,
and that means that the source address selection must be changed to
take this into account. Adding this feature to the previous algorithm
would have resulted in a mess, so this patch rewrites the algorithm to
stay close to the rules described in RFC 6724 (formerly 3484) Sec. 5.
This yields the following changes:
- Rule 2 ("prefer appropriate scope") is now fully implemented, most
importantly allowing larger-scope addresses to be picked if no
smaller-scope addresses are available (e.g., a global address may
now be used to connect to a unique-local address);
- Rule 3 ("avoid deprecated addresses") is now also fully implemented;
- unknown-scope addresses are also supported, with lowest priority;
- the link between the prescribed rules and the actual algorithm is
made much more explicit, hopefully allowing future improvements to
be made more easily.
For reasons explained in comments, one previous deviation from the RFC
on Rule 2 is retained for now.
As laid out in RFC 5942, the assumption that a dynamically assigned
(SLAAC/DHCPv6) address implies an on-link subnet, is wrong. lwIP does
currently make that assumption, routing packets according to local
address subnets rather than the on-link prefix list. The result is
that packets may not make it to their destination due to incorrect
routing decisions.
This patch changes the routing algorithms to be (more) compliant with
RFC 5942, by implementing the following new routing policies:
- all routing decisions check the on-link prefix list first, and
select a default router for off-link routing only if there is no
matching entry in the on-link prefix list;
- dynamically assigned addresses (from address autoconfiguration) are
considered /128 assignments, and thus, no routing decisions are taken
based on matches against their (/64) subnet anymore;
- more generally, all addresses that have a lifetime are considered
dynamically assigned and thus of size /128, which is the required
behavior for externally implemented SLAAC clients and DHCPv6;
- statically assigned (i.e., manually configured) addresses are still
considered /64 assignments, and thus, their associated subnet is
considered for routing decisions, in order to behave as generally
expected by end users and to retain backward compatibility;
- the link-local address in IPv6 address slot #0 is considered static
and thus has no lifetime and an implied /64 subnet, although link-
local routing is currently always handled separately anyway.
IPv6 source address selection is kept as is, as the subnet tests in
the algorithm serve as poor man's longest-common-prefix equivalent
there (RFC 6724 Sec. 5, Rule 8).
Previously, IPv6 routing could select a next-hop router on a netif
that was down or disconnected, potentially resulting in packets being
dropped unnecessarily. This patch changes router selection to take
into account the state of the router's associated netif, eliminating
such unnecessary packet loss.
Also, this patch fixes the test for router validity, which was
erroneously based on the router's invalidation timer rather than its
neighbor cache entry state. Given that an expired router has no
associated neighbor cache entry, no invalid routers would previously
ever be returned.
Finally, this patch also adds round-robin selection of routers that
are not known to be reachable or probably reachable, as per RFC 4861
Sec. 6.3.6 point (2). Support for this feature was partially present
but not actually functional.
This patch rearranges the code division between nd6.c and ip6.c such
that the latter does not need to access ND6-internal data structures
(specifically, "default_router_list") directly anymore.
Generally speaking, packets with a loopback destination address -
127.0.0.1 for IPv4 and ::1 for IPv6 - should not be accepted on
non-loopback interfaces. For IPv4, this is implied by RFC 1122
Sec. 3.2.1.3. For IPv6, it is mandated by RFC 4291 Sec. 2.5.3.
Failure to perform this filtering may have security implications, as
applications that bind sockets to loopback addresses may not expect
that nodes on the local external network be able to produce traffic
that will arrive at such sockets.
With this patch, lwIP drops packets that are sent to a loopback
address but do not originate from the interface that has the loopback
address assigned to it. This approach works regardless of whether it
is lwIP or the system using it that implements a loopback netif. The
only exception that must be made is for configurations that enable
netif packet loopback but disable the lwIP loopback netif: in that
case, loopback packets are routed across non-loopback netifs and would
thus be lost by the new filter as well.
For IPv6, loopback-destined packets are also no longer forwarded; the
IPv4 forwarding code already had a check for that.
As a small performance improvement, the IPv6 link-local/loopback
address check is now performed only once per packet rather than
repeatedly for every candidate netif.
In general, netif_default may be NULL, and various places in the code
already check for this case before attempting to dereference the
netif_default pointer. Some places do not perform this check though,
and may cause null pointer dereferences if netif_default is not set.
This patch adds NULL checks to those places as well.
In the BSD socket API world, IP_HDRINCL is a socket option for "raw"
sockets that indicates whether sent packets already include an IP
header. Within lwIP, "IP_HDRINCL" is redefined as a special value
that indicates to lwIP-internal functions that an IP header is already
included. While somewhat related, the two meanings are different and,
on platforms that define the IP_HDRINCL socket option, this results in
a conflict. This patch renames the lwIP one to "LWIP_IP_HDRINCL",
thus resolving the conflict.
ip_addr_t is used for all generic IP addresses for the API, ip(4/6)_addr_t are only used internally or when initializing netifs or when calling version-related functions