1*ef8d499eSDavid van Moolenbroek /* LWIP service - ifaddr.c - network interface address management */
2*ef8d499eSDavid van Moolenbroek /*
3*ef8d499eSDavid van Moolenbroek * This module is an exception to the regular source organization of this
4*ef8d499eSDavid van Moolenbroek * service, in that it manages part of another module's data structures, namely
5*ef8d499eSDavid van Moolenbroek * ifdev. As such, it should be seen as logically part of ifdev. It is
6*ef8d499eSDavid van Moolenbroek * separated only to keep the source code more manageable. Still, this module
7*ef8d499eSDavid van Moolenbroek * may use direct access only on the address-related fields of the ifdev
8*ef8d499eSDavid van Moolenbroek * structure, so that those one day may be move into an ifaddr-specific
9*ef8d499eSDavid van Moolenbroek * substructure within ifdev.
10*ef8d499eSDavid van Moolenbroek */
11*ef8d499eSDavid van Moolenbroek /*
12*ef8d499eSDavid van Moolenbroek * We manage three types of addresses here: IPv4 addresses (ifaddr_v4),
13*ef8d499eSDavid van Moolenbroek * IPv6 addresses (ifaddr_v6), and link-layer a.k.a. MAC addresses (ifaddr_dl).
14*ef8d499eSDavid van Moolenbroek *
15*ef8d499eSDavid van Moolenbroek * Managing IPv4 addresses is easy. lwIP supports only one IPv4 address per
16*ef8d499eSDavid van Moolenbroek * netif. While it would be possible to construct a model where one ifdev
17*ef8d499eSDavid van Moolenbroek * consists of multiple netifs (with one IPv4 address each), we not support
18*ef8d499eSDavid van Moolenbroek * this--mostly because it is a pain to keep state synchronized between the
19*ef8d499eSDavid van Moolenbroek * netifs in that case. Such support can still be added later; the IPv4 API
20*ef8d499eSDavid van Moolenbroek * exposed from here does support multiple IPv4 addresses already just in case,
21*ef8d499eSDavid van Moolenbroek * as does much of the code using the API.
22*ef8d499eSDavid van Moolenbroek *
23*ef8d499eSDavid van Moolenbroek * For IPv4 addresses we maintain only one extra piece of information here,
24*ef8d499eSDavid van Moolenbroek * which is whether an IPv4 address has been set at all. This is because for
25*ef8d499eSDavid van Moolenbroek * our userland (DHCP clients in particular), we must allow assigning 0.0.0.0
26*ef8d499eSDavid van Moolenbroek * as address to an interface. We do not use the lwIP per-netif IPv4 gateway
27*ef8d499eSDavid van Moolenbroek * field, nor the concept of a "default netif", in both cases because we
28*ef8d499eSDavid van Moolenbroek * override all (routing) decisions that would use those settings. lwIP does
29*ef8d499eSDavid van Moolenbroek * not allow a broadcast address to be set, so support for broadcast addresses
30*ef8d499eSDavid van Moolenbroek * is botched here: we disregard custom broadcast addresses given to us, and
31*ef8d499eSDavid van Moolenbroek * instead expose the broadcast address that is used within lwIP.
32*ef8d499eSDavid van Moolenbroek *
33*ef8d499eSDavid van Moolenbroek * Managing IPv6 addresses is much more complicated. First of all, even though
34*ef8d499eSDavid van Moolenbroek * lwIP supports stateless address autoconfiguration (SLAAC) as per RFC 4862,
35*ef8d499eSDavid van Moolenbroek * we disable that and instead make dhcpcd(8) responsible for all IPv6 address
36*ef8d499eSDavid van Moolenbroek * configuration. dhcpcd(8) will set addresses and routes as necessary, the
37*ef8d499eSDavid van Moolenbroek * latter of which are used in lwIP through our routing hooks (in the route
38*ef8d499eSDavid van Moolenbroek * module). This approach, which is in line with where NetBSD is headed,
39*ef8d499eSDavid van Moolenbroek * allows us to work around a number of lwIP limitations. As a result we do
40*ef8d499eSDavid van Moolenbroek * differ in this respect from NetBSD, which may switch between kernel-only,
41*ef8d499eSDavid van Moolenbroek * dhcpcd-only, and hybrid autoconfiguration, mainly throught the accept_rtadv
42*ef8d499eSDavid van Moolenbroek * sysctl(7) node. Writing to this node has no real effect on MINIX 3.
43*ef8d499eSDavid van Moolenbroek *
44*ef8d499eSDavid van Moolenbroek * All IPv6 addresses have a prefix length, which is almost but not quite the
45*ef8d499eSDavid van Moolenbroek * same as IPv4's subnet masks (see RFC 5942). We must maintain the per-
46*ef8d499eSDavid van Moolenbroek * address prefix length ourselves, as lwIP supports IPv6 prefix lengths of 64
47*ef8d499eSDavid van Moolenbroek * bits only. Our dhcpcd(8)-based approach allows us to work around that.
48*ef8d499eSDavid van Moolenbroek *
49*ef8d499eSDavid van Moolenbroek * All IPv6 addresses also have a state and a lifetime, both of which are
50*ef8d499eSDavid van Moolenbroek * managed by lwIP. Unlike for IPv4, address-derived routes and routing socket
51*ef8d499eSDavid van Moolenbroek * messages are only created for addresses that are "valid", which means that
52*ef8d499eSDavid van Moolenbroek * they are in either PREFERRED or DEPRECATED state. This means that we have
53*ef8d499eSDavid van Moolenbroek * to be aware of all address state transitions between "valid" and "not
54*ef8d499eSDavid van Moolenbroek * valid", some of which (namely address duplication detection and lifetime
55*ef8d499eSDavid van Moolenbroek * expirations) are initiated by lwIP. As such, we need to keep shadow state
56*ef8d499eSDavid van Moolenbroek * for each address, and use a callback to detect whether state has changed.
57*ef8d499eSDavid van Moolenbroek *
58*ef8d499eSDavid van Moolenbroek * For understanding of this module as well as lwIP, it is important to note
59*ef8d499eSDavid van Moolenbroek * that "valid" is not the opposite of "invalid" in this context: "not valid"
60*ef8d499eSDavid van Moolenbroek * includes the address states INVALID, DUPLICATED, and TENTATIVE, while
61*ef8d499eSDavid van Moolenbroek * "invalid"/INVALID simply means that the address slot is free.
62*ef8d499eSDavid van Moolenbroek *
63*ef8d499eSDavid van Moolenbroek * Each IPv6 address also has associated flags. We support an AUTOCONF flag
64*ef8d499eSDavid van Moolenbroek * which indicates that no subnet route should be added for the address; on
65*ef8d499eSDavid van Moolenbroek * MINIX 3, dhcpcd(8) is modified to pass in that flag when appropriate, thus
66*ef8d499eSDavid van Moolenbroek * solving a problem that NetBSD suffers from, namely that it does not know
67*ef8d499eSDavid van Moolenbroek * whether a userland-given route is static (implying a subnet) or auto-
68*ef8d499eSDavid van Moolenbroek * configured (implying no subnet, again as per RFC 5942), leading to it doing
69*ef8d499eSDavid van Moolenbroek * the wrong thing in dhcpcd-only autoconfiguration mode. The TEMPORARY flag,
70*ef8d499eSDavid van Moolenbroek * for privacy addresses (RFC 4941) should be the same as on NetBSD; it is
71*ef8d499eSDavid van Moolenbroek * currently used only in source address selection (RFC 6724). We override
72*ef8d499eSDavid van Moolenbroek * lwIP's IPv6 source address selection algorithm to include support for not
73*ef8d499eSDavid van Moolenbroek * just this flag, but also label and proper longest-common-prefix comparisons.
74*ef8d499eSDavid van Moolenbroek * Finally, there is an HWBASED flag to make sure that when the link-layer
75*ef8d499eSDavid van Moolenbroek * address is changed, the IPv6 link-local address is changed accordingly only
76*ef8d499eSDavid van Moolenbroek * if the previous link-local address was also autogenerated from a link-layer
77*ef8d499eSDavid van Moolenbroek * address and not set manually by userland.
78*ef8d499eSDavid van Moolenbroek *
79*ef8d499eSDavid van Moolenbroek * Finally, we support multiple link-layer addresses per interface, but only
80*ef8d499eSDavid van Moolenbroek * because NetBSD's ifconfig(8) uses an API that expects such multi-address
81*ef8d499eSDavid van Moolenbroek * support. At any time, only one of the addresses is marked as "active",
82*ef8d499eSDavid van Moolenbroek * which means it is used as MAC address in outgoing packets. We support only
83*ef8d499eSDavid van Moolenbroek * one MAC address per device driver, so the support for additional, inactive
84*ef8d499eSDavid van Moolenbroek * link-layer addresses is there exclusively for ifconfig(8) interoperability.
85*ef8d499eSDavid van Moolenbroek *
86*ef8d499eSDavid van Moolenbroek * All interfaces, including those that do not have MAC addresses at all (e.g.,
87*ef8d499eSDavid van Moolenbroek * loopback interfaces), do have one link-layer address. This is expected in
88*ef8d499eSDavid van Moolenbroek * particular by getifaddrs(3), which only recognizes interfaces that have a
89*ef8d499eSDavid van Moolenbroek * link-layer address.
90*ef8d499eSDavid van Moolenbroek *
91*ef8d499eSDavid van Moolenbroek * Many features are still missing here, especially for IP addresses. For
92*ef8d499eSDavid van Moolenbroek * example, we do not yet support destination addresses at all yet, simply
93*ef8d499eSDavid van Moolenbroek * because there is no interface type that uses them. For IPv6, more work is
94*ef8d499eSDavid van Moolenbroek * to be done to support proper netif status transitions versus address states,
95*ef8d499eSDavid van Moolenbroek * fallout from address duplication, and various ND6_IFF_ flags.
96*ef8d499eSDavid van Moolenbroek */
97*ef8d499eSDavid van Moolenbroek
98*ef8d499eSDavid van Moolenbroek #include "lwip.h"
99*ef8d499eSDavid van Moolenbroek #include "rtsock.h"
100*ef8d499eSDavid van Moolenbroek #include "route.h"
101*ef8d499eSDavid van Moolenbroek
102*ef8d499eSDavid van Moolenbroek #include "lwip/etharp.h"
103*ef8d499eSDavid van Moolenbroek
104*ef8d499eSDavid van Moolenbroek #include <netinet6/in6_var.h>
105*ef8d499eSDavid van Moolenbroek #include <netinet6/nd6.h>
106*ef8d499eSDavid van Moolenbroek
107*ef8d499eSDavid van Moolenbroek /*
108*ef8d499eSDavid van Moolenbroek * Routing flags for local address and local network routing entries. This
109*ef8d499eSDavid van Moolenbroek * may later have to be refined, for example in order not to set RTF_CLONING
110*ef8d499eSDavid van Moolenbroek * for routes on interfaces that do not have link-layer addressing.
111*ef8d499eSDavid van Moolenbroek *
112*ef8d499eSDavid van Moolenbroek * IMPORTANT: as of NetBSD 8, RTF_CLONING has been renamed to RTF_CONNECTED.
113*ef8d499eSDavid van Moolenbroek */
114*ef8d499eSDavid van Moolenbroek #define IFADDR_HOST_RTFLAGS (RTF_UP | RTF_HOST | RTF_LOCAL)
115*ef8d499eSDavid van Moolenbroek #define IFADDR_NET_RTFLAGS (RTF_UP | RTF_CLONING)
116*ef8d499eSDavid van Moolenbroek
117*ef8d499eSDavid van Moolenbroek /* Address-related sysctl(7) settings. */
118*ef8d499eSDavid van Moolenbroek int ifaddr_auto_linklocal = 1; /* different from NetBSD, see its usage */
119*ef8d499eSDavid van Moolenbroek int ifaddr_accept_rtadv = 0; /* settable but completely disregarded */
120*ef8d499eSDavid van Moolenbroek
121*ef8d499eSDavid van Moolenbroek /*
122*ef8d499eSDavid van Moolenbroek * Initialize the local address administration for an interface that is in the
123*ef8d499eSDavid van Moolenbroek * process of being created.
124*ef8d499eSDavid van Moolenbroek */
125*ef8d499eSDavid van Moolenbroek void
ifaddr_init(struct ifdev * ifdev)126*ef8d499eSDavid van Moolenbroek ifaddr_init(struct ifdev * ifdev)
127*ef8d499eSDavid van Moolenbroek {
128*ef8d499eSDavid van Moolenbroek unsigned int i;
129*ef8d499eSDavid van Moolenbroek
130*ef8d499eSDavid van Moolenbroek ifdev->ifdev_v4set = FALSE;
131*ef8d499eSDavid van Moolenbroek
132*ef8d499eSDavid van Moolenbroek for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++)
133*ef8d499eSDavid van Moolenbroek ifdev->ifdev_v6state[i] = IP6_ADDR_INVALID;
134*ef8d499eSDavid van Moolenbroek
135*ef8d499eSDavid van Moolenbroek for (i = 0; i < __arraycount(ifdev->ifdev_hwlist); i++)
136*ef8d499eSDavid van Moolenbroek ifdev->ifdev_hwlist[i].ifhwa_flags = 0;
137*ef8d499eSDavid van Moolenbroek }
138*ef8d499eSDavid van Moolenbroek
139*ef8d499eSDavid van Moolenbroek /*
140*ef8d499eSDavid van Moolenbroek * Find an IPv4 address locally assigned to a interface. The IPv4 address is
141*ef8d499eSDavid van Moolenbroek * given as 'addr'. The interface is given as 'ifdev'. On success, return OK,
142*ef8d499eSDavid van Moolenbroek * with the IPv4 address number stored in 'num'. On failure, return a negative
143*ef8d499eSDavid van Moolenbroek * error code.
144*ef8d499eSDavid van Moolenbroek */
145*ef8d499eSDavid van Moolenbroek int
ifaddr_v4_find(struct ifdev * ifdev,const struct sockaddr_in * addr,ifaddr_v4_num_t * num)146*ef8d499eSDavid van Moolenbroek ifaddr_v4_find(struct ifdev * ifdev, const struct sockaddr_in * addr,
147*ef8d499eSDavid van Moolenbroek ifaddr_v4_num_t * num)
148*ef8d499eSDavid van Moolenbroek {
149*ef8d499eSDavid van Moolenbroek ip_addr_t ipaddr;
150*ef8d499eSDavid van Moolenbroek int r;
151*ef8d499eSDavid van Moolenbroek
152*ef8d499eSDavid van Moolenbroek if ((r = addr_get_inet((const struct sockaddr *)addr, sizeof(*addr),
153*ef8d499eSDavid van Moolenbroek IPADDR_TYPE_V4, &ipaddr, TRUE /*kame*/, NULL /*port*/)) != OK)
154*ef8d499eSDavid van Moolenbroek return r;
155*ef8d499eSDavid van Moolenbroek
156*ef8d499eSDavid van Moolenbroek if (!ifdev->ifdev_v4set ||
157*ef8d499eSDavid van Moolenbroek !ip_addr_cmp(netif_ip_addr4(ifdev_get_netif(ifdev)), &ipaddr))
158*ef8d499eSDavid van Moolenbroek return EADDRNOTAVAIL;
159*ef8d499eSDavid van Moolenbroek
160*ef8d499eSDavid van Moolenbroek *num = 0;
161*ef8d499eSDavid van Moolenbroek return OK;
162*ef8d499eSDavid van Moolenbroek }
163*ef8d499eSDavid van Moolenbroek
164*ef8d499eSDavid van Moolenbroek /*
165*ef8d499eSDavid van Moolenbroek * Enumerate IPv4 addresses locally assigned to the given interface 'ifdev'.
166*ef8d499eSDavid van Moolenbroek * The caller should set 'nump' to 0 initially, and increase it by one between
167*ef8d499eSDavid van Moolenbroek * a successful call and the next enumeration call. Return TRUE on success,
168*ef8d499eSDavid van Moolenbroek * meaning that starting from the given value of 'nump' there is at least one
169*ef8d499eSDavid van Moolenbroek * IPv4 address, of which the number is stored in 'nump' on return. Return
170*ef8d499eSDavid van Moolenbroek * FALSE if there are no more IPv4 addresses locally assigned to the interface.
171*ef8d499eSDavid van Moolenbroek */
172*ef8d499eSDavid van Moolenbroek int
ifaddr_v4_enum(struct ifdev * ifdev,ifaddr_v4_num_t * num)173*ef8d499eSDavid van Moolenbroek ifaddr_v4_enum(struct ifdev * ifdev, ifaddr_v4_num_t * num)
174*ef8d499eSDavid van Moolenbroek {
175*ef8d499eSDavid van Moolenbroek
176*ef8d499eSDavid van Moolenbroek /*
177*ef8d499eSDavid van Moolenbroek * For now, we support only up to one IPv4 address per interface.
178*ef8d499eSDavid van Moolenbroek * set if we are to return it.
179*ef8d499eSDavid van Moolenbroek */
180*ef8d499eSDavid van Moolenbroek return (*num == 0 && ifdev->ifdev_v4set);
181*ef8d499eSDavid van Moolenbroek }
182*ef8d499eSDavid van Moolenbroek
183*ef8d499eSDavid van Moolenbroek /*
184*ef8d499eSDavid van Moolenbroek * Obtain information about the IPv4 address 'num' assigned to the interface
185*ef8d499eSDavid van Moolenbroek * 'ifdev'. On success, return OK, with the IPv4 address stored in 'addr', the
186*ef8d499eSDavid van Moolenbroek * network mask stored in 'mask', the broadcast stored in 'bcast', and the
187*ef8d499eSDavid van Moolenbroek * destination address stored in 'dest'. Each of these pointers may be NULL.
188*ef8d499eSDavid van Moolenbroek * The interface may not have a broadcast and/or destination address; in that
189*ef8d499eSDavid van Moolenbroek * case, their corresponding structures are not filled in at all, and thus must
190*ef8d499eSDavid van Moolenbroek * be preinitialized by the caller to a default state. The reason for not
191*ef8d499eSDavid van Moolenbroek * zeroing them is that some callers use the same buffer for both. On failure,
192*ef8d499eSDavid van Moolenbroek * return a negative error code.
193*ef8d499eSDavid van Moolenbroek */
194*ef8d499eSDavid van Moolenbroek int
ifaddr_v4_get(struct ifdev * ifdev,ifaddr_v4_num_t num,struct sockaddr_in * addr,struct sockaddr_in * mask,struct sockaddr_in * bcast,struct sockaddr_in * dest)195*ef8d499eSDavid van Moolenbroek ifaddr_v4_get(struct ifdev * ifdev, ifaddr_v4_num_t num,
196*ef8d499eSDavid van Moolenbroek struct sockaddr_in * addr, struct sockaddr_in * mask,
197*ef8d499eSDavid van Moolenbroek struct sockaddr_in * bcast, struct sockaddr_in * dest)
198*ef8d499eSDavid van Moolenbroek {
199*ef8d499eSDavid van Moolenbroek const ip_addr_t *ipaddr, *netmask;
200*ef8d499eSDavid van Moolenbroek struct netif *netif;
201*ef8d499eSDavid van Moolenbroek ip_addr_t broad;
202*ef8d499eSDavid van Moolenbroek socklen_t addr_len;
203*ef8d499eSDavid van Moolenbroek
204*ef8d499eSDavid van Moolenbroek if (!ifaddr_v4_enum(ifdev, &num))
205*ef8d499eSDavid van Moolenbroek return EADDRNOTAVAIL;
206*ef8d499eSDavid van Moolenbroek
207*ef8d499eSDavid van Moolenbroek netif = ifdev_get_netif(ifdev);
208*ef8d499eSDavid van Moolenbroek
209*ef8d499eSDavid van Moolenbroek if (addr != NULL) {
210*ef8d499eSDavid van Moolenbroek addr_len = sizeof(*addr);
211*ef8d499eSDavid van Moolenbroek
212*ef8d499eSDavid van Moolenbroek addr_put_inet((struct sockaddr *)addr, &addr_len,
213*ef8d499eSDavid van Moolenbroek netif_ip_addr4(netif), TRUE /*kame*/, 0 /*port*/);
214*ef8d499eSDavid van Moolenbroek }
215*ef8d499eSDavid van Moolenbroek
216*ef8d499eSDavid van Moolenbroek if (mask != NULL) {
217*ef8d499eSDavid van Moolenbroek addr_len = sizeof(*mask);
218*ef8d499eSDavid van Moolenbroek
219*ef8d499eSDavid van Moolenbroek /*
220*ef8d499eSDavid van Moolenbroek * Do not bother using addr_put_netmask() here, as we would
221*ef8d499eSDavid van Moolenbroek * then first have to compute the prefix length..
222*ef8d499eSDavid van Moolenbroek */
223*ef8d499eSDavid van Moolenbroek addr_put_inet((struct sockaddr *)mask, &addr_len,
224*ef8d499eSDavid van Moolenbroek netif_ip_netmask4(netif), TRUE /*kame*/, 0 /*port*/);
225*ef8d499eSDavid van Moolenbroek }
226*ef8d499eSDavid van Moolenbroek
227*ef8d499eSDavid van Moolenbroek if (bcast != NULL) {
228*ef8d499eSDavid van Moolenbroek if (netif->flags & NETIF_FLAG_BROADCAST) {
229*ef8d499eSDavid van Moolenbroek /* Fake a broadcast address. */
230*ef8d499eSDavid van Moolenbroek ipaddr = netif_ip_addr4(netif);
231*ef8d499eSDavid van Moolenbroek netmask = netif_ip_netmask4(netif);
232*ef8d499eSDavid van Moolenbroek
233*ef8d499eSDavid van Moolenbroek ip_addr_set_ip4_u32(&broad,
234*ef8d499eSDavid van Moolenbroek ip_addr_get_ip4_u32(ipaddr) |
235*ef8d499eSDavid van Moolenbroek ~ip_addr_get_ip4_u32(netmask));
236*ef8d499eSDavid van Moolenbroek
237*ef8d499eSDavid van Moolenbroek addr_len = sizeof(*bcast);
238*ef8d499eSDavid van Moolenbroek
239*ef8d499eSDavid van Moolenbroek addr_put_inet((struct sockaddr *)bcast, &addr_len,
240*ef8d499eSDavid van Moolenbroek &broad, TRUE /*kame*/, 0 /*port*/);
241*ef8d499eSDavid van Moolenbroek } else {
242*ef8d499eSDavid van Moolenbroek bcast->sin_len = 0;
243*ef8d499eSDavid van Moolenbroek bcast->sin_family = AF_UNSPEC;
244*ef8d499eSDavid van Moolenbroek }
245*ef8d499eSDavid van Moolenbroek }
246*ef8d499eSDavid van Moolenbroek
247*ef8d499eSDavid van Moolenbroek if (dest != NULL) {
248*ef8d499eSDavid van Moolenbroek /* TODO: dest */
249*ef8d499eSDavid van Moolenbroek dest->sin_len = 0;
250*ef8d499eSDavid van Moolenbroek dest->sin_family = AF_UNSPEC;
251*ef8d499eSDavid van Moolenbroek }
252*ef8d499eSDavid van Moolenbroek
253*ef8d499eSDavid van Moolenbroek return OK;
254*ef8d499eSDavid van Moolenbroek }
255*ef8d499eSDavid van Moolenbroek
256*ef8d499eSDavid van Moolenbroek /*
257*ef8d499eSDavid van Moolenbroek * Obtain NetBSD-style state flags (IN_IFF_) for the given local IPv4 address.
258*ef8d499eSDavid van Moolenbroek * The given number must identify an existing address. Return the flags.
259*ef8d499eSDavid van Moolenbroek */
260*ef8d499eSDavid van Moolenbroek int
ifaddr_v4_get_flags(struct ifdev * ifdev,ifaddr_v4_num_t num)261*ef8d499eSDavid van Moolenbroek ifaddr_v4_get_flags(struct ifdev * ifdev, ifaddr_v4_num_t num)
262*ef8d499eSDavid van Moolenbroek {
263*ef8d499eSDavid van Moolenbroek
264*ef8d499eSDavid van Moolenbroek /* IPv4 per-address flags are not supported yet. */
265*ef8d499eSDavid van Moolenbroek return 0;
266*ef8d499eSDavid van Moolenbroek }
267*ef8d499eSDavid van Moolenbroek
268*ef8d499eSDavid van Moolenbroek /*
269*ef8d499eSDavid van Moolenbroek * Determine whether there should be a local subnet route for the given
270*ef8d499eSDavid van Moolenbroek * assigned IPv4 address, and if so, compute the subnet mask to add. Return
271*ef8d499eSDavid van Moolenbroek * TRUE if a local subnet route should be added, and return the network base
272*ef8d499eSDavid van Moolenbroek * address in 'netbase' and the number of prefix bits in 'prefixp'. Return
273*ef8d499eSDavid van Moolenbroek * FALSE if no subnet route should be added for the assigned address.
274*ef8d499eSDavid van Moolenbroek */
275*ef8d499eSDavid van Moolenbroek static unsigned int
ifaddr_v4_netroute(struct ifdev * ifdev,ifaddr_v4_num_t num,ip_addr_t * netbase,unsigned int * prefixp)276*ef8d499eSDavid van Moolenbroek ifaddr_v4_netroute(struct ifdev * ifdev, ifaddr_v4_num_t num,
277*ef8d499eSDavid van Moolenbroek ip_addr_t * netbase, unsigned int * prefixp)
278*ef8d499eSDavid van Moolenbroek {
279*ef8d499eSDavid van Moolenbroek const ip_addr_t *ipaddr, *netmask;
280*ef8d499eSDavid van Moolenbroek unsigned int prefix;
281*ef8d499eSDavid van Moolenbroek uint32_t val;
282*ef8d499eSDavid van Moolenbroek
283*ef8d499eSDavid van Moolenbroek /* Do not add subnet masks for loopback interfaces. */
284*ef8d499eSDavid van Moolenbroek if (ifdev_is_loopback(ifdev))
285*ef8d499eSDavid van Moolenbroek return FALSE;
286*ef8d499eSDavid van Moolenbroek
287*ef8d499eSDavid van Moolenbroek assert(num == 0);
288*ef8d499eSDavid van Moolenbroek assert(ifdev->ifdev_v4set);
289*ef8d499eSDavid van Moolenbroek
290*ef8d499eSDavid van Moolenbroek ipaddr = netif_ip_addr4(ifdev_get_netif(ifdev));
291*ef8d499eSDavid van Moolenbroek netmask = netif_ip_netmask4(ifdev_get_netif(ifdev));
292*ef8d499eSDavid van Moolenbroek
293*ef8d499eSDavid van Moolenbroek /*
294*ef8d499eSDavid van Moolenbroek * If the subnet is a /32, skip adding a local host route: not only
295*ef8d499eSDavid van Moolenbroek * would it not be useful, it would fail anyway because we currently do
296*ef8d499eSDavid van Moolenbroek * not support adding a host-type route and a full-width net-type route
297*ef8d499eSDavid van Moolenbroek * for the same IP address.
298*ef8d499eSDavid van Moolenbroek */
299*ef8d499eSDavid van Moolenbroek if (ip_addr_get_ip4_u32(netmask) == PP_HTONL(0xffffffffUL))
300*ef8d499eSDavid van Moolenbroek return FALSE;
301*ef8d499eSDavid van Moolenbroek
302*ef8d499eSDavid van Moolenbroek /* Compute the network base address. */
303*ef8d499eSDavid van Moolenbroek ip_addr_set_ip4_u32(netbase,
304*ef8d499eSDavid van Moolenbroek ip_addr_get_ip4_u32(ipaddr) & ip_addr_get_ip4_u32(netmask));
305*ef8d499eSDavid van Moolenbroek
306*ef8d499eSDavid van Moolenbroek /* Find the number of prefix bits of the netmask. TODO: improve.. */
307*ef8d499eSDavid van Moolenbroek val = ntohl(ip_addr_get_ip4_u32(netmask));
308*ef8d499eSDavid van Moolenbroek
309*ef8d499eSDavid van Moolenbroek for (prefix = 0; prefix < IP4_BITS; prefix++)
310*ef8d499eSDavid van Moolenbroek if (!(val & (1 << (IP4_BITS - prefix - 1))))
311*ef8d499eSDavid van Moolenbroek break;
312*ef8d499eSDavid van Moolenbroek
313*ef8d499eSDavid van Moolenbroek *prefixp = prefix;
314*ef8d499eSDavid van Moolenbroek return TRUE;
315*ef8d499eSDavid van Moolenbroek }
316*ef8d499eSDavid van Moolenbroek
317*ef8d499eSDavid van Moolenbroek /*
318*ef8d499eSDavid van Moolenbroek * A local IPv4 address has been added to an interface. The interface is given
319*ef8d499eSDavid van Moolenbroek * as 'ifdev', and the number of the just-added IPv4 address is given as 'num'.
320*ef8d499eSDavid van Moolenbroek * Generate a routing socket message and add local routes as appropriate.
321*ef8d499eSDavid van Moolenbroek */
322*ef8d499eSDavid van Moolenbroek static void
ifaddr_v4_added(struct ifdev * ifdev,ifaddr_v4_num_t num)323*ef8d499eSDavid van Moolenbroek ifaddr_v4_added(struct ifdev * ifdev, ifaddr_v4_num_t num)
324*ef8d499eSDavid van Moolenbroek {
325*ef8d499eSDavid van Moolenbroek const ip_addr_t *ipaddr;
326*ef8d499eSDavid van Moolenbroek ip_addr_t netbase;
327*ef8d499eSDavid van Moolenbroek unsigned int prefix;
328*ef8d499eSDavid van Moolenbroek
329*ef8d499eSDavid van Moolenbroek assert(num == 0);
330*ef8d499eSDavid van Moolenbroek assert(ifdev->ifdev_v4set);
331*ef8d499eSDavid van Moolenbroek
332*ef8d499eSDavid van Moolenbroek /* Report the addition of the interface address. */
333*ef8d499eSDavid van Moolenbroek rtsock_msg_addr_v4(ifdev, RTM_NEWADDR, num);
334*ef8d499eSDavid van Moolenbroek
335*ef8d499eSDavid van Moolenbroek /*
336*ef8d499eSDavid van Moolenbroek * Add the local host route. This will always succeed: for addition,
337*ef8d499eSDavid van Moolenbroek * we just checked with route_can_add(); when updating, we first remove
338*ef8d499eSDavid van Moolenbroek * the exact same route. For now, we forbid users from messing with
339*ef8d499eSDavid van Moolenbroek * RTF_LOCAL routes directly, since nothing good (and a whole lot of
340*ef8d499eSDavid van Moolenbroek * bad) can come out of that, so the routes will not change under us.
341*ef8d499eSDavid van Moolenbroek *
342*ef8d499eSDavid van Moolenbroek * Why are we not using lo0 for this route, like the BSDs do? Because
343*ef8d499eSDavid van Moolenbroek * that approach is not compatible with link-local addresses. Instead,
344*ef8d499eSDavid van Moolenbroek * we intercept outgoing traffic to the local address, and redirect it
345*ef8d499eSDavid van Moolenbroek * over lo0, bypassing routing. If we did not do this, we would never
346*ef8d499eSDavid van Moolenbroek * know the originally intended zone of the outgoing packet. As an
347*ef8d499eSDavid van Moolenbroek * intended side effect, the traffic does show up on lo0 with BPF, just
348*ef8d499eSDavid van Moolenbroek * like on BSDs. Similarly, we do not need to set a gateway here.
349*ef8d499eSDavid van Moolenbroek *
350*ef8d499eSDavid van Moolenbroek * We currently do not use the routing tables for lookups on local
351*ef8d499eSDavid van Moolenbroek * addresses - see ifaddr_v6_map() as to why. If we ever do, that adds
352*ef8d499eSDavid van Moolenbroek * another reason that the interface associated with the route must be
353*ef8d499eSDavid van Moolenbroek * the interface that owns the address (and not, say, lo0).
354*ef8d499eSDavid van Moolenbroek */
355*ef8d499eSDavid van Moolenbroek ipaddr = netif_ip_addr4(ifdev_get_netif(ifdev));
356*ef8d499eSDavid van Moolenbroek
357*ef8d499eSDavid van Moolenbroek (void)route_add(ipaddr, IP4_BITS, NULL /*gateway*/, ifdev,
358*ef8d499eSDavid van Moolenbroek IFADDR_HOST_RTFLAGS, NULL /*rtr*/);
359*ef8d499eSDavid van Moolenbroek
360*ef8d499eSDavid van Moolenbroek /*
361*ef8d499eSDavid van Moolenbroek * Add the local network route, if the rules say that we should. Even
362*ef8d499eSDavid van Moolenbroek * then, adding the route may fail for various reasons, but this route
363*ef8d499eSDavid van Moolenbroek * is not essential and so we ignore failures here.
364*ef8d499eSDavid van Moolenbroek */
365*ef8d499eSDavid van Moolenbroek if (ifaddr_v4_netroute(ifdev, num, &netbase, &prefix))
366*ef8d499eSDavid van Moolenbroek (void)route_add(&netbase, prefix, NULL /*gateway*/, ifdev,
367*ef8d499eSDavid van Moolenbroek IFADDR_NET_RTFLAGS, NULL /*rtr*/);
368*ef8d499eSDavid van Moolenbroek }
369*ef8d499eSDavid van Moolenbroek
370*ef8d499eSDavid van Moolenbroek /*
371*ef8d499eSDavid van Moolenbroek * A particular local IPv4 address is being deleted. See if there is another
372*ef8d499eSDavid van Moolenbroek * local IPv4 address assigned to another interface that should have the same
373*ef8d499eSDavid van Moolenbroek * local subnet route (but didn't, as such duplicate routes can obviously not
374*ef8d499eSDavid van Moolenbroek * be added), and if so, readd the route for that other address.
375*ef8d499eSDavid van Moolenbroek */
376*ef8d499eSDavid van Moolenbroek static void
ifaddr_v4_dupcheck(struct ifdev * oifdev,const ip_addr_t * onetbase,unsigned int oprefix)377*ef8d499eSDavid van Moolenbroek ifaddr_v4_dupcheck(struct ifdev * oifdev, const ip_addr_t * onetbase,
378*ef8d499eSDavid van Moolenbroek unsigned int oprefix)
379*ef8d499eSDavid van Moolenbroek {
380*ef8d499eSDavid van Moolenbroek struct ifdev *ifdev;
381*ef8d499eSDavid van Moolenbroek ip_addr_t netbase;
382*ef8d499eSDavid van Moolenbroek unsigned int prefix;
383*ef8d499eSDavid van Moolenbroek
384*ef8d499eSDavid van Moolenbroek for (ifdev = NULL; (ifdev = ifdev_enum(ifdev)) != NULL; ) {
385*ef8d499eSDavid van Moolenbroek if (ifdev == oifdev || !ifdev->ifdev_v4set)
386*ef8d499eSDavid van Moolenbroek continue;
387*ef8d499eSDavid van Moolenbroek
388*ef8d499eSDavid van Moolenbroek if (ifaddr_v4_netroute(ifdev, (ifaddr_v4_num_t)0, &netbase,
389*ef8d499eSDavid van Moolenbroek &prefix) && prefix == oprefix &&
390*ef8d499eSDavid van Moolenbroek ip_addr_cmp(&netbase, onetbase)) {
391*ef8d499eSDavid van Moolenbroek (void)route_add(&netbase, prefix, NULL /*gateway*/,
392*ef8d499eSDavid van Moolenbroek ifdev, IFADDR_NET_RTFLAGS, NULL /*rtr*/);
393*ef8d499eSDavid van Moolenbroek
394*ef8d499eSDavid van Moolenbroek return;
395*ef8d499eSDavid van Moolenbroek }
396*ef8d499eSDavid van Moolenbroek }
397*ef8d499eSDavid van Moolenbroek }
398*ef8d499eSDavid van Moolenbroek
399*ef8d499eSDavid van Moolenbroek /*
400*ef8d499eSDavid van Moolenbroek * A local IPv4 address is about to be deleted from an interface, or the
401*ef8d499eSDavid van Moolenbroek * interface itself is about to be destroyed. Generate a routing socket
402*ef8d499eSDavid van Moolenbroek * message about this and delete local routes as appropriate. The interface is
403*ef8d499eSDavid van Moolenbroek * given as 'ifdev', and the number of the IPv4 address that is about to be
404*ef8d499eSDavid van Moolenbroek * deleted is given as 'num'.
405*ef8d499eSDavid van Moolenbroek */
406*ef8d499eSDavid van Moolenbroek static void
ifaddr_v4_deleted(struct ifdev * ifdev,ifaddr_v4_num_t num)407*ef8d499eSDavid van Moolenbroek ifaddr_v4_deleted(struct ifdev * ifdev, ifaddr_v4_num_t num)
408*ef8d499eSDavid van Moolenbroek {
409*ef8d499eSDavid van Moolenbroek struct route_entry *route;
410*ef8d499eSDavid van Moolenbroek ip_addr_t netbase;
411*ef8d499eSDavid van Moolenbroek unsigned int prefix;
412*ef8d499eSDavid van Moolenbroek
413*ef8d499eSDavid van Moolenbroek assert(num == 0);
414*ef8d499eSDavid van Moolenbroek assert(ifdev->ifdev_v4set);
415*ef8d499eSDavid van Moolenbroek
416*ef8d499eSDavid van Moolenbroek /* Delete the local network route, if we tried adding it at all. */
417*ef8d499eSDavid van Moolenbroek if (ifaddr_v4_netroute(ifdev, num, &netbase, &prefix) &&
418*ef8d499eSDavid van Moolenbroek (route = route_find(&netbase, prefix,
419*ef8d499eSDavid van Moolenbroek FALSE /*is_host*/)) != NULL &&
420*ef8d499eSDavid van Moolenbroek route_get_flags(route) == IFADDR_NET_RTFLAGS) {
421*ef8d499eSDavid van Moolenbroek route_delete(route, NULL /*rtr*/);
422*ef8d499eSDavid van Moolenbroek
423*ef8d499eSDavid van Moolenbroek /*
424*ef8d499eSDavid van Moolenbroek * Readd the local network route for another interface, if that
425*ef8d499eSDavid van Moolenbroek * interface has a local address on the very same network.
426*ef8d499eSDavid van Moolenbroek */
427*ef8d499eSDavid van Moolenbroek ifaddr_v4_dupcheck(ifdev, &netbase, prefix);
428*ef8d499eSDavid van Moolenbroek }
429*ef8d499eSDavid van Moolenbroek
430*ef8d499eSDavid van Moolenbroek /* Delete the local host route. */
431*ef8d499eSDavid van Moolenbroek if ((route = route_find(netif_ip_addr4(ifdev_get_netif(ifdev)),
432*ef8d499eSDavid van Moolenbroek IP4_BITS, TRUE /*is_host*/)) != NULL)
433*ef8d499eSDavid van Moolenbroek route_delete(route, NULL /*rtr*/);
434*ef8d499eSDavid van Moolenbroek
435*ef8d499eSDavid van Moolenbroek /* Report the deletion of the interface address. */
436*ef8d499eSDavid van Moolenbroek rtsock_msg_addr_v4(ifdev, RTM_DELADDR, num);
437*ef8d499eSDavid van Moolenbroek }
438*ef8d499eSDavid van Moolenbroek
439*ef8d499eSDavid van Moolenbroek /*
440*ef8d499eSDavid van Moolenbroek * Add or update an IPv4 address on an interface. The interface is given as
441*ef8d499eSDavid van Moolenbroek * 'ifdev'. The address to add or update is pointed to by 'addr', which must
442*ef8d499eSDavid van Moolenbroek * always be a pointer to a valid address. For DHCP clients it must be
443*ef8d499eSDavid van Moolenbroek * possible to add the 'any' address (0.0.0.0). The network mask, broadcast
444*ef8d499eSDavid van Moolenbroek * address, and destination address parameters 'mask', 'bcast', and 'dest'
445*ef8d499eSDavid van Moolenbroek * (respectively) may be NULL pointers or pointers to AF_UNSPEC addresses, and
446*ef8d499eSDavid van Moolenbroek * will be disregarded if they are. If 'mask' and/or 'bcast' are NULL when
447*ef8d499eSDavid van Moolenbroek * adding an address, default values will be computed for them. The 'flags'
448*ef8d499eSDavid van Moolenbroek * field may contain NetBSD-style address flags (IN_IFF_). Return OK if the
449*ef8d499eSDavid van Moolenbroek * address was successfully added or updated, or a negative error code if not.
450*ef8d499eSDavid van Moolenbroek */
451*ef8d499eSDavid van Moolenbroek int
ifaddr_v4_add(struct ifdev * ifdev,const struct sockaddr_in * addr,const struct sockaddr_in * mask,const struct sockaddr_in * bcast,const struct sockaddr_in * dest,int flags)452*ef8d499eSDavid van Moolenbroek ifaddr_v4_add(struct ifdev * ifdev, const struct sockaddr_in * addr,
453*ef8d499eSDavid van Moolenbroek const struct sockaddr_in * mask, const struct sockaddr_in * bcast,
454*ef8d499eSDavid van Moolenbroek const struct sockaddr_in * dest, int flags)
455*ef8d499eSDavid van Moolenbroek {
456*ef8d499eSDavid van Moolenbroek ip_addr_t ipaddr, netmask, broad;
457*ef8d499eSDavid van Moolenbroek ip4_addr_t ip4zero;
458*ef8d499eSDavid van Moolenbroek struct netif *netif;
459*ef8d499eSDavid van Moolenbroek unsigned int dummy;
460*ef8d499eSDavid van Moolenbroek uint32_t val;
461*ef8d499eSDavid van Moolenbroek int r;
462*ef8d499eSDavid van Moolenbroek
463*ef8d499eSDavid van Moolenbroek assert(addr != NULL);
464*ef8d499eSDavid van Moolenbroek
465*ef8d499eSDavid van Moolenbroek if ((r = addr_get_inet((const struct sockaddr *)addr, sizeof(*addr),
466*ef8d499eSDavid van Moolenbroek IPADDR_TYPE_V4, &ipaddr, TRUE /*kame*/, NULL /*port*/)) != OK)
467*ef8d499eSDavid van Moolenbroek return r;
468*ef8d499eSDavid van Moolenbroek
469*ef8d499eSDavid van Moolenbroek /* Forbid multicast (class D) and experimental (class E) addresses. */
470*ef8d499eSDavid van Moolenbroek val = ntohl(ip_addr_get_ip4_u32(&ipaddr));
471*ef8d499eSDavid van Moolenbroek
472*ef8d499eSDavid van Moolenbroek if (ip_addr_ismulticast(&ipaddr) || IP_EXPERIMENTAL(val))
473*ef8d499eSDavid van Moolenbroek return EINVAL;
474*ef8d499eSDavid van Moolenbroek
475*ef8d499eSDavid van Moolenbroek if (mask != NULL && mask->sin_family != AF_UNSPEC) {
476*ef8d499eSDavid van Moolenbroek if ((r = addr_get_netmask((const struct sockaddr *)mask,
477*ef8d499eSDavid van Moolenbroek sizeof(*mask), IPADDR_TYPE_V4, &dummy, &netmask)) != OK)
478*ef8d499eSDavid van Moolenbroek return r;
479*ef8d499eSDavid van Moolenbroek } else {
480*ef8d499eSDavid van Moolenbroek /*
481*ef8d499eSDavid van Moolenbroek * Generate a netmask based on IP class. Old, obsolete stuff,
482*ef8d499eSDavid van Moolenbroek * but we can't have no netmask.
483*ef8d499eSDavid van Moolenbroek */
484*ef8d499eSDavid van Moolenbroek if (IN_CLASSA(val))
485*ef8d499eSDavid van Moolenbroek ip_addr_set_ip4_u32(&netmask, PP_HTONL(IN_CLASSA_NET));
486*ef8d499eSDavid van Moolenbroek else if (IN_CLASSB(val))
487*ef8d499eSDavid van Moolenbroek ip_addr_set_ip4_u32(&netmask, PP_HTONL(IN_CLASSB_NET));
488*ef8d499eSDavid van Moolenbroek else if (IN_CLASSC(val))
489*ef8d499eSDavid van Moolenbroek ip_addr_set_ip4_u32(&netmask, PP_HTONL(IN_CLASSC_NET));
490*ef8d499eSDavid van Moolenbroek else /* should not trigger */
491*ef8d499eSDavid van Moolenbroek ip_addr_set_ip4_u32(&netmask, PP_HTONL(IN_CLASSD_NET));
492*ef8d499eSDavid van Moolenbroek }
493*ef8d499eSDavid van Moolenbroek
494*ef8d499eSDavid van Moolenbroek if (bcast != NULL && bcast->sin_family != AF_UNSPEC) {
495*ef8d499eSDavid van Moolenbroek if ((r = addr_get_inet((const struct sockaddr *)bcast,
496*ef8d499eSDavid van Moolenbroek sizeof(*bcast), IPADDR_TYPE_V4, &broad, TRUE /*kame*/,
497*ef8d499eSDavid van Moolenbroek NULL /*port*/)) != OK)
498*ef8d499eSDavid van Moolenbroek return r;
499*ef8d499eSDavid van Moolenbroek
500*ef8d499eSDavid van Moolenbroek /*
501*ef8d499eSDavid van Moolenbroek * lwIP does not allow setting the broadcast address, so we
502*ef8d499eSDavid van Moolenbroek * must ensure that the given address is what lwIP uses anyway.
503*ef8d499eSDavid van Moolenbroek * No need to perform byte order swaps here.
504*ef8d499eSDavid van Moolenbroek */
505*ef8d499eSDavid van Moolenbroek if (ip_addr_get_ip4_u32(&broad) !=
506*ef8d499eSDavid van Moolenbroek (ip_addr_get_ip4_u32(&ipaddr) |
507*ef8d499eSDavid van Moolenbroek ~ip_addr_get_ip4_u32(&netmask)))
508*ef8d499eSDavid van Moolenbroek return EINVAL;
509*ef8d499eSDavid van Moolenbroek }
510*ef8d499eSDavid van Moolenbroek
511*ef8d499eSDavid van Moolenbroek /* TODO: dest (note: may be NULL) */
512*ef8d499eSDavid van Moolenbroek
513*ef8d499eSDavid van Moolenbroek /*
514*ef8d499eSDavid van Moolenbroek * We currently do not support any IPv4 address flags. Even though
515*ef8d499eSDavid van Moolenbroek * supporting them would make maintaining dhcpcd(8) easier, lwIP does
516*ef8d499eSDavid van Moolenbroek * not offers the means to implement them properly.
517*ef8d499eSDavid van Moolenbroek */
518*ef8d499eSDavid van Moolenbroek if (flags != 0)
519*ef8d499eSDavid van Moolenbroek return EINVAL;
520*ef8d499eSDavid van Moolenbroek
521*ef8d499eSDavid van Moolenbroek netif = ifdev_get_netif(ifdev);
522*ef8d499eSDavid van Moolenbroek
523*ef8d499eSDavid van Moolenbroek /* Should we add a new address, or update an existing one? */
524*ef8d499eSDavid van Moolenbroek if (!ifdev->ifdev_v4set ||
525*ef8d499eSDavid van Moolenbroek !ip_addr_cmp(netif_ip_addr4(netif), &ipaddr)) {
526*ef8d499eSDavid van Moolenbroek /*
527*ef8d499eSDavid van Moolenbroek * Add a new address. lwIP supports only one IPv4 address per
528*ef8d499eSDavid van Moolenbroek * netif.
529*ef8d499eSDavid van Moolenbroek */
530*ef8d499eSDavid van Moolenbroek if (ifdev->ifdev_v4set)
531*ef8d499eSDavid van Moolenbroek return ENOBUFS; /* TODO: a better error code */
532*ef8d499eSDavid van Moolenbroek
533*ef8d499eSDavid van Moolenbroek /*
534*ef8d499eSDavid van Moolenbroek * It must be possible to add the address to the routing table,
535*ef8d499eSDavid van Moolenbroek * so make sure that we can add such a route later on. The
536*ef8d499eSDavid van Moolenbroek * error code should be accurate for most real-world cases.
537*ef8d499eSDavid van Moolenbroek */
538*ef8d499eSDavid van Moolenbroek if (!route_can_add(&ipaddr, IP4_BITS, TRUE /*is_host*/))
539*ef8d499eSDavid van Moolenbroek return EEXIST;
540*ef8d499eSDavid van Moolenbroek
541*ef8d499eSDavid van Moolenbroek ip4_addr_set_zero(&ip4zero);
542*ef8d499eSDavid van Moolenbroek
543*ef8d499eSDavid van Moolenbroek netif_set_addr(netif, ip_2_ip4(&ipaddr), ip_2_ip4(&netmask),
544*ef8d499eSDavid van Moolenbroek &ip4zero);
545*ef8d499eSDavid van Moolenbroek
546*ef8d499eSDavid van Moolenbroek ifdev->ifdev_v4set = TRUE;
547*ef8d499eSDavid van Moolenbroek } else {
548*ef8d499eSDavid van Moolenbroek /*
549*ef8d499eSDavid van Moolenbroek * Update an existing address. First report the address as
550*ef8d499eSDavid van Moolenbroek * deleted. Do not actually delete the address in netif,
551*ef8d499eSDavid van Moolenbroek * because that would cause problems with its changing IP
552*ef8d499eSDavid van Moolenbroek * addresses on existing sockets.
553*ef8d499eSDavid van Moolenbroek */
554*ef8d499eSDavid van Moolenbroek ifaddr_v4_deleted(ifdev, (ifaddr_v4_num_t)0);
555*ef8d499eSDavid van Moolenbroek
556*ef8d499eSDavid van Moolenbroek /* Update the one part that may have actually changed. */
557*ef8d499eSDavid van Moolenbroek netif_set_netmask(netif, ip_2_ip4(&netmask));
558*ef8d499eSDavid van Moolenbroek }
559*ef8d499eSDavid van Moolenbroek
560*ef8d499eSDavid van Moolenbroek /* In both cases, we now need to report the address as added. */
561*ef8d499eSDavid van Moolenbroek ifaddr_v4_added(ifdev, (ifaddr_v4_num_t)0);
562*ef8d499eSDavid van Moolenbroek
563*ef8d499eSDavid van Moolenbroek return OK;
564*ef8d499eSDavid van Moolenbroek }
565*ef8d499eSDavid van Moolenbroek
566*ef8d499eSDavid van Moolenbroek /*
567*ef8d499eSDavid van Moolenbroek * Delete an IPv4 address from an interface. The given address number 'num'
568*ef8d499eSDavid van Moolenbroek * must have been obtained from ifaddr_v4_find() or ifaddr_v4_enum() on the
569*ef8d499eSDavid van Moolenbroek * same interface just before. This function always succeeds.
570*ef8d499eSDavid van Moolenbroek */
571*ef8d499eSDavid van Moolenbroek void
ifaddr_v4_del(struct ifdev * ifdev,ifaddr_v4_num_t num)572*ef8d499eSDavid van Moolenbroek ifaddr_v4_del(struct ifdev * ifdev, ifaddr_v4_num_t num)
573*ef8d499eSDavid van Moolenbroek {
574*ef8d499eSDavid van Moolenbroek ip4_addr_t ip4zero;
575*ef8d499eSDavid van Moolenbroek
576*ef8d499eSDavid van Moolenbroek assert(num == 0);
577*ef8d499eSDavid van Moolenbroek assert(ifdev->ifdev_v4set);
578*ef8d499eSDavid van Moolenbroek
579*ef8d499eSDavid van Moolenbroek /*
580*ef8d499eSDavid van Moolenbroek * Report the address as deleted. Always do this first, because the
581*ef8d499eSDavid van Moolenbroek * reporting requires that the address is still there.
582*ef8d499eSDavid van Moolenbroek */
583*ef8d499eSDavid van Moolenbroek ifaddr_v4_deleted(ifdev, num);
584*ef8d499eSDavid van Moolenbroek
585*ef8d499eSDavid van Moolenbroek /* Then actually delete the address. */
586*ef8d499eSDavid van Moolenbroek ip4_addr_set_zero(&ip4zero);
587*ef8d499eSDavid van Moolenbroek
588*ef8d499eSDavid van Moolenbroek netif_set_addr(ifdev_get_netif(ifdev), &ip4zero, &ip4zero, &ip4zero);
589*ef8d499eSDavid van Moolenbroek
590*ef8d499eSDavid van Moolenbroek ifdev->ifdev_v4set = FALSE;
591*ef8d499eSDavid van Moolenbroek }
592*ef8d499eSDavid van Moolenbroek
593*ef8d499eSDavid van Moolenbroek /*
594*ef8d499eSDavid van Moolenbroek * Announce all IPv4 addresses associated with the given interface as deleted,
595*ef8d499eSDavid van Moolenbroek * Used (only) right before the interface is destroyed.
596*ef8d499eSDavid van Moolenbroek */
597*ef8d499eSDavid van Moolenbroek void
ifaddr_v4_clear(struct ifdev * ifdev)598*ef8d499eSDavid van Moolenbroek ifaddr_v4_clear(struct ifdev * ifdev)
599*ef8d499eSDavid van Moolenbroek {
600*ef8d499eSDavid van Moolenbroek
601*ef8d499eSDavid van Moolenbroek if (ifdev->ifdev_v4set)
602*ef8d499eSDavid van Moolenbroek ifaddr_v4_deleted(ifdev, (ifaddr_v4_num_t)0);
603*ef8d499eSDavid van Moolenbroek }
604*ef8d499eSDavid van Moolenbroek
605*ef8d499eSDavid van Moolenbroek /*
606*ef8d499eSDavid van Moolenbroek * Return the first interface device that owns the given IPv4 address, or NULL
607*ef8d499eSDavid van Moolenbroek * if it is not a valid local IPv4 address.
608*ef8d499eSDavid van Moolenbroek */
609*ef8d499eSDavid van Moolenbroek struct ifdev *
ifaddr_v4_map_by_addr(const ip4_addr_t * ip4addr)610*ef8d499eSDavid van Moolenbroek ifaddr_v4_map_by_addr(const ip4_addr_t * ip4addr)
611*ef8d499eSDavid van Moolenbroek {
612*ef8d499eSDavid van Moolenbroek struct ifdev *ifdev;
613*ef8d499eSDavid van Moolenbroek
614*ef8d499eSDavid van Moolenbroek /*
615*ef8d499eSDavid van Moolenbroek * It would be nice to be able to do a route lookup on an RTF_LOCAL
616*ef8d499eSDavid van Moolenbroek * entry here, but we do not do this for IPv6 either - see the comment
617*ef8d499eSDavid van Moolenbroek * in ifaddr_v6_map() - and it is much less needed here, because each
618*ef8d499eSDavid van Moolenbroek * interface has at most one IPv4 address.
619*ef8d499eSDavid van Moolenbroek */
620*ef8d499eSDavid van Moolenbroek for (ifdev = NULL; (ifdev = ifdev_enum(ifdev)) != NULL; ) {
621*ef8d499eSDavid van Moolenbroek if (ifdev->ifdev_v4set &&
622*ef8d499eSDavid van Moolenbroek ip4_addr_cmp(netif_ip4_addr(ifdev_get_netif(ifdev)),
623*ef8d499eSDavid van Moolenbroek ip4addr))
624*ef8d499eSDavid van Moolenbroek return ifdev;
625*ef8d499eSDavid van Moolenbroek }
626*ef8d499eSDavid van Moolenbroek
627*ef8d499eSDavid van Moolenbroek return NULL;
628*ef8d499eSDavid van Moolenbroek }
629*ef8d499eSDavid van Moolenbroek
630*ef8d499eSDavid van Moolenbroek /*
631*ef8d499eSDavid van Moolenbroek * Return the first interface device for which the given IPv4 address is on a
632*ef8d499eSDavid van Moolenbroek * configured local subnet, or NULL if no match was found.
633*ef8d499eSDavid van Moolenbroek */
634*ef8d499eSDavid van Moolenbroek static struct ifdev *
ifaddr_v4_map_by_subnet(const ip4_addr_t * ip4addr)635*ef8d499eSDavid van Moolenbroek ifaddr_v4_map_by_subnet(const ip4_addr_t * ip4addr)
636*ef8d499eSDavid van Moolenbroek {
637*ef8d499eSDavid van Moolenbroek struct ifdev *ifdev;
638*ef8d499eSDavid van Moolenbroek struct netif *netif;
639*ef8d499eSDavid van Moolenbroek uint32_t addr1, addr2, mask;
640*ef8d499eSDavid van Moolenbroek
641*ef8d499eSDavid van Moolenbroek addr1 = ip4_addr_get_u32(ip4addr);
642*ef8d499eSDavid van Moolenbroek
643*ef8d499eSDavid van Moolenbroek /*
644*ef8d499eSDavid van Moolenbroek * Here, we must never do a route lookup, because this routine is used
645*ef8d499eSDavid van Moolenbroek * for SO_DONTROUTE/MSG_DONTROUTE.
646*ef8d499eSDavid van Moolenbroek */
647*ef8d499eSDavid van Moolenbroek for (ifdev = NULL; (ifdev = ifdev_enum(ifdev)) != NULL; ) {
648*ef8d499eSDavid van Moolenbroek if (!ifdev->ifdev_v4set)
649*ef8d499eSDavid van Moolenbroek continue;
650*ef8d499eSDavid van Moolenbroek
651*ef8d499eSDavid van Moolenbroek netif = ifdev_get_netif(ifdev);
652*ef8d499eSDavid van Moolenbroek
653*ef8d499eSDavid van Moolenbroek addr2 = ip4_addr_get_u32(netif_ip4_addr(netif));
654*ef8d499eSDavid van Moolenbroek mask = ip4_addr_get_u32(netif_ip4_netmask(netif));
655*ef8d499eSDavid van Moolenbroek
656*ef8d499eSDavid van Moolenbroek if ((addr1 & mask) == (addr2 & mask))
657*ef8d499eSDavid van Moolenbroek return ifdev;
658*ef8d499eSDavid van Moolenbroek }
659*ef8d499eSDavid van Moolenbroek
660*ef8d499eSDavid van Moolenbroek return NULL;
661*ef8d499eSDavid van Moolenbroek }
662*ef8d499eSDavid van Moolenbroek
663*ef8d499eSDavid van Moolenbroek /*
664*ef8d499eSDavid van Moolenbroek * Return TRUE if the given local IPv6 interface address is valid (= preferred
665*ef8d499eSDavid van Moolenbroek * or deprecated), or FALSE if it is not (= tentative or duplicated). The
666*ef8d499eSDavid van Moolenbroek * address slot must be in use, that is, it must not be free (= invalid).
667*ef8d499eSDavid van Moolenbroek */
668*ef8d499eSDavid van Moolenbroek static int
ifaddr_v6_isvalid(struct ifdev * ifdev,ifaddr_v6_num_t num)669*ef8d499eSDavid van Moolenbroek ifaddr_v6_isvalid(struct ifdev * ifdev, ifaddr_v6_num_t num)
670*ef8d499eSDavid van Moolenbroek {
671*ef8d499eSDavid van Moolenbroek int state;
672*ef8d499eSDavid van Moolenbroek
673*ef8d499eSDavid van Moolenbroek state = ifdev->ifdev_v6state[num];
674*ef8d499eSDavid van Moolenbroek
675*ef8d499eSDavid van Moolenbroek /* Note that 'valid' and 'invalid' are not each other's inverse! */
676*ef8d499eSDavid van Moolenbroek assert(!ip6_addr_isinvalid(state));
677*ef8d499eSDavid van Moolenbroek
678*ef8d499eSDavid van Moolenbroek return ip6_addr_isvalid(state);
679*ef8d499eSDavid van Moolenbroek }
680*ef8d499eSDavid van Moolenbroek
681*ef8d499eSDavid van Moolenbroek /*
682*ef8d499eSDavid van Moolenbroek * Find an IPv6 address assigned to the given interface that matches the given
683*ef8d499eSDavid van Moolenbroek * IPv6 address. Return TRUE if a match was found, with its number stored in
684*ef8d499eSDavid van Moolenbroek * 'nump'. Return FALSE if the address is not assigned to the interface.
685*ef8d499eSDavid van Moolenbroek */
686*ef8d499eSDavid van Moolenbroek static int
ifaddr_v6_match(struct ifdev * ifdev,const ip_addr_t * ipaddr,ifaddr_v6_num_t * nump)687*ef8d499eSDavid van Moolenbroek ifaddr_v6_match(struct ifdev * ifdev, const ip_addr_t * ipaddr,
688*ef8d499eSDavid van Moolenbroek ifaddr_v6_num_t * nump)
689*ef8d499eSDavid van Moolenbroek {
690*ef8d499eSDavid van Moolenbroek int8_t i;
691*ef8d499eSDavid van Moolenbroek
692*ef8d499eSDavid van Moolenbroek assert(IP_IS_V6(ipaddr));
693*ef8d499eSDavid van Moolenbroek
694*ef8d499eSDavid van Moolenbroek i = netif_get_ip6_addr_match(ifdev_get_netif(ifdev), ip_2_ip6(ipaddr));
695*ef8d499eSDavid van Moolenbroek if (i < 0)
696*ef8d499eSDavid van Moolenbroek return FALSE;
697*ef8d499eSDavid van Moolenbroek
698*ef8d499eSDavid van Moolenbroek *nump = i;
699*ef8d499eSDavid van Moolenbroek return TRUE;
700*ef8d499eSDavid van Moolenbroek }
701*ef8d499eSDavid van Moolenbroek
702*ef8d499eSDavid van Moolenbroek /*
703*ef8d499eSDavid van Moolenbroek * Find an IPv6 address locally assigned to a interface. The IPv6 address is
704*ef8d499eSDavid van Moolenbroek * given as 'addr6', and must use KAME-style embedding for zones. The
705*ef8d499eSDavid van Moolenbroek * interface is given as 'ifdev'. On success, return OK, with the IPv6 address
706*ef8d499eSDavid van Moolenbroek * number stored in 'num'. On failure, return a negative error code. This
707*ef8d499eSDavid van Moolenbroek * function also returns tentative and duplicated addresses.
708*ef8d499eSDavid van Moolenbroek */
709*ef8d499eSDavid van Moolenbroek int
ifaddr_v6_find(struct ifdev * ifdev,const struct sockaddr_in6 * addr6,ifaddr_v6_num_t * nump)710*ef8d499eSDavid van Moolenbroek ifaddr_v6_find(struct ifdev * ifdev, const struct sockaddr_in6 * addr6,
711*ef8d499eSDavid van Moolenbroek ifaddr_v6_num_t * nump)
712*ef8d499eSDavid van Moolenbroek {
713*ef8d499eSDavid van Moolenbroek ip_addr_t ipaddr;
714*ef8d499eSDavid van Moolenbroek int r;
715*ef8d499eSDavid van Moolenbroek
716*ef8d499eSDavid van Moolenbroek if ((r = addr_get_inet((const struct sockaddr *)addr6, sizeof(*addr6),
717*ef8d499eSDavid van Moolenbroek IPADDR_TYPE_V6, &ipaddr, TRUE /*kame*/, NULL /*port*/)) != OK)
718*ef8d499eSDavid van Moolenbroek return r;
719*ef8d499eSDavid van Moolenbroek
720*ef8d499eSDavid van Moolenbroek if (ip6_addr_has_zone(ip_2_ip6(&ipaddr)) &&
721*ef8d499eSDavid van Moolenbroek ip6_addr_zone(ip_2_ip6(&ipaddr)) != ifdev_get_index(ifdev))
722*ef8d499eSDavid van Moolenbroek return EADDRNOTAVAIL;
723*ef8d499eSDavid van Moolenbroek
724*ef8d499eSDavid van Moolenbroek if (!ifaddr_v6_match(ifdev, &ipaddr, nump))
725*ef8d499eSDavid van Moolenbroek return EADDRNOTAVAIL;
726*ef8d499eSDavid van Moolenbroek
727*ef8d499eSDavid van Moolenbroek return OK;
728*ef8d499eSDavid van Moolenbroek }
729*ef8d499eSDavid van Moolenbroek
730*ef8d499eSDavid van Moolenbroek /*
731*ef8d499eSDavid van Moolenbroek * Enumerate IPv6 addresses locally assigned to the given interface 'ifdev'.
732*ef8d499eSDavid van Moolenbroek * The caller should set 'nump' to 0 initially, and increase it by one between
733*ef8d499eSDavid van Moolenbroek * a successful call and the next enumeration call. Return TRUE on success,
734*ef8d499eSDavid van Moolenbroek * meaning that starting from the given value of 'nump' there is at least one
735*ef8d499eSDavid van Moolenbroek * IPv6 address, of which the number is stored in 'nump' on return. Return
736*ef8d499eSDavid van Moolenbroek * FALSE if there are no more IPv6 addresses locally assigned to the interface.
737*ef8d499eSDavid van Moolenbroek * This function also returns tentative and duplicated address entries.
738*ef8d499eSDavid van Moolenbroek */
739*ef8d499eSDavid van Moolenbroek int
ifaddr_v6_enum(struct ifdev * ifdev,ifaddr_v6_num_t * nump)740*ef8d499eSDavid van Moolenbroek ifaddr_v6_enum(struct ifdev * ifdev, ifaddr_v6_num_t * nump)
741*ef8d499eSDavid van Moolenbroek {
742*ef8d499eSDavid van Moolenbroek ifaddr_v6_num_t num;
743*ef8d499eSDavid van Moolenbroek
744*ef8d499eSDavid van Moolenbroek for (num = *nump; num < LWIP_IPV6_NUM_ADDRESSES; num++) {
745*ef8d499eSDavid van Moolenbroek if (!ip6_addr_isinvalid(ifdev->ifdev_v6state[num])) {
746*ef8d499eSDavid van Moolenbroek *nump = num;
747*ef8d499eSDavid van Moolenbroek return TRUE;
748*ef8d499eSDavid van Moolenbroek }
749*ef8d499eSDavid van Moolenbroek }
750*ef8d499eSDavid van Moolenbroek
751*ef8d499eSDavid van Moolenbroek return FALSE;
752*ef8d499eSDavid van Moolenbroek }
753*ef8d499eSDavid van Moolenbroek
754*ef8d499eSDavid van Moolenbroek /*
755*ef8d499eSDavid van Moolenbroek * Obtain information about the IPv6 address 'num' assigned to the interface
756*ef8d499eSDavid van Moolenbroek * 'ifdev'. Store the IPv6 address in 'addr6', the network mask in 'mask6',
757*ef8d499eSDavid van Moolenbroek * and the destination address in 'dest6'. Each of these pointers may be NULL.
758*ef8d499eSDavid van Moolenbroek * The returned addresses use KAME-style embedding for zones. This function
759*ef8d499eSDavid van Moolenbroek * also returns tentative and duplicated addresses. It always succeeds.
760*ef8d499eSDavid van Moolenbroek */
761*ef8d499eSDavid van Moolenbroek void
ifaddr_v6_get(struct ifdev * ifdev,ifaddr_v6_num_t num,struct sockaddr_in6 * addr6,struct sockaddr_in6 * mask6,struct sockaddr_in6 * dest6)762*ef8d499eSDavid van Moolenbroek ifaddr_v6_get(struct ifdev * ifdev, ifaddr_v6_num_t num,
763*ef8d499eSDavid van Moolenbroek struct sockaddr_in6 * addr6, struct sockaddr_in6 * mask6,
764*ef8d499eSDavid van Moolenbroek struct sockaddr_in6 * dest6)
765*ef8d499eSDavid van Moolenbroek {
766*ef8d499eSDavid van Moolenbroek struct netif *netif;
767*ef8d499eSDavid van Moolenbroek socklen_t addr_len;
768*ef8d499eSDavid van Moolenbroek
769*ef8d499eSDavid van Moolenbroek /*
770*ef8d499eSDavid van Moolenbroek * Due to route message generation upon address addition and deletion,
771*ef8d499eSDavid van Moolenbroek * either the ifdev_v6state or the netif state may not yet have been
772*ef8d499eSDavid van Moolenbroek * updated here.
773*ef8d499eSDavid van Moolenbroek */
774*ef8d499eSDavid van Moolenbroek assert(!ip6_addr_isinvalid(ifdev->ifdev_v6state[num]) ||
775*ef8d499eSDavid van Moolenbroek !ip6_addr_isinvalid(netif_ip6_addr_state(ifdev_get_netif(ifdev),
776*ef8d499eSDavid van Moolenbroek (int)num)));
777*ef8d499eSDavid van Moolenbroek
778*ef8d499eSDavid van Moolenbroek netif = ifdev_get_netif(ifdev);
779*ef8d499eSDavid van Moolenbroek
780*ef8d499eSDavid van Moolenbroek if (addr6 != NULL) {
781*ef8d499eSDavid van Moolenbroek addr_len = sizeof(*addr6);
782*ef8d499eSDavid van Moolenbroek
783*ef8d499eSDavid van Moolenbroek (void)addr_put_inet((struct sockaddr *)addr6, &addr_len,
784*ef8d499eSDavid van Moolenbroek netif_ip_addr6(netif, (int)num), TRUE /*kame*/,
785*ef8d499eSDavid van Moolenbroek 0 /*port*/);
786*ef8d499eSDavid van Moolenbroek }
787*ef8d499eSDavid van Moolenbroek
788*ef8d499eSDavid van Moolenbroek if (mask6 != NULL) {
789*ef8d499eSDavid van Moolenbroek addr_len = sizeof(*mask6);
790*ef8d499eSDavid van Moolenbroek
791*ef8d499eSDavid van Moolenbroek addr_put_netmask((struct sockaddr *)mask6, &addr_len,
792*ef8d499eSDavid van Moolenbroek IPADDR_TYPE_V6, ifdev->ifdev_v6prefix[num]);
793*ef8d499eSDavid van Moolenbroek }
794*ef8d499eSDavid van Moolenbroek
795*ef8d499eSDavid van Moolenbroek if (dest6 != NULL) {
796*ef8d499eSDavid van Moolenbroek /* TODO: dest6 */
797*ef8d499eSDavid van Moolenbroek dest6->sin6_len = 0;
798*ef8d499eSDavid van Moolenbroek dest6->sin6_family = AF_UNSPEC;
799*ef8d499eSDavid van Moolenbroek }
800*ef8d499eSDavid van Moolenbroek }
801*ef8d499eSDavid van Moolenbroek
802*ef8d499eSDavid van Moolenbroek /*
803*ef8d499eSDavid van Moolenbroek * Obtain NetBSD-style state flags (IN6_IFF_) for the given local IPv6 address.
804*ef8d499eSDavid van Moolenbroek * The given number must identify an existing address. Return the flags.
805*ef8d499eSDavid van Moolenbroek */
806*ef8d499eSDavid van Moolenbroek int
ifaddr_v6_get_flags(struct ifdev * ifdev,ifaddr_v6_num_t num)807*ef8d499eSDavid van Moolenbroek ifaddr_v6_get_flags(struct ifdev * ifdev, ifaddr_v6_num_t num)
808*ef8d499eSDavid van Moolenbroek {
809*ef8d499eSDavid van Moolenbroek int state, flags;
810*ef8d499eSDavid van Moolenbroek
811*ef8d499eSDavid van Moolenbroek state = ifdev->ifdev_v6state[num];
812*ef8d499eSDavid van Moolenbroek
813*ef8d499eSDavid van Moolenbroek assert(!ip6_addr_isinvalid(state));
814*ef8d499eSDavid van Moolenbroek
815*ef8d499eSDavid van Moolenbroek flags = 0;
816*ef8d499eSDavid van Moolenbroek if (ip6_addr_isduplicated(state))
817*ef8d499eSDavid van Moolenbroek flags |= IN6_IFF_DUPLICATED;
818*ef8d499eSDavid van Moolenbroek if (ip6_addr_istentative(state))
819*ef8d499eSDavid van Moolenbroek flags |= IN6_IFF_TENTATIVE;
820*ef8d499eSDavid van Moolenbroek if (ip6_addr_isdeprecated(state))
821*ef8d499eSDavid van Moolenbroek flags |= IN6_IFF_DEPRECATED;
822*ef8d499eSDavid van Moolenbroek if (ifdev->ifdev_v6flags[num] & IFADDR_V6F_AUTOCONF)
823*ef8d499eSDavid van Moolenbroek flags |= IN6_IFF_AUTOCONF;
824*ef8d499eSDavid van Moolenbroek if (ifdev->ifdev_v6flags[num] & IFADDR_V6F_TEMPORARY)
825*ef8d499eSDavid van Moolenbroek flags |= IN6_IFF_TEMPORARY;
826*ef8d499eSDavid van Moolenbroek
827*ef8d499eSDavid van Moolenbroek return flags;
828*ef8d499eSDavid van Moolenbroek }
829*ef8d499eSDavid van Moolenbroek
830*ef8d499eSDavid van Moolenbroek /*
831*ef8d499eSDavid van Moolenbroek * Obtain lifetime information about the given local IPv6 address. The given
832*ef8d499eSDavid van Moolenbroek * 'lifetime' structure is filled as a result. This function always succeeds.
833*ef8d499eSDavid van Moolenbroek */
834*ef8d499eSDavid van Moolenbroek void
ifaddr_v6_get_lifetime(struct ifdev * ifdev,ifaddr_v6_num_t num,struct in6_addrlifetime * lifetime)835*ef8d499eSDavid van Moolenbroek ifaddr_v6_get_lifetime(struct ifdev * ifdev, ifaddr_v6_num_t num,
836*ef8d499eSDavid van Moolenbroek struct in6_addrlifetime * lifetime)
837*ef8d499eSDavid van Moolenbroek {
838*ef8d499eSDavid van Moolenbroek struct netif *netif;
839*ef8d499eSDavid van Moolenbroek uint32_t valid_life, pref_life;
840*ef8d499eSDavid van Moolenbroek time_t now;
841*ef8d499eSDavid van Moolenbroek
842*ef8d499eSDavid van Moolenbroek assert(!ip6_addr_isinvalid(ifdev->ifdev_v6state[num]));
843*ef8d499eSDavid van Moolenbroek
844*ef8d499eSDavid van Moolenbroek netif = ifdev_get_netif(ifdev);
845*ef8d499eSDavid van Moolenbroek
846*ef8d499eSDavid van Moolenbroek valid_life = netif_ip6_addr_valid_life(netif, (int)num);
847*ef8d499eSDavid van Moolenbroek pref_life = netif_ip6_addr_pref_life(netif, (int)num);
848*ef8d499eSDavid van Moolenbroek
849*ef8d499eSDavid van Moolenbroek /*
850*ef8d499eSDavid van Moolenbroek * Represent 'static' as 'infinite' to userland. This applies only to
851*ef8d499eSDavid van Moolenbroek * link-local addresses, which do not have lifetimes at all.
852*ef8d499eSDavid van Moolenbroek */
853*ef8d499eSDavid van Moolenbroek if (ip6_addr_life_isstatic(valid_life)) {
854*ef8d499eSDavid van Moolenbroek valid_life = IP6_ADDR_LIFE_INFINITE;
855*ef8d499eSDavid van Moolenbroek pref_life = IP6_ADDR_LIFE_INFINITE;
856*ef8d499eSDavid van Moolenbroek }
857*ef8d499eSDavid van Moolenbroek
858*ef8d499eSDavid van Moolenbroek now = clock_time(NULL);
859*ef8d499eSDavid van Moolenbroek
860*ef8d499eSDavid van Moolenbroek /*
861*ef8d499eSDavid van Moolenbroek * TODO: the _vltime and _pltime values filled in here are not correct.
862*ef8d499eSDavid van Moolenbroek * They should be set to the originally assigned values rather than the
863*ef8d499eSDavid van Moolenbroek * current ones. Getting this right would mean we'd have to save the
864*ef8d499eSDavid van Moolenbroek * original values. So far it does not look like userland needs that..
865*ef8d499eSDavid van Moolenbroek */
866*ef8d499eSDavid van Moolenbroek memset(lifetime, 0, sizeof(*lifetime));
867*ef8d499eSDavid van Moolenbroek lifetime->ia6t_vltime = valid_life;
868*ef8d499eSDavid van Moolenbroek lifetime->ia6t_pltime = pref_life;
869*ef8d499eSDavid van Moolenbroek if (!ip6_addr_life_isinfinite(valid_life))
870*ef8d499eSDavid van Moolenbroek lifetime->ia6t_expire = now + valid_life;
871*ef8d499eSDavid van Moolenbroek if (!ip6_addr_life_isinfinite(pref_life))
872*ef8d499eSDavid van Moolenbroek lifetime->ia6t_preferred = now + pref_life;
873*ef8d499eSDavid van Moolenbroek }
874*ef8d499eSDavid van Moolenbroek
875*ef8d499eSDavid van Moolenbroek /*
876*ef8d499eSDavid van Moolenbroek * Determine whether there should be a local subnet route for the given
877*ef8d499eSDavid van Moolenbroek * assigned IPv6 address, and if so, compute the subnet mask to add. Return
878*ef8d499eSDavid van Moolenbroek * TRUE if a local subnet route should be added, and return the network base
879*ef8d499eSDavid van Moolenbroek * address in 'netbase' and the number of prefix bits in 'prefixp'. Return
880*ef8d499eSDavid van Moolenbroek * FALSE if no subnet route should be added for the assigned address.
881*ef8d499eSDavid van Moolenbroek */
882*ef8d499eSDavid van Moolenbroek static unsigned int
ifaddr_v6_netroute(struct ifdev * ifdev,ifaddr_v6_num_t num,ip_addr_t * netbase,unsigned int * prefixp)883*ef8d499eSDavid van Moolenbroek ifaddr_v6_netroute(struct ifdev * ifdev, ifaddr_v6_num_t num,
884*ef8d499eSDavid van Moolenbroek ip_addr_t * netbase, unsigned int * prefixp)
885*ef8d499eSDavid van Moolenbroek {
886*ef8d499eSDavid van Moolenbroek const ip_addr_t *ipaddr;
887*ef8d499eSDavid van Moolenbroek
888*ef8d499eSDavid van Moolenbroek ipaddr = netif_ip_addr6(ifdev_get_netif(ifdev), (int)num);
889*ef8d499eSDavid van Moolenbroek
890*ef8d499eSDavid van Moolenbroek /*
891*ef8d499eSDavid van Moolenbroek * A local network route should be added only if all of the following
892*ef8d499eSDavid van Moolenbroek * conditions are met:
893*ef8d499eSDavid van Moolenbroek *
894*ef8d499eSDavid van Moolenbroek * 1) The address is not auto-configured. Autoconfigured addresses do
895*ef8d499eSDavid van Moolenbroek * not have an implied subnet, as explained in RFC 5942.
896*ef8d499eSDavid van Moolenbroek * Consistency with respect to subnet routes is why we do not allow
897*ef8d499eSDavid van Moolenbroek * changing the AUTOCONF flag after an address has been added.
898*ef8d499eSDavid van Moolenbroek * 2) The subnet assignment is not a /128 prefix. Not only would such
899*ef8d499eSDavid van Moolenbroek * a route not be useful, adding it would fail anyway because we
900*ef8d499eSDavid van Moolenbroek * currently do not support adding a host-type route and a
901*ef8d499eSDavid van Moolenbroek * full-width net-type route for the same IP address.
902*ef8d499eSDavid van Moolenbroek * 3) If the interface is a loopback device, the address is not a link-
903*ef8d499eSDavid van Moolenbroek * local address. This appears to be what NetBSD does, but
904*ef8d499eSDavid van Moolenbroek * additional loopback-related exceptions may be needed here.
905*ef8d499eSDavid van Moolenbroek */
906*ef8d499eSDavid van Moolenbroek if ((ifdev->ifdev_v6flags[num] & IFADDR_V6F_AUTOCONF) ||
907*ef8d499eSDavid van Moolenbroek ifdev->ifdev_v6prefix[num] == IP6_BITS ||
908*ef8d499eSDavid van Moolenbroek (ifdev_is_loopback(ifdev) &&
909*ef8d499eSDavid van Moolenbroek ip6_addr_islinklocal(ip_2_ip6(ipaddr))))
910*ef8d499eSDavid van Moolenbroek return FALSE;
911*ef8d499eSDavid van Moolenbroek
912*ef8d499eSDavid van Moolenbroek addr_normalize(netbase, ipaddr, ifdev->ifdev_v6prefix[num]);
913*ef8d499eSDavid van Moolenbroek
914*ef8d499eSDavid van Moolenbroek *prefixp = ifdev->ifdev_v6prefix[num];
915*ef8d499eSDavid van Moolenbroek return TRUE;
916*ef8d499eSDavid van Moolenbroek }
917*ef8d499eSDavid van Moolenbroek
918*ef8d499eSDavid van Moolenbroek /*
919*ef8d499eSDavid van Moolenbroek * A local IPv6 has become valid (preferred or deprecated) after previously
920*ef8d499eSDavid van Moolenbroek * being invalid (tentative, duplicated, or free). Report the addition of the
921*ef8d499eSDavid van Moolenbroek * now-usable address, and add appropriate routes to the IPv6 routing table.
922*ef8d499eSDavid van Moolenbroek *
923*ef8d499eSDavid van Moolenbroek * This function is *not* called immediately when an address is added, but
924*ef8d499eSDavid van Moolenbroek * rather when the address becomes valid (meaning it is no longer tentative,
925*ef8d499eSDavid van Moolenbroek * and thus supposedly collision-free). For that reason, unlike for IPv4, this
926*ef8d499eSDavid van Moolenbroek * function is only ever called indirectly, through the netif status callback.
927*ef8d499eSDavid van Moolenbroek */
928*ef8d499eSDavid van Moolenbroek static void
ifaddr_v6_added(struct ifdev * ifdev,ifaddr_v6_num_t num)929*ef8d499eSDavid van Moolenbroek ifaddr_v6_added(struct ifdev * ifdev, ifaddr_v6_num_t num)
930*ef8d499eSDavid van Moolenbroek {
931*ef8d499eSDavid van Moolenbroek const ip_addr_t *ipaddr;
932*ef8d499eSDavid van Moolenbroek ip_addr_t base;
933*ef8d499eSDavid van Moolenbroek ip6_addr_t *base6;
934*ef8d499eSDavid van Moolenbroek unsigned int prefix;
935*ef8d499eSDavid van Moolenbroek
936*ef8d499eSDavid van Moolenbroek /* Check the netif as ifdev_v6state is not yet updated here. */
937*ef8d499eSDavid van Moolenbroek assert(!ip6_addr_isinvalid(netif_ip6_addr_state(ifdev_get_netif(ifdev),
938*ef8d499eSDavid van Moolenbroek (int)num)));
939*ef8d499eSDavid van Moolenbroek
940*ef8d499eSDavid van Moolenbroek /* Report the addition of the interface address. */
941*ef8d499eSDavid van Moolenbroek rtsock_msg_addr_v6(ifdev, RTM_NEWADDR, num);
942*ef8d499eSDavid van Moolenbroek
943*ef8d499eSDavid van Moolenbroek /*
944*ef8d499eSDavid van Moolenbroek * Add the local host route. This will always succeed. See the IPv4
945*ef8d499eSDavid van Moolenbroek * version of this code for more information.
946*ef8d499eSDavid van Moolenbroek */
947*ef8d499eSDavid van Moolenbroek ipaddr = netif_ip_addr6(ifdev_get_netif(ifdev), (int)num);
948*ef8d499eSDavid van Moolenbroek
949*ef8d499eSDavid van Moolenbroek (void)route_add(ipaddr, IP6_BITS, NULL /*gateway*/, ifdev,
950*ef8d499eSDavid van Moolenbroek IFADDR_HOST_RTFLAGS, NULL /*rtr*/);
951*ef8d499eSDavid van Moolenbroek
952*ef8d499eSDavid van Moolenbroek /*
953*ef8d499eSDavid van Moolenbroek * Add the local network route, if the rules say that we should. Even
954*ef8d499eSDavid van Moolenbroek * then, adding the route may fail for various reasons, but this route
955*ef8d499eSDavid van Moolenbroek * is not essential and so we ignore failures here.
956*ef8d499eSDavid van Moolenbroek */
957*ef8d499eSDavid van Moolenbroek if (ifaddr_v6_netroute(ifdev, num, &base, &prefix))
958*ef8d499eSDavid van Moolenbroek (void)route_add(&base, prefix, NULL /*gateway*/, ifdev,
959*ef8d499eSDavid van Moolenbroek IFADDR_NET_RTFLAGS, NULL /*rtr*/);
960*ef8d499eSDavid van Moolenbroek
961*ef8d499eSDavid van Moolenbroek /*
962*ef8d499eSDavid van Moolenbroek * Add the node-local and link-local scope multicast routes. These are
963*ef8d499eSDavid van Moolenbroek * interface-specific rather than address-specific. They are (re)added
964*ef8d499eSDavid van Moolenbroek * for every address, and never deleted until interface destruction.
965*ef8d499eSDavid van Moolenbroek */
966*ef8d499eSDavid van Moolenbroek ip_addr_set_zero_ip6(&base);
967*ef8d499eSDavid van Moolenbroek base6 = ip_2_ip6(&base);
968*ef8d499eSDavid van Moolenbroek
969*ef8d499eSDavid van Moolenbroek base6->addr[0] = htonl(0xff010000UL | ifdev_get_index(ifdev));
970*ef8d499eSDavid van Moolenbroek
971*ef8d499eSDavid van Moolenbroek (void)route_add(&base, 32, NULL /*gateway*/, ifdev, IFADDR_NET_RTFLAGS,
972*ef8d499eSDavid van Moolenbroek NULL /*rtr*/);
973*ef8d499eSDavid van Moolenbroek
974*ef8d499eSDavid van Moolenbroek base6->addr[0] = htonl(0xff020000UL | ifdev_get_index(ifdev));
975*ef8d499eSDavid van Moolenbroek
976*ef8d499eSDavid van Moolenbroek (void)route_add(&base, 32, NULL /*gateway*/, ifdev, IFADDR_NET_RTFLAGS,
977*ef8d499eSDavid van Moolenbroek NULL /*rtr*/);
978*ef8d499eSDavid van Moolenbroek }
979*ef8d499eSDavid van Moolenbroek
980*ef8d499eSDavid van Moolenbroek /*
981*ef8d499eSDavid van Moolenbroek * A particular local IPv6 address is being deleted. See if there is another
982*ef8d499eSDavid van Moolenbroek * local IPv6 address assigned that should have the same local subnet route
983*ef8d499eSDavid van Moolenbroek * (but didn't, as such duplicate routes can obviously not be added), and if
984*ef8d499eSDavid van Moolenbroek * so, readd the route for that other address, possibly for the same interface.
985*ef8d499eSDavid van Moolenbroek */
986*ef8d499eSDavid van Moolenbroek static void
ifaddr_v6_dupcheck(struct ifdev * oifdev,const ip_addr_t * onetbase,unsigned int oprefix)987*ef8d499eSDavid van Moolenbroek ifaddr_v6_dupcheck(struct ifdev * oifdev, const ip_addr_t * onetbase,
988*ef8d499eSDavid van Moolenbroek unsigned int oprefix)
989*ef8d499eSDavid van Moolenbroek {
990*ef8d499eSDavid van Moolenbroek struct ifdev *ifdev;
991*ef8d499eSDavid van Moolenbroek ip_addr_t netbase;
992*ef8d499eSDavid van Moolenbroek unsigned int prefix;
993*ef8d499eSDavid van Moolenbroek ifaddr_v6_num_t num;
994*ef8d499eSDavid van Moolenbroek
995*ef8d499eSDavid van Moolenbroek for (ifdev = NULL; (ifdev = ifdev_enum(ifdev)) != NULL; ) {
996*ef8d499eSDavid van Moolenbroek if (ifdev == oifdev)
997*ef8d499eSDavid van Moolenbroek continue;
998*ef8d499eSDavid van Moolenbroek
999*ef8d499eSDavid van Moolenbroek for (num = 0; num < LWIP_IPV6_NUM_ADDRESSES; num++) {
1000*ef8d499eSDavid van Moolenbroek if (ip6_addr_isinvalid(ifdev->ifdev_v6state[num]) ||
1001*ef8d499eSDavid van Moolenbroek !ifaddr_v6_isvalid(ifdev, num))
1002*ef8d499eSDavid van Moolenbroek continue;
1003*ef8d499eSDavid van Moolenbroek
1004*ef8d499eSDavid van Moolenbroek if (!ifaddr_v6_netroute(ifdev, num, &netbase, &prefix))
1005*ef8d499eSDavid van Moolenbroek continue;
1006*ef8d499eSDavid van Moolenbroek
1007*ef8d499eSDavid van Moolenbroek if (prefix != oprefix ||
1008*ef8d499eSDavid van Moolenbroek !ip_addr_cmp(&netbase, onetbase))
1009*ef8d499eSDavid van Moolenbroek continue;
1010*ef8d499eSDavid van Moolenbroek
1011*ef8d499eSDavid van Moolenbroek (void)route_add(&netbase, prefix, NULL /*gateway*/,
1012*ef8d499eSDavid van Moolenbroek ifdev, IFADDR_NET_RTFLAGS, NULL /*rtr*/);
1013*ef8d499eSDavid van Moolenbroek
1014*ef8d499eSDavid van Moolenbroek return;
1015*ef8d499eSDavid van Moolenbroek }
1016*ef8d499eSDavid van Moolenbroek }
1017*ef8d499eSDavid van Moolenbroek }
1018*ef8d499eSDavid van Moolenbroek
1019*ef8d499eSDavid van Moolenbroek /*
1020*ef8d499eSDavid van Moolenbroek * A local IPv6 has become invalid (tentative, duplicated, or free) after
1021*ef8d499eSDavid van Moolenbroek * previously being valid (preferred or deprecated). Report the deletion of
1022*ef8d499eSDavid van Moolenbroek * the previously-usable address, and remove previously added routes from the
1023*ef8d499eSDavid van Moolenbroek * IPv6 routing table.
1024*ef8d499eSDavid van Moolenbroek *
1025*ef8d499eSDavid van Moolenbroek * This function is not always called for every deleted address: instead, it is
1026*ef8d499eSDavid van Moolenbroek * called only when the address was previously valid, meaning that
1027*ef8d499eSDavid van Moolenbroek * ifaddr_v6_added() was invoked on it before as well. Unlike for IPv4, this
1028*ef8d499eSDavid van Moolenbroek * function is typically called indirectly, through the netif status callback.
1029*ef8d499eSDavid van Moolenbroek */
1030*ef8d499eSDavid van Moolenbroek static void
ifaddr_v6_deleted(struct ifdev * ifdev,ifaddr_v6_num_t num)1031*ef8d499eSDavid van Moolenbroek ifaddr_v6_deleted(struct ifdev * ifdev, ifaddr_v6_num_t num)
1032*ef8d499eSDavid van Moolenbroek {
1033*ef8d499eSDavid van Moolenbroek struct route_entry *route;
1034*ef8d499eSDavid van Moolenbroek const ip_addr_t *ipaddr;
1035*ef8d499eSDavid van Moolenbroek ip_addr_t netbase;
1036*ef8d499eSDavid van Moolenbroek unsigned int prefix;
1037*ef8d499eSDavid van Moolenbroek
1038*ef8d499eSDavid van Moolenbroek assert(!ip6_addr_isinvalid(ifdev->ifdev_v6state[num]));
1039*ef8d499eSDavid van Moolenbroek
1040*ef8d499eSDavid van Moolenbroek ipaddr = netif_ip_addr6(ifdev_get_netif(ifdev), (int)num);
1041*ef8d499eSDavid van Moolenbroek
1042*ef8d499eSDavid van Moolenbroek /* Delete the local network route, if we tried adding it at all. */
1043*ef8d499eSDavid van Moolenbroek if (ifaddr_v6_netroute(ifdev, num, &netbase, &prefix) &&
1044*ef8d499eSDavid van Moolenbroek (route = route_find(&netbase, prefix,
1045*ef8d499eSDavid van Moolenbroek FALSE /*is_host*/)) != NULL &&
1046*ef8d499eSDavid van Moolenbroek route_get_flags(route) == IFADDR_NET_RTFLAGS) {
1047*ef8d499eSDavid van Moolenbroek route_delete(route, NULL /*rtr*/);
1048*ef8d499eSDavid van Moolenbroek
1049*ef8d499eSDavid van Moolenbroek /*
1050*ef8d499eSDavid van Moolenbroek * Readd the local network route for another interface, if that
1051*ef8d499eSDavid van Moolenbroek * interface has a local address on the very same network.
1052*ef8d499eSDavid van Moolenbroek * Skip scoped (e.g., link-local) addresses, for which the
1053*ef8d499eSDavid van Moolenbroek * routes are unique anyway.
1054*ef8d499eSDavid van Moolenbroek */
1055*ef8d499eSDavid van Moolenbroek if (!ip6_addr_has_scope(ip_2_ip6(ipaddr), IP6_UNICAST))
1056*ef8d499eSDavid van Moolenbroek ifaddr_v6_dupcheck(ifdev, &netbase, prefix);
1057*ef8d499eSDavid van Moolenbroek }
1058*ef8d499eSDavid van Moolenbroek
1059*ef8d499eSDavid van Moolenbroek /* Delete the local host route. */
1060*ef8d499eSDavid van Moolenbroek if ((route = route_find(ipaddr, IP6_BITS, TRUE /*is_host*/)) != NULL)
1061*ef8d499eSDavid van Moolenbroek route_delete(route, NULL /*rtr*/);
1062*ef8d499eSDavid van Moolenbroek
1063*ef8d499eSDavid van Moolenbroek /* Report the deletion of the interface address. */
1064*ef8d499eSDavid van Moolenbroek rtsock_msg_addr_v6(ifdev, RTM_DELADDR, num);
1065*ef8d499eSDavid van Moolenbroek }
1066*ef8d499eSDavid van Moolenbroek
1067*ef8d499eSDavid van Moolenbroek /*
1068*ef8d499eSDavid van Moolenbroek * Add or update an IPv6 address on an interface. The interface is given as
1069*ef8d499eSDavid van Moolenbroek * 'ifdev'. The IPv6 address to add or update is pointed to by 'addr6', which
1070*ef8d499eSDavid van Moolenbroek * must always be a pointer to a valid address. The network mask is given as
1071*ef8d499eSDavid van Moolenbroek * 'mask6', but may be NULL when updating an address. The same applies to the
1072*ef8d499eSDavid van Moolenbroek * destination address 'dest6'. The given IPv6 address and destination address
1073*ef8d499eSDavid van Moolenbroek * must use KAME-style embedding for zones. The flags field 'flags' contains
1074*ef8d499eSDavid van Moolenbroek * a set of NetBSD-style address flags (IN6_IFF_). The 'lifetime' parameter
1075*ef8d499eSDavid van Moolenbroek * always points to lifetime information to be set or updated. Return OK if
1076*ef8d499eSDavid van Moolenbroek * the address was successfully added or updated, or a negative error code
1077*ef8d499eSDavid van Moolenbroek * otherwise.
1078*ef8d499eSDavid van Moolenbroek */
1079*ef8d499eSDavid van Moolenbroek int
ifaddr_v6_add(struct ifdev * ifdev,const struct sockaddr_in6 * addr6,const struct sockaddr_in6 * mask6,const struct sockaddr_in6 * dest6,int flags,const struct in6_addrlifetime * lifetime)1080*ef8d499eSDavid van Moolenbroek ifaddr_v6_add(struct ifdev * ifdev, const struct sockaddr_in6 * addr6,
1081*ef8d499eSDavid van Moolenbroek const struct sockaddr_in6 * mask6, const struct sockaddr_in6 * dest6,
1082*ef8d499eSDavid van Moolenbroek int flags, const struct in6_addrlifetime * lifetime)
1083*ef8d499eSDavid van Moolenbroek {
1084*ef8d499eSDavid van Moolenbroek ip_addr_t ipaddr;
1085*ef8d499eSDavid van Moolenbroek ip6_addr_t *ip6addr;
1086*ef8d499eSDavid van Moolenbroek struct netif *netif;
1087*ef8d499eSDavid van Moolenbroek unsigned int prefix;
1088*ef8d499eSDavid van Moolenbroek ifaddr_v6_num_t num;
1089*ef8d499eSDavid van Moolenbroek uint32_t valid_life;
1090*ef8d499eSDavid van Moolenbroek int r, state;
1091*ef8d499eSDavid van Moolenbroek
1092*ef8d499eSDavid van Moolenbroek netif = ifdev_get_netif(ifdev);
1093*ef8d499eSDavid van Moolenbroek
1094*ef8d499eSDavid van Moolenbroek /*
1095*ef8d499eSDavid van Moolenbroek * Somewhat curiously, NetBSD ignores the zone ID for these requests,
1096*ef8d499eSDavid van Moolenbroek * rather than rejecting requests with a zone ID that does not match
1097*ef8d499eSDavid van Moolenbroek * the associated interface's. We have no reason to be stricter, and
1098*ef8d499eSDavid van Moolenbroek * so we overwrite whatever zone was given..
1099*ef8d499eSDavid van Moolenbroek */
1100*ef8d499eSDavid van Moolenbroek if ((r = addr_get_inet((const struct sockaddr *)addr6, sizeof(*addr6),
1101*ef8d499eSDavid van Moolenbroek IPADDR_TYPE_V6, &ipaddr, TRUE /*kame*/, NULL /*port*/)) != OK)
1102*ef8d499eSDavid van Moolenbroek return r;
1103*ef8d499eSDavid van Moolenbroek
1104*ef8d499eSDavid van Moolenbroek /*
1105*ef8d499eSDavid van Moolenbroek * Forbid locally-assigned multicast addresses. Not only are those
1106*ef8d499eSDavid van Moolenbroek * absolutely disallowed in theory, we also assume all locally assigned
1107*ef8d499eSDavid van Moolenbroek * addresses are unicast in various places in practice.
1108*ef8d499eSDavid van Moolenbroek */
1109*ef8d499eSDavid van Moolenbroek if (ip_addr_ismulticast(&ipaddr))
1110*ef8d499eSDavid van Moolenbroek return EINVAL;
1111*ef8d499eSDavid van Moolenbroek
1112*ef8d499eSDavid van Moolenbroek ip6_addr_assign_zone(ip_2_ip6(&ipaddr), IP6_UNICAST, netif);
1113*ef8d499eSDavid van Moolenbroek
1114*ef8d499eSDavid van Moolenbroek /*
1115*ef8d499eSDavid van Moolenbroek * The netmask needs to be there only when adding a new address, but if
1116*ef8d499eSDavid van Moolenbroek * a netmask is given, it must be valid. Note that lwIP itself
1117*ef8d499eSDavid van Moolenbroek * supports only /64 subnets; however, due to our custom routing hooks,
1118*ef8d499eSDavid van Moolenbroek * combined with giving lifetimes to all addresses (except the primary
1119*ef8d499eSDavid van Moolenbroek * link-local address, which is a /64), we control all routing
1120*ef8d499eSDavid van Moolenbroek * decisions that would otherwise be affected by that lwIP limitation.
1121*ef8d499eSDavid van Moolenbroek */
1122*ef8d499eSDavid van Moolenbroek if (mask6 != NULL && mask6->sin6_family != AF_UNSPEC) {
1123*ef8d499eSDavid van Moolenbroek if ((r = addr_get_netmask((const struct sockaddr *)mask6,
1124*ef8d499eSDavid van Moolenbroek sizeof(*mask6), IPADDR_TYPE_V6, &prefix,
1125*ef8d499eSDavid van Moolenbroek NULL /*ipaddr*/)) != OK)
1126*ef8d499eSDavid van Moolenbroek return r;
1127*ef8d499eSDavid van Moolenbroek } else
1128*ef8d499eSDavid van Moolenbroek prefix = 0;
1129*ef8d499eSDavid van Moolenbroek
1130*ef8d499eSDavid van Moolenbroek /* TODO: dest6 (note: may be NULL) */
1131*ef8d499eSDavid van Moolenbroek
1132*ef8d499eSDavid van Moolenbroek /* TODO: support for IN6_IFF_ANYCAST and IN6_IFF_DETACHED. */
1133*ef8d499eSDavid van Moolenbroek if (flags & ~(IN6_IFF_TENTATIVE | IN6_IFF_DEPRECATED | IN6_IFF_NODAD |
1134*ef8d499eSDavid van Moolenbroek IN6_IFF_AUTOCONF | IN6_IFF_TEMPORARY))
1135*ef8d499eSDavid van Moolenbroek return EINVAL;
1136*ef8d499eSDavid van Moolenbroek
1137*ef8d499eSDavid van Moolenbroek /* Should we add a new address, or update an existing one? */
1138*ef8d499eSDavid van Moolenbroek ip6addr = ip_2_ip6(&ipaddr);
1139*ef8d499eSDavid van Moolenbroek
1140*ef8d499eSDavid van Moolenbroek if (!ifaddr_v6_match(ifdev, &ipaddr, &num)) {
1141*ef8d499eSDavid van Moolenbroek /* Add a new address. */
1142*ef8d499eSDavid van Moolenbroek if (prefix == 0)
1143*ef8d499eSDavid van Moolenbroek return EINVAL;
1144*ef8d499eSDavid van Moolenbroek
1145*ef8d499eSDavid van Moolenbroek /*
1146*ef8d499eSDavid van Moolenbroek * It must be possible to add the address to the routing table,
1147*ef8d499eSDavid van Moolenbroek * so make sure that we can add such a route later on. The
1148*ef8d499eSDavid van Moolenbroek * error code should be accurate for most real-world cases.
1149*ef8d499eSDavid van Moolenbroek */
1150*ef8d499eSDavid van Moolenbroek if (!route_can_add(&ipaddr, IP6_BITS, TRUE /*is_host*/))
1151*ef8d499eSDavid van Moolenbroek return EEXIST;
1152*ef8d499eSDavid van Moolenbroek
1153*ef8d499eSDavid van Moolenbroek /*
1154*ef8d499eSDavid van Moolenbroek * As an exception, if the given address is a link-local
1155*ef8d499eSDavid van Moolenbroek * address and there is no link-local address in slot 0, use
1156*ef8d499eSDavid van Moolenbroek * slot 0 to store this address. This requires a /64 prefix
1157*ef8d499eSDavid van Moolenbroek * length, because lwIP will use an implied /64 subnet for it.
1158*ef8d499eSDavid van Moolenbroek */
1159*ef8d499eSDavid van Moolenbroek if (ip6_addr_isinvalid(ifdev->ifdev_v6state[0]) &&
1160*ef8d499eSDavid van Moolenbroek ip6_addr_islinklocal(ip6addr) && prefix == 64) {
1161*ef8d499eSDavid van Moolenbroek num = (ifaddr_v6_num_t)0;
1162*ef8d499eSDavid van Moolenbroek
1163*ef8d499eSDavid van Moolenbroek /*
1164*ef8d499eSDavid van Moolenbroek * Such link-local addresses are not considered to be
1165*ef8d499eSDavid van Moolenbroek * autoconfigured, because they always have an implied
1166*ef8d499eSDavid van Moolenbroek * subnet. Therefore, clear that flag.
1167*ef8d499eSDavid van Moolenbroek */
1168*ef8d499eSDavid van Moolenbroek flags &= ~IN6_IFF_AUTOCONF;
1169*ef8d499eSDavid van Moolenbroek } else {
1170*ef8d499eSDavid van Moolenbroek /*
1171*ef8d499eSDavid van Moolenbroek * Find a free slot. We bypass netif_ip6_addr_add() as
1172*ef8d499eSDavid van Moolenbroek * it makes things more, rather than less, complicated
1173*ef8d499eSDavid van Moolenbroek * for us here.
1174*ef8d499eSDavid van Moolenbroek */
1175*ef8d499eSDavid van Moolenbroek for (num = 1; num < LWIP_IPV6_NUM_ADDRESSES; num++) {
1176*ef8d499eSDavid van Moolenbroek state = ifdev->ifdev_v6state[num];
1177*ef8d499eSDavid van Moolenbroek
1178*ef8d499eSDavid van Moolenbroek if (ip6_addr_isinvalid(state))
1179*ef8d499eSDavid van Moolenbroek break;
1180*ef8d499eSDavid van Moolenbroek }
1181*ef8d499eSDavid van Moolenbroek
1182*ef8d499eSDavid van Moolenbroek if (num == LWIP_IPV6_NUM_ADDRESSES)
1183*ef8d499eSDavid van Moolenbroek return ENOBUFS; /* TODO: a better error code */
1184*ef8d499eSDavid van Moolenbroek }
1185*ef8d499eSDavid van Moolenbroek
1186*ef8d499eSDavid van Moolenbroek assert(ip6_addr_isinvalid(netif_ip6_addr_state(netif, num)));
1187*ef8d499eSDavid van Moolenbroek
1188*ef8d499eSDavid van Moolenbroek /*
1189*ef8d499eSDavid van Moolenbroek * We bypass the standard netif IPv6 address assignment
1190*ef8d499eSDavid van Moolenbroek * functions here, because we may want to change the state of
1191*ef8d499eSDavid van Moolenbroek * the address to something particular (rather than always
1192*ef8d499eSDavid van Moolenbroek * tentative) and set the state only when we're otherwise done.
1193*ef8d499eSDavid van Moolenbroek */
1194*ef8d499eSDavid van Moolenbroek netif->ip6_addr[num] = ipaddr;
1195*ef8d499eSDavid van Moolenbroek
1196*ef8d499eSDavid van Moolenbroek ifdev->ifdev_v6prefix[num] = prefix;
1197*ef8d499eSDavid van Moolenbroek
1198*ef8d499eSDavid van Moolenbroek /*
1199*ef8d499eSDavid van Moolenbroek * New addresses are always DAD-tested for collisions first,
1200*ef8d499eSDavid van Moolenbroek * except on loopback interfaces, which will simply get back
1201*ef8d499eSDavid van Moolenbroek * its own DAD request and conclude there is a collision..
1202*ef8d499eSDavid van Moolenbroek */
1203*ef8d499eSDavid van Moolenbroek if (flags & IN6_IFF_TENTATIVE)
1204*ef8d499eSDavid van Moolenbroek state = IP6_ADDR_TENTATIVE;
1205*ef8d499eSDavid van Moolenbroek else if (flags & IN6_IFF_DEPRECATED)
1206*ef8d499eSDavid van Moolenbroek state = IP6_ADDR_VALID;
1207*ef8d499eSDavid van Moolenbroek else if (ifdev_is_loopback(ifdev) || (flags & IN6_IFF_NODAD))
1208*ef8d499eSDavid van Moolenbroek state = IP6_ADDR_PREFERRED;
1209*ef8d499eSDavid van Moolenbroek else
1210*ef8d499eSDavid van Moolenbroek state = IP6_ADDR_TENTATIVE;
1211*ef8d499eSDavid van Moolenbroek
1212*ef8d499eSDavid van Moolenbroek ifdev->ifdev_v6flags[num] = 0;
1213*ef8d499eSDavid van Moolenbroek if (flags & IN6_IFF_AUTOCONF)
1214*ef8d499eSDavid van Moolenbroek ifdev->ifdev_v6flags[num] |= IFADDR_V6F_AUTOCONF;
1215*ef8d499eSDavid van Moolenbroek if (flags & IN6_IFF_TEMPORARY)
1216*ef8d499eSDavid van Moolenbroek ifdev->ifdev_v6flags[num] |= IFADDR_V6F_TEMPORARY;
1217*ef8d499eSDavid van Moolenbroek
1218*ef8d499eSDavid van Moolenbroek /* Precompute the address scope as well. */
1219*ef8d499eSDavid van Moolenbroek ifdev->ifdev_v6scope[num] =
1220*ef8d499eSDavid van Moolenbroek addrpol_get_scope(&ipaddr, TRUE /*is_src*/);
1221*ef8d499eSDavid van Moolenbroek } else {
1222*ef8d499eSDavid van Moolenbroek /* Update an existing address. */
1223*ef8d499eSDavid van Moolenbroek /*
1224*ef8d499eSDavid van Moolenbroek * Since no fundamental aspects about the address may change
1225*ef8d499eSDavid van Moolenbroek * we also do not need to delete and readd the address here.
1226*ef8d499eSDavid van Moolenbroek */
1227*ef8d499eSDavid van Moolenbroek if (prefix != 0 && prefix != ifdev->ifdev_v6prefix[num])
1228*ef8d499eSDavid van Moolenbroek return EINVAL;
1229*ef8d499eSDavid van Moolenbroek
1230*ef8d499eSDavid van Moolenbroek /* TODO: figure out exactly what userland wants here.. */
1231*ef8d499eSDavid van Moolenbroek if (flags & IN6_IFF_TENTATIVE)
1232*ef8d499eSDavid van Moolenbroek state = IP6_ADDR_TENTATIVE;
1233*ef8d499eSDavid van Moolenbroek else if (flags & IN6_IFF_DEPRECATED)
1234*ef8d499eSDavid van Moolenbroek state = IP6_ADDR_VALID;
1235*ef8d499eSDavid van Moolenbroek else
1236*ef8d499eSDavid van Moolenbroek state = IP6_ADDR_PREFERRED;
1237*ef8d499eSDavid van Moolenbroek
1238*ef8d499eSDavid van Moolenbroek /*
1239*ef8d499eSDavid van Moolenbroek * Leave the AUTOCONF flag as is, because otherwise we might
1240*ef8d499eSDavid van Moolenbroek * also have to add or delete a subnet route here.
1241*ef8d499eSDavid van Moolenbroek */
1242*ef8d499eSDavid van Moolenbroek if (flags & IN6_IFF_TEMPORARY)
1243*ef8d499eSDavid van Moolenbroek ifdev->ifdev_v6flags[num] |= IFADDR_V6F_TEMPORARY;
1244*ef8d499eSDavid van Moolenbroek else
1245*ef8d499eSDavid van Moolenbroek ifdev->ifdev_v6flags[num] &= ~IFADDR_V6F_TEMPORARY;
1246*ef8d499eSDavid van Moolenbroek }
1247*ef8d499eSDavid van Moolenbroek
1248*ef8d499eSDavid van Moolenbroek /*
1249*ef8d499eSDavid van Moolenbroek * In our implementation, all addresses except the first link-local
1250*ef8d499eSDavid van Moolenbroek * address (which is always stored in slot 0) have a lifetime and are
1251*ef8d499eSDavid van Moolenbroek * thus not static as far as lwIP is concerned. The result is that all
1252*ef8d499eSDavid van Moolenbroek * those addresses are considered to be /128 assignments, leaving the
1253*ef8d499eSDavid van Moolenbroek * routing decisions entirely to us, which is exactly what we want. As
1254*ef8d499eSDavid van Moolenbroek * such we have to be careful not to assign a valid lifetime of 0
1255*ef8d499eSDavid van Moolenbroek * ("static"). For preferred lifetimes, 0 is not a special value,
1256*ef8d499eSDavid van Moolenbroek * though. Either value may be 0xffffffff, which denotes "infinite".
1257*ef8d499eSDavid van Moolenbroek *
1258*ef8d499eSDavid van Moolenbroek * As for those routing decisions: we use the AUTOCONF flag as the
1259*ef8d499eSDavid van Moolenbroek * indication whether or not to add a subnet (= on-link prefix) route
1260*ef8d499eSDavid van Moolenbroek * for the address. See also ifaddr_v6_added().
1261*ef8d499eSDavid van Moolenbroek */
1262*ef8d499eSDavid van Moolenbroek if (num != 0) {
1263*ef8d499eSDavid van Moolenbroek valid_life = lifetime->ia6t_vltime;
1264*ef8d499eSDavid van Moolenbroek if (ip6_addr_life_isstatic(valid_life))
1265*ef8d499eSDavid van Moolenbroek valid_life++;
1266*ef8d499eSDavid van Moolenbroek netif_ip6_addr_set_valid_life(netif, (int)num, valid_life);
1267*ef8d499eSDavid van Moolenbroek netif_ip6_addr_set_pref_life(netif, (int)num,
1268*ef8d499eSDavid van Moolenbroek lifetime->ia6t_pltime);
1269*ef8d499eSDavid van Moolenbroek }
1270*ef8d499eSDavid van Moolenbroek
1271*ef8d499eSDavid van Moolenbroek /*
1272*ef8d499eSDavid van Moolenbroek * The lifetime of address slot 0 is initialized to, and remains at all
1273*ef8d499eSDavid van Moolenbroek * times, zero ("static"). All other slots have an actual lifetime.
1274*ef8d499eSDavid van Moolenbroek */
1275*ef8d499eSDavid van Moolenbroek assert(netif_ip6_addr_isstatic(netif, (int)num) == !num);
1276*ef8d499eSDavid van Moolenbroek
1277*ef8d499eSDavid van Moolenbroek /*
1278*ef8d499eSDavid van Moolenbroek * Change the address state last, as this may immediately trigger
1279*ef8d499eSDavid van Moolenbroek * reports and route addition etc, although usually it will not:
1280*ef8d499eSDavid van Moolenbroek * addresses are typically added as tentative, and ifaddr_v6_added()
1281*ef8d499eSDavid van Moolenbroek * will be called only once the address is valid.
1282*ef8d499eSDavid van Moolenbroek */
1283*ef8d499eSDavid van Moolenbroek netif_ip6_addr_set_state(netif, (int)num, state);
1284*ef8d499eSDavid van Moolenbroek
1285*ef8d499eSDavid van Moolenbroek return OK;
1286*ef8d499eSDavid van Moolenbroek }
1287*ef8d499eSDavid van Moolenbroek
1288*ef8d499eSDavid van Moolenbroek /*
1289*ef8d499eSDavid van Moolenbroek * Delete an IPv6 address from an interface. The given address number must
1290*ef8d499eSDavid van Moolenbroek * have been obtained through ifaddr_v6_find() or ifaddr_v6_enum().
1291*ef8d499eSDavid van Moolenbroek * This function always succeeds.
1292*ef8d499eSDavid van Moolenbroek */
1293*ef8d499eSDavid van Moolenbroek void
ifaddr_v6_del(struct ifdev * ifdev,ifaddr_v6_num_t num)1294*ef8d499eSDavid van Moolenbroek ifaddr_v6_del(struct ifdev * ifdev, ifaddr_v6_num_t num)
1295*ef8d499eSDavid van Moolenbroek {
1296*ef8d499eSDavid van Moolenbroek
1297*ef8d499eSDavid van Moolenbroek assert(num <= LWIP_IPV6_NUM_ADDRESSES);
1298*ef8d499eSDavid van Moolenbroek assert(!ip6_addr_isinvalid(ifdev->ifdev_v6state[num]));
1299*ef8d499eSDavid van Moolenbroek
1300*ef8d499eSDavid van Moolenbroek /* The state change will also trigger ifaddr_v6_deleted() if needed. */
1301*ef8d499eSDavid van Moolenbroek netif_ip6_addr_set_state(ifdev_get_netif(ifdev), (int)num,
1302*ef8d499eSDavid van Moolenbroek IP6_ADDR_INVALID);
1303*ef8d499eSDavid van Moolenbroek }
1304*ef8d499eSDavid van Moolenbroek
1305*ef8d499eSDavid van Moolenbroek /*
1306*ef8d499eSDavid van Moolenbroek * Announce all IPv6 addresses associated with the given interface as deleted.
1307*ef8d499eSDavid van Moolenbroek * Used (only) right before the interface is destroyed.
1308*ef8d499eSDavid van Moolenbroek */
1309*ef8d499eSDavid van Moolenbroek void
ifaddr_v6_clear(struct ifdev * ifdev)1310*ef8d499eSDavid van Moolenbroek ifaddr_v6_clear(struct ifdev * ifdev)
1311*ef8d499eSDavid van Moolenbroek {
1312*ef8d499eSDavid van Moolenbroek ifaddr_v6_num_t num;
1313*ef8d499eSDavid van Moolenbroek
1314*ef8d499eSDavid van Moolenbroek for (num = 0; ifaddr_v6_enum(ifdev, &num); num++) {
1315*ef8d499eSDavid van Moolenbroek if (ifaddr_v6_isvalid(ifdev, num))
1316*ef8d499eSDavid van Moolenbroek ifaddr_v6_deleted(ifdev, num);
1317*ef8d499eSDavid van Moolenbroek }
1318*ef8d499eSDavid van Moolenbroek }
1319*ef8d499eSDavid van Moolenbroek
1320*ef8d499eSDavid van Moolenbroek /*
1321*ef8d499eSDavid van Moolenbroek * Check state changes on local IPv6 addresses and update shadow state
1322*ef8d499eSDavid van Moolenbroek * accordingly.
1323*ef8d499eSDavid van Moolenbroek */
1324*ef8d499eSDavid van Moolenbroek void
ifaddr_v6_check(struct ifdev * ifdev)1325*ef8d499eSDavid van Moolenbroek ifaddr_v6_check(struct ifdev * ifdev)
1326*ef8d499eSDavid van Moolenbroek {
1327*ef8d499eSDavid van Moolenbroek struct netif *netif;
1328*ef8d499eSDavid van Moolenbroek ifaddr_v6_num_t num;
1329*ef8d499eSDavid van Moolenbroek int old_state, new_state, was_valid, is_valid;
1330*ef8d499eSDavid van Moolenbroek
1331*ef8d499eSDavid van Moolenbroek netif = ifdev_get_netif(ifdev);
1332*ef8d499eSDavid van Moolenbroek
1333*ef8d499eSDavid van Moolenbroek for (num = 0; num < LWIP_IPV6_NUM_ADDRESSES; num++) {
1334*ef8d499eSDavid van Moolenbroek /*
1335*ef8d499eSDavid van Moolenbroek * Since we compile lwIP without support for stateless
1336*ef8d499eSDavid van Moolenbroek * autoconfiguration, there will be no cases where new
1337*ef8d499eSDavid van Moolenbroek * addresses appear out of nowhere. As such, we can rely on
1338*ef8d499eSDavid van Moolenbroek * all necessary fields already being initialized here.
1339*ef8d499eSDavid van Moolenbroek */
1340*ef8d499eSDavid van Moolenbroek old_state = ifdev->ifdev_v6state[num];
1341*ef8d499eSDavid van Moolenbroek new_state = netif_ip6_addr_state(netif, num);
1342*ef8d499eSDavid van Moolenbroek
1343*ef8d499eSDavid van Moolenbroek if (old_state == new_state)
1344*ef8d499eSDavid van Moolenbroek continue;
1345*ef8d499eSDavid van Moolenbroek
1346*ef8d499eSDavid van Moolenbroek was_valid = ip6_addr_isvalid(old_state);
1347*ef8d499eSDavid van Moolenbroek is_valid = ip6_addr_isvalid(new_state);
1348*ef8d499eSDavid van Moolenbroek
1349*ef8d499eSDavid van Moolenbroek if (was_valid != is_valid) {
1350*ef8d499eSDavid van Moolenbroek if (is_valid)
1351*ef8d499eSDavid van Moolenbroek ifaddr_v6_added(ifdev, num);
1352*ef8d499eSDavid van Moolenbroek else
1353*ef8d499eSDavid van Moolenbroek ifaddr_v6_deleted(ifdev, num);
1354*ef8d499eSDavid van Moolenbroek }
1355*ef8d499eSDavid van Moolenbroek
1356*ef8d499eSDavid van Moolenbroek ifdev->ifdev_v6state[num] = new_state;
1357*ef8d499eSDavid van Moolenbroek
1358*ef8d499eSDavid van Moolenbroek /*
1359*ef8d499eSDavid van Moolenbroek * TODO: implement the requirements for dealing with duplicated
1360*ef8d499eSDavid van Moolenbroek * addresses, in particular the link-local address, as
1361*ef8d499eSDavid van Moolenbroek * specified by RFC 4862 Sec. 5.4.5. NetBSD uses the
1362*ef8d499eSDavid van Moolenbroek * ND6_IFF_IFDISABLED flag for this, essentially disabling
1363*ef8d499eSDavid van Moolenbroek * the interface completely when that flag is set.
1364*ef8d499eSDavid van Moolenbroek */
1365*ef8d499eSDavid van Moolenbroek }
1366*ef8d499eSDavid van Moolenbroek }
1367*ef8d499eSDavid van Moolenbroek
1368*ef8d499eSDavid van Moolenbroek /*
1369*ef8d499eSDavid van Moolenbroek * A change in the interface and/or link status has resulted in both now being
1370*ef8d499eSDavid van Moolenbroek * up. Set the link-local address, if any, to tentative state. Exempt
1371*ef8d499eSDavid van Moolenbroek * loopback interfaces, which would just see their own requests as collisions.
1372*ef8d499eSDavid van Moolenbroek *
1373*ef8d499eSDavid van Moolenbroek * TODO: the current implementation is the absolute minimum required for
1374*ef8d499eSDavid van Moolenbroek * dhcpcd(8) to function somewhat properly, but there is much more to be
1375*ef8d499eSDavid van Moolenbroek * decided and done when it comes to dealing with status changes..
1376*ef8d499eSDavid van Moolenbroek */
1377*ef8d499eSDavid van Moolenbroek void
ifaddr_v6_set_up(struct ifdev * ifdev)1378*ef8d499eSDavid van Moolenbroek ifaddr_v6_set_up(struct ifdev * ifdev)
1379*ef8d499eSDavid van Moolenbroek {
1380*ef8d499eSDavid van Moolenbroek
1381*ef8d499eSDavid van Moolenbroek if (!ifdev_is_loopback(ifdev) &&
1382*ef8d499eSDavid van Moolenbroek !ip6_addr_isinvalid(ifdev->ifdev_v6state[0]))
1383*ef8d499eSDavid van Moolenbroek netif_ip6_addr_set_state(ifdev_get_netif(ifdev), 0,
1384*ef8d499eSDavid van Moolenbroek IP6_ADDR_TENTATIVE);
1385*ef8d499eSDavid van Moolenbroek }
1386*ef8d499eSDavid van Moolenbroek
1387*ef8d499eSDavid van Moolenbroek /*
1388*ef8d499eSDavid van Moolenbroek * Check whether all conditions are met for (re)assigning a link-local IPv6
1389*ef8d499eSDavid van Moolenbroek * address, and if so, do just that.
1390*ef8d499eSDavid van Moolenbroek */
1391*ef8d499eSDavid van Moolenbroek void
ifaddr_v6_set_linklocal(struct ifdev * ifdev)1392*ef8d499eSDavid van Moolenbroek ifaddr_v6_set_linklocal(struct ifdev * ifdev)
1393*ef8d499eSDavid van Moolenbroek {
1394*ef8d499eSDavid van Moolenbroek
1395*ef8d499eSDavid van Moolenbroek /*
1396*ef8d499eSDavid van Moolenbroek * A few conditions must be met for link-local address assignment.
1397*ef8d499eSDavid van Moolenbroek * First of all, link-local address assignment must be enabled both
1398*ef8d499eSDavid van Moolenbroek * globally and on the interface. The BSDs use the global setting as
1399*ef8d499eSDavid van Moolenbroek * an initial value for the link-local setting, but if we do this, it
1400*ef8d499eSDavid van Moolenbroek * would basically be impossible to change the global setting and have
1401*ef8d499eSDavid van Moolenbroek * any effect. Thus, we use the global setting as an additional
1402*ef8d499eSDavid van Moolenbroek * requirement, with as reasoning that people will typically disable
1403*ef8d499eSDavid van Moolenbroek * the global setting in order to assign no IPv6 addresses at all.
1404*ef8d499eSDavid van Moolenbroek */
1405*ef8d499eSDavid van Moolenbroek if (!(ifdev_get_nd6flags(ifdev) & ND6_IFF_AUTO_LINKLOCAL) ||
1406*ef8d499eSDavid van Moolenbroek !ifaddr_auto_linklocal)
1407*ef8d499eSDavid van Moolenbroek return;
1408*ef8d499eSDavid van Moolenbroek
1409*ef8d499eSDavid van Moolenbroek /*
1410*ef8d499eSDavid van Moolenbroek * Second, the interface must be up. This is an artificial requirement
1411*ef8d499eSDavid van Moolenbroek * that allows for the above settings to be changed at all: if we
1412*ef8d499eSDavid van Moolenbroek * assigned a link-local address as soon as we could (see below), this
1413*ef8d499eSDavid van Moolenbroek * would leave virtually no opportunity to change the settings. Once
1414*ef8d499eSDavid van Moolenbroek * assigned, a link-local address is never removed automatically.
1415*ef8d499eSDavid van Moolenbroek */
1416*ef8d499eSDavid van Moolenbroek if (!ifdev_is_up(ifdev))
1417*ef8d499eSDavid van Moolenbroek return;
1418*ef8d499eSDavid van Moolenbroek
1419*ef8d499eSDavid van Moolenbroek /*
1420*ef8d499eSDavid van Moolenbroek * A proper (48-bit) hardware address must be set. Interfaces without
1421*ef8d499eSDavid van Moolenbroek * hardware addresses (e.g., loopback devices) do not have this kind of
1422*ef8d499eSDavid van Moolenbroek * auto-assignment. It may take a while for the driver to get back to
1423*ef8d499eSDavid van Moolenbroek * us with its initial hardware address, so wait for at least that.
1424*ef8d499eSDavid van Moolenbroek * Also update the link-local address upon subsequent (user-initiated)
1425*ef8d499eSDavid van Moolenbroek * changes to the hardware address, as long as if the IPv6 address has
1426*ef8d499eSDavid van Moolenbroek * not been overridden by userland by then.
1427*ef8d499eSDavid van Moolenbroek */
1428*ef8d499eSDavid van Moolenbroek if (ifdev_get_hwlen(ifdev) != ETHARP_HWADDR_LEN ||
1429*ef8d499eSDavid van Moolenbroek !(ifdev->ifdev_hwlist[0].ifhwa_flags & IFHWAF_VALID))
1430*ef8d499eSDavid van Moolenbroek return;
1431*ef8d499eSDavid van Moolenbroek
1432*ef8d499eSDavid van Moolenbroek if (!ip6_addr_isinvalid(ifdev->ifdev_v6state[0]) &&
1433*ef8d499eSDavid van Moolenbroek (ifdev->ifdev_v6flags[0] & IFADDR_V6F_HWBASED))
1434*ef8d499eSDavid van Moolenbroek return;
1435*ef8d499eSDavid van Moolenbroek
1436*ef8d499eSDavid van Moolenbroek /*
1437*ef8d499eSDavid van Moolenbroek * All conditions are met. Set or replace the interface's IPv6
1438*ef8d499eSDavid van Moolenbroek * link-local address. This uses the first IPv6 address slot, which
1439*ef8d499eSDavid van Moolenbroek * will be skipped when adding non-link-local addresses. We first
1440*ef8d499eSDavid van Moolenbroek * delete the old address if any, in order to force invalidation of
1441*ef8d499eSDavid van Moolenbroek * bound sockets, because setting the new address does not (currently)
1442*ef8d499eSDavid van Moolenbroek * consider sockets.
1443*ef8d499eSDavid van Moolenbroek */
1444*ef8d499eSDavid van Moolenbroek if (!ip6_addr_isinvalid(ifdev->ifdev_v6state[0]))
1445*ef8d499eSDavid van Moolenbroek ifaddr_v6_del(ifdev, (ifaddr_v6_num_t)0);
1446*ef8d499eSDavid van Moolenbroek
1447*ef8d499eSDavid van Moolenbroek #ifdef INET6
1448*ef8d499eSDavid van Moolenbroek ifdev->ifdev_v6flags[0] = IFADDR_V6F_HWBASED;
1449*ef8d499eSDavid van Moolenbroek ifdev->ifdev_v6prefix[0] = 64;
1450*ef8d499eSDavid van Moolenbroek netif_create_ip6_linklocal_address(ifdev_get_netif(ifdev),
1451*ef8d499eSDavid van Moolenbroek 1 /*from_mac_48bit*/);
1452*ef8d499eSDavid van Moolenbroek assert(!ip6_addr_isinvalid(ifdev->ifdev_v6state[0]));
1453*ef8d499eSDavid van Moolenbroek
1454*ef8d499eSDavid van Moolenbroek ifdev->ifdev_v6scope[0] =
1455*ef8d499eSDavid van Moolenbroek addrpol_get_scope(netif_ip_addr6(ifdev_get_netif(ifdev), 0),
1456*ef8d499eSDavid van Moolenbroek TRUE /*is_src*/);
1457*ef8d499eSDavid van Moolenbroek #endif /* INET6 */
1458*ef8d499eSDavid van Moolenbroek }
1459*ef8d499eSDavid van Moolenbroek
1460*ef8d499eSDavid van Moolenbroek /*
1461*ef8d499eSDavid van Moolenbroek * Return the first interface device that owns the given (non-any) IPv6
1462*ef8d499eSDavid van Moolenbroek * address, or NULL if it is not a valid local IPv6 address. Addresses that
1463*ef8d499eSDavid van Moolenbroek * exist but are not usable ("usually assigned" in the RFC4862 sense) are
1464*ef8d499eSDavid van Moolenbroek * considered not valid in this context.
1465*ef8d499eSDavid van Moolenbroek */
1466*ef8d499eSDavid van Moolenbroek struct ifdev *
ifaddr_v6_map_by_addr(const ip6_addr_t * ip6addr)1467*ef8d499eSDavid van Moolenbroek ifaddr_v6_map_by_addr(const ip6_addr_t * ip6addr)
1468*ef8d499eSDavid van Moolenbroek {
1469*ef8d499eSDavid van Moolenbroek struct ifdev *ifdev;
1470*ef8d499eSDavid van Moolenbroek struct netif *netif;
1471*ef8d499eSDavid van Moolenbroek ifaddr_v6_num_t num;
1472*ef8d499eSDavid van Moolenbroek
1473*ef8d499eSDavid van Moolenbroek /*
1474*ef8d499eSDavid van Moolenbroek * It would be nice to be able to do a route lookup on an RTF_LOCAL
1475*ef8d499eSDavid van Moolenbroek * entry here, but this approach would currently have two problems.
1476*ef8d499eSDavid van Moolenbroek *
1477*ef8d499eSDavid van Moolenbroek * 1) link-local addresses would require a lookup with a different
1478*ef8d499eSDavid van Moolenbroek * embedded zone for each possible interface, requiring a loop over
1479*ef8d499eSDavid van Moolenbroek * all interfaces after all; we could do a route lookup for global
1480*ef8d499eSDavid van Moolenbroek * addresses only, but then there's also the issue that..
1481*ef8d499eSDavid van Moolenbroek * 2) once we get the interface from the route, we still have to check
1482*ef8d499eSDavid van Moolenbroek * check the state of the address, as done below, and that requires
1483*ef8d499eSDavid van Moolenbroek * us to go through all the interface addresses after all; we could
1484*ef8d499eSDavid van Moolenbroek * embed the local address number in the RTF_LOCAL routing entry but
1485*ef8d499eSDavid van Moolenbroek * that would get rather messy API-wise.
1486*ef8d499eSDavid van Moolenbroek *
1487*ef8d499eSDavid van Moolenbroek * Still, if it turns out that this function is a bottleneck, the above
1488*ef8d499eSDavid van Moolenbroek * workarounds should offer a way forward for the common case.
1489*ef8d499eSDavid van Moolenbroek */
1490*ef8d499eSDavid van Moolenbroek for (ifdev = NULL; (ifdev = ifdev_enum(ifdev)) != NULL; ) {
1491*ef8d499eSDavid van Moolenbroek netif = ifdev_get_netif(ifdev);
1492*ef8d499eSDavid van Moolenbroek
1493*ef8d499eSDavid van Moolenbroek for (num = 0; num < LWIP_IPV6_NUM_ADDRESSES; num++) {
1494*ef8d499eSDavid van Moolenbroek if (ip6_addr_isinvalid(ifdev->ifdev_v6state[num]))
1495*ef8d499eSDavid van Moolenbroek continue;
1496*ef8d499eSDavid van Moolenbroek
1497*ef8d499eSDavid van Moolenbroek /*
1498*ef8d499eSDavid van Moolenbroek * An address may be used as a local address only if it
1499*ef8d499eSDavid van Moolenbroek * is preferred or deprecated, not if it is tentative
1500*ef8d499eSDavid van Moolenbroek * or duplicated.
1501*ef8d499eSDavid van Moolenbroek */
1502*ef8d499eSDavid van Moolenbroek if (!ifaddr_v6_isvalid(ifdev, num))
1503*ef8d499eSDavid van Moolenbroek continue;
1504*ef8d499eSDavid van Moolenbroek
1505*ef8d499eSDavid van Moolenbroek /*
1506*ef8d499eSDavid van Moolenbroek * Ignore the zone if the given address does not have
1507*ef8d499eSDavid van Moolenbroek * one set. Otherwise, the zone must match.
1508*ef8d499eSDavid van Moolenbroek */
1509*ef8d499eSDavid van Moolenbroek if (ip6_addr_cmp_zoneless(netif_ip6_addr(netif, num),
1510*ef8d499eSDavid van Moolenbroek ip6addr) && (!ip6_addr_has_zone(ip6addr) ||
1511*ef8d499eSDavid van Moolenbroek ip6_addr_test_zone(ip6addr, netif)))
1512*ef8d499eSDavid van Moolenbroek return ifdev;
1513*ef8d499eSDavid van Moolenbroek }
1514*ef8d499eSDavid van Moolenbroek }
1515*ef8d499eSDavid van Moolenbroek
1516*ef8d499eSDavid van Moolenbroek return NULL;
1517*ef8d499eSDavid van Moolenbroek }
1518*ef8d499eSDavid van Moolenbroek
1519*ef8d499eSDavid van Moolenbroek /*
1520*ef8d499eSDavid van Moolenbroek * Return the first interface device for which the given IPv6 address is on a
1521*ef8d499eSDavid van Moolenbroek * configured local subnet, or NULL if no match was found.
1522*ef8d499eSDavid van Moolenbroek */
1523*ef8d499eSDavid van Moolenbroek static struct ifdev *
ifaddr_v6_map_by_subnet(const ip_addr_t * ipaddr)1524*ef8d499eSDavid van Moolenbroek ifaddr_v6_map_by_subnet(const ip_addr_t * ipaddr)
1525*ef8d499eSDavid van Moolenbroek {
1526*ef8d499eSDavid van Moolenbroek const ip_addr_t *addr;
1527*ef8d499eSDavid van Moolenbroek struct ifdev *ifdev;
1528*ef8d499eSDavid van Moolenbroek struct netif *netif;
1529*ef8d499eSDavid van Moolenbroek ifaddr_v6_num_t num;
1530*ef8d499eSDavid van Moolenbroek unsigned int prefix;
1531*ef8d499eSDavid van Moolenbroek
1532*ef8d499eSDavid van Moolenbroek assert(IP_IS_V6(ipaddr));
1533*ef8d499eSDavid van Moolenbroek
1534*ef8d499eSDavid van Moolenbroek for (ifdev = NULL; (ifdev = ifdev_enum(ifdev)) != NULL; ) {
1535*ef8d499eSDavid van Moolenbroek netif = ifdev_get_netif(ifdev);
1536*ef8d499eSDavid van Moolenbroek
1537*ef8d499eSDavid van Moolenbroek if (ip6_addr_has_zone(ip_2_ip6(ipaddr)) &&
1538*ef8d499eSDavid van Moolenbroek !ip6_addr_test_zone(ip_2_ip6(ipaddr), netif))
1539*ef8d499eSDavid van Moolenbroek continue;
1540*ef8d499eSDavid van Moolenbroek
1541*ef8d499eSDavid van Moolenbroek for (num = 0; num < LWIP_IPV6_NUM_ADDRESSES; num++) {
1542*ef8d499eSDavid van Moolenbroek if (ip6_addr_isinvalid(ifdev->ifdev_v6state[num]))
1543*ef8d499eSDavid van Moolenbroek continue;
1544*ef8d499eSDavid van Moolenbroek
1545*ef8d499eSDavid van Moolenbroek if (!ifaddr_v6_isvalid(ifdev, num))
1546*ef8d499eSDavid van Moolenbroek continue;
1547*ef8d499eSDavid van Moolenbroek
1548*ef8d499eSDavid van Moolenbroek addr = netif_ip_addr6(netif, num);
1549*ef8d499eSDavid van Moolenbroek
1550*ef8d499eSDavid van Moolenbroek /*
1551*ef8d499eSDavid van Moolenbroek * For addresses with no implied subnet, check against
1552*ef8d499eSDavid van Moolenbroek * the full address, so as to match only that address.
1553*ef8d499eSDavid van Moolenbroek */
1554*ef8d499eSDavid van Moolenbroek if (ifdev->ifdev_v6flags[num] & IFADDR_V6F_AUTOCONF)
1555*ef8d499eSDavid van Moolenbroek prefix = IP6_BITS;
1556*ef8d499eSDavid van Moolenbroek else
1557*ef8d499eSDavid van Moolenbroek prefix = ifdev->ifdev_v6prefix[num];
1558*ef8d499eSDavid van Moolenbroek
1559*ef8d499eSDavid van Moolenbroek if (addr_get_common_bits(ipaddr, addr, prefix) ==
1560*ef8d499eSDavid van Moolenbroek prefix)
1561*ef8d499eSDavid van Moolenbroek return ifdev;
1562*ef8d499eSDavid van Moolenbroek }
1563*ef8d499eSDavid van Moolenbroek }
1564*ef8d499eSDavid van Moolenbroek
1565*ef8d499eSDavid van Moolenbroek return NULL;
1566*ef8d499eSDavid van Moolenbroek }
1567*ef8d499eSDavid van Moolenbroek
1568*ef8d499eSDavid van Moolenbroek /*
1569*ef8d499eSDavid van Moolenbroek * Select an IPv6 source address for communication to the given destination
1570*ef8d499eSDavid van Moolenbroek * address on the given interface. Return the selected source address, or NULL
1571*ef8d499eSDavid van Moolenbroek * if no appropriate source address could be found. This function implements
1572*ef8d499eSDavid van Moolenbroek * RFC 6724 Sec. 5, and is very close to a drop-in replacement for lwIP's own
1573*ef8d499eSDavid van Moolenbroek * ip6_select_source_address() function. We can do a slightly better job
1574*ef8d499eSDavid van Moolenbroek * because we have more information (for Rules 6 and 7) and can offer a more
1575*ef8d499eSDavid van Moolenbroek * complete, less lightweight implementation (for Rule 8).
1576*ef8d499eSDavid van Moolenbroek *
1577*ef8d499eSDavid van Moolenbroek * In summary, this is the implementation status of the rules:
1578*ef8d499eSDavid van Moolenbroek *
1579*ef8d499eSDavid van Moolenbroek * - Rules 1, 2, 3: fully implemented
1580*ef8d499eSDavid van Moolenbroek * - Rules 4, 5, 5.5: not applicable
1581*ef8d499eSDavid van Moolenbroek * - Rules 6, 7, 8: fully implemented
1582*ef8d499eSDavid van Moolenbroek *
1583*ef8d499eSDavid van Moolenbroek * Note that for rule 2, scope decisions are left to the addrpol module, which
1584*ef8d499eSDavid van Moolenbroek * makes a deliberate exception from the RFC for Unique-Local Addresses.
1585*ef8d499eSDavid van Moolenbroek *
1586*ef8d499eSDavid van Moolenbroek * The given destination address may not be properly zoned.
1587*ef8d499eSDavid van Moolenbroek */
1588*ef8d499eSDavid van Moolenbroek static const ip_addr_t *
ifaddr_v6_select(struct ifdev * ifdev,const ip_addr_t * dest_addr)1589*ef8d499eSDavid van Moolenbroek ifaddr_v6_select(struct ifdev * ifdev, const ip_addr_t * dest_addr)
1590*ef8d499eSDavid van Moolenbroek {
1591*ef8d499eSDavid van Moolenbroek const ip_addr_t *cand_addr, *best_addr;
1592*ef8d499eSDavid van Moolenbroek int dest_scope, cand_scope, best_scope;
1593*ef8d499eSDavid van Moolenbroek int dest_label, cand_label, best_label = 0 /*gcc*/;
1594*ef8d499eSDavid van Moolenbroek uint8_t cand_pref, best_pref = 0 /*gcc*/;
1595*ef8d499eSDavid van Moolenbroek uint8_t cand_temp, best_temp = 0 /*gcc*/;
1596*ef8d499eSDavid van Moolenbroek int cand_bits, best_bits = 0 /*gcc*/;
1597*ef8d499eSDavid van Moolenbroek ifaddr_v6_num_t num, best_num;
1598*ef8d499eSDavid van Moolenbroek
1599*ef8d499eSDavid van Moolenbroek assert(ifdev != NULL);
1600*ef8d499eSDavid van Moolenbroek assert(IP_IS_V6(dest_addr));
1601*ef8d499eSDavid van Moolenbroek
1602*ef8d499eSDavid van Moolenbroek dest_scope = addrpol_get_scope(dest_addr, FALSE /*is_src*/);
1603*ef8d499eSDavid van Moolenbroek dest_label = -1; /* obtain only when necessary */
1604*ef8d499eSDavid van Moolenbroek
1605*ef8d499eSDavid van Moolenbroek best_addr = NULL;
1606*ef8d499eSDavid van Moolenbroek best_num = -1;
1607*ef8d499eSDavid van Moolenbroek
1608*ef8d499eSDavid van Moolenbroek for (num = 0; num < LWIP_IPV6_NUM_ADDRESSES; num++) {
1609*ef8d499eSDavid van Moolenbroek /* Consider only valid (preferred and deprecated) addresses. */
1610*ef8d499eSDavid van Moolenbroek if (!ip6_addr_isvalid(ifdev->ifdev_v6state[num]))
1611*ef8d499eSDavid van Moolenbroek continue;
1612*ef8d499eSDavid van Moolenbroek
1613*ef8d499eSDavid van Moolenbroek cand_addr = netif_ip_addr6(ifdev_get_netif(ifdev), (int)num);
1614*ef8d499eSDavid van Moolenbroek
1615*ef8d499eSDavid van Moolenbroek /* Rule 1 */
1616*ef8d499eSDavid van Moolenbroek if (ip6_addr_cmp_zoneless(ip_2_ip6(cand_addr),
1617*ef8d499eSDavid van Moolenbroek ip_2_ip6(dest_addr)))
1618*ef8d499eSDavid van Moolenbroek return cand_addr;
1619*ef8d499eSDavid van Moolenbroek
1620*ef8d499eSDavid van Moolenbroek cand_scope = ifdev->ifdev_v6scope[num];
1621*ef8d499eSDavid van Moolenbroek cand_pref = ip6_addr_ispreferred(ifdev->ifdev_v6state[num]);
1622*ef8d499eSDavid van Moolenbroek cand_temp = (ifdev->ifdev_v6flags[num] & IFADDR_V6F_TEMPORARY);
1623*ef8d499eSDavid van Moolenbroek cand_label = -1;
1624*ef8d499eSDavid van Moolenbroek cand_bits = -1;
1625*ef8d499eSDavid van Moolenbroek
1626*ef8d499eSDavid van Moolenbroek /*
1627*ef8d499eSDavid van Moolenbroek * The following monster of an if-condition relies on order of
1628*ef8d499eSDavid van Moolenbroek * evaluation to obtain the more expensive-to-compute values
1629*ef8d499eSDavid van Moolenbroek * only when strictly necessary. We use a shortcut for Rule 6:
1630*ef8d499eSDavid van Moolenbroek * labels are computed based on longest matching prefix, so if
1631*ef8d499eSDavid van Moolenbroek * Rule 6 prefers the candidate address, Rule 8 would have
1632*ef8d499eSDavid van Moolenbroek * preferred the candidate address as well. Therefore, skip
1633*ef8d499eSDavid van Moolenbroek * even computing labels when Rule 7 would not prefer either
1634*ef8d499eSDavid van Moolenbroek * address, i.e. the "temporary" state of the candidate and the
1635*ef8d499eSDavid van Moolenbroek * best address are equal. For complete ties (which exist,
1636*ef8d499eSDavid van Moolenbroek * because Rule 8 - longest common prefix - checks up to the
1637*ef8d499eSDavid van Moolenbroek * subnet size), as "policy" we always pick the first address.
1638*ef8d499eSDavid van Moolenbroek */
1639*ef8d499eSDavid van Moolenbroek #define ADDRPOL_GET_LABEL(addr, label) \
1640*ef8d499eSDavid van Moolenbroek (label != -1 || (label = addrpol_get_label(addr), 1))
1641*ef8d499eSDavid van Moolenbroek #define ADDR_GET_COMMON_BITS(addr1, addr2, num, bits) \
1642*ef8d499eSDavid van Moolenbroek (bits != -1 || (bits = (int) \
1643*ef8d499eSDavid van Moolenbroek addr_get_common_bits(addr1, addr2, ifdev->ifdev_v6prefix[num]), 1))
1644*ef8d499eSDavid van Moolenbroek
1645*ef8d499eSDavid van Moolenbroek if (best_addr == NULL || /* no alternative yet */
1646*ef8d499eSDavid van Moolenbroek /* Rule 2 */
1647*ef8d499eSDavid van Moolenbroek (cand_scope < best_scope && cand_scope >= dest_scope) ||
1648*ef8d499eSDavid van Moolenbroek (cand_scope > best_scope && best_scope < dest_scope) ||
1649*ef8d499eSDavid van Moolenbroek (cand_scope == best_scope &&
1650*ef8d499eSDavid van Moolenbroek /* Rule 3 */
1651*ef8d499eSDavid van Moolenbroek (cand_pref > best_pref || (cand_pref == best_pref &&
1652*ef8d499eSDavid van Moolenbroek /* Rule 6 */
1653*ef8d499eSDavid van Moolenbroek ((cand_temp != best_temp && /* shortcut, part 1 */
1654*ef8d499eSDavid van Moolenbroek ADDRPOL_GET_LABEL(dest_addr, dest_label) &&
1655*ef8d499eSDavid van Moolenbroek ADDRPOL_GET_LABEL(cand_addr, cand_label) &&
1656*ef8d499eSDavid van Moolenbroek ADDRPOL_GET_LABEL(best_addr, best_label) &&
1657*ef8d499eSDavid van Moolenbroek cand_label == dest_label && best_label != dest_label) ||
1658*ef8d499eSDavid van Moolenbroek ((cand_temp == best_temp || /* shortcut, part 2 */
1659*ef8d499eSDavid van Moolenbroek ((cand_label == dest_label) ==
1660*ef8d499eSDavid van Moolenbroek (best_label == dest_label))) &&
1661*ef8d499eSDavid van Moolenbroek /* Rule 7 */
1662*ef8d499eSDavid van Moolenbroek (cand_temp > best_temp || (cand_temp == best_temp &&
1663*ef8d499eSDavid van Moolenbroek /* Rule 8 */
1664*ef8d499eSDavid van Moolenbroek ADDR_GET_COMMON_BITS(cand_addr, dest_addr, num,
1665*ef8d499eSDavid van Moolenbroek cand_bits) &&
1666*ef8d499eSDavid van Moolenbroek ADDR_GET_COMMON_BITS(best_addr, dest_addr, best_num,
1667*ef8d499eSDavid van Moolenbroek best_bits) &&
1668*ef8d499eSDavid van Moolenbroek cand_bits > best_bits)))))))) {
1669*ef8d499eSDavid van Moolenbroek /* We found a new "winning" candidate. */
1670*ef8d499eSDavid van Moolenbroek best_addr = cand_addr;
1671*ef8d499eSDavid van Moolenbroek best_scope = cand_scope;
1672*ef8d499eSDavid van Moolenbroek best_pref = cand_pref;
1673*ef8d499eSDavid van Moolenbroek best_temp = cand_temp;
1674*ef8d499eSDavid van Moolenbroek best_label = cand_label;
1675*ef8d499eSDavid van Moolenbroek best_bits = cand_bits;
1676*ef8d499eSDavid van Moolenbroek best_num = num;
1677*ef8d499eSDavid van Moolenbroek }
1678*ef8d499eSDavid van Moolenbroek }
1679*ef8d499eSDavid van Moolenbroek
1680*ef8d499eSDavid van Moolenbroek /* Return the best candidate, if any. */
1681*ef8d499eSDavid van Moolenbroek return best_addr;
1682*ef8d499eSDavid van Moolenbroek }
1683*ef8d499eSDavid van Moolenbroek
1684*ef8d499eSDavid van Moolenbroek /*
1685*ef8d499eSDavid van Moolenbroek * Pick an IPv6 source address locally assigned to the given interface, for use
1686*ef8d499eSDavid van Moolenbroek * with the given IPv6 destination address. See ifaddr_v6_select() on why we
1687*ef8d499eSDavid van Moolenbroek * override lwIP's version of this function.
1688*ef8d499eSDavid van Moolenbroek *
1689*ef8d499eSDavid van Moolenbroek * This is a full replacement of the corresponding lwIP function, which should
1690*ef8d499eSDavid van Moolenbroek * be overridden with weak symbols, using patches against the lwIP source code.
1691*ef8d499eSDavid van Moolenbroek * As such, the lwIP headers should already provide the correct prototype for
1692*ef8d499eSDavid van Moolenbroek * this function. If not, something will have changed in the lwIP
1693*ef8d499eSDavid van Moolenbroek * implementation, and this code must be revised accordingly.
1694*ef8d499eSDavid van Moolenbroek *
1695*ef8d499eSDavid van Moolenbroek * Important: there are currently no tests that will detect that overriding is
1696*ef8d499eSDavid van Moolenbroek * broken, since our test code (necessarily) uses the code path that calls
1697*ef8d499eSDavid van Moolenbroek * ifaddr_v6_select() directly, even though there are other places in the lwIP
1698*ef8d499eSDavid van Moolenbroek * source code that explicitly call this functions.
1699*ef8d499eSDavid van Moolenbroek */
1700*ef8d499eSDavid van Moolenbroek const ip_addr_t *
ip6_select_source_address(struct netif * netif,const ip6_addr_t * dest_addr)1701*ef8d499eSDavid van Moolenbroek ip6_select_source_address(struct netif * netif, const ip6_addr_t * dest_addr)
1702*ef8d499eSDavid van Moolenbroek {
1703*ef8d499eSDavid van Moolenbroek ip_addr_t ipaddr;
1704*ef8d499eSDavid van Moolenbroek
1705*ef8d499eSDavid van Moolenbroek ip_addr_copy_from_ip6(ipaddr, *dest_addr);
1706*ef8d499eSDavid van Moolenbroek
1707*ef8d499eSDavid van Moolenbroek return ifaddr_v6_select(netif_get_ifdev(netif), &ipaddr);
1708*ef8d499eSDavid van Moolenbroek }
1709*ef8d499eSDavid van Moolenbroek
1710*ef8d499eSDavid van Moolenbroek /*
1711*ef8d499eSDavid van Moolenbroek * Find and return the interface to which the given address is assigned as a
1712*ef8d499eSDavid van Moolenbroek * local (source) address, or NULL if the given address is not a local address
1713*ef8d499eSDavid van Moolenbroek * for any interface. The 'any' address as well as IPv4-mapped IPv6 addresses
1714*ef8d499eSDavid van Moolenbroek * are not supported and will yield NULL.
1715*ef8d499eSDavid van Moolenbroek */
1716*ef8d499eSDavid van Moolenbroek struct ifdev *
ifaddr_map_by_addr(const ip_addr_t * ipaddr)1717*ef8d499eSDavid van Moolenbroek ifaddr_map_by_addr(const ip_addr_t * ipaddr)
1718*ef8d499eSDavid van Moolenbroek {
1719*ef8d499eSDavid van Moolenbroek
1720*ef8d499eSDavid van Moolenbroek switch (IP_GET_TYPE(ipaddr)) {
1721*ef8d499eSDavid van Moolenbroek case IPADDR_TYPE_V4:
1722*ef8d499eSDavid van Moolenbroek return ifaddr_v4_map_by_addr(ip_2_ip4(ipaddr));
1723*ef8d499eSDavid van Moolenbroek
1724*ef8d499eSDavid van Moolenbroek case IPADDR_TYPE_V6:
1725*ef8d499eSDavid van Moolenbroek if (ip6_addr_isipv4mappedipv6(ip_2_ip6(ipaddr)))
1726*ef8d499eSDavid van Moolenbroek return NULL;
1727*ef8d499eSDavid van Moolenbroek
1728*ef8d499eSDavid van Moolenbroek return ifaddr_v6_map_by_addr(ip_2_ip6(ipaddr));
1729*ef8d499eSDavid van Moolenbroek
1730*ef8d499eSDavid van Moolenbroek case IPADDR_TYPE_ANY:
1731*ef8d499eSDavid van Moolenbroek return NULL;
1732*ef8d499eSDavid van Moolenbroek
1733*ef8d499eSDavid van Moolenbroek default:
1734*ef8d499eSDavid van Moolenbroek panic("unknown IP address type: %u", IP_GET_TYPE(ipaddr));
1735*ef8d499eSDavid van Moolenbroek }
1736*ef8d499eSDavid van Moolenbroek }
1737*ef8d499eSDavid van Moolenbroek
1738*ef8d499eSDavid van Moolenbroek /*
1739*ef8d499eSDavid van Moolenbroek * Find and return an interface that has a local network configured that
1740*ef8d499eSDavid van Moolenbroek * contains the given address, or NULL if there is no match. If there are
1741*ef8d499eSDavid van Moolenbroek * multiple matches, an arbitrary one is returned. The 'any' address as well
1742*ef8d499eSDavid van Moolenbroek * as IPv4-mapped IPv6 addresses are not supported and will yield NULL.
1743*ef8d499eSDavid van Moolenbroek */
1744*ef8d499eSDavid van Moolenbroek struct ifdev *
ifaddr_map_by_subnet(const ip_addr_t * ipaddr)1745*ef8d499eSDavid van Moolenbroek ifaddr_map_by_subnet(const ip_addr_t * ipaddr)
1746*ef8d499eSDavid van Moolenbroek {
1747*ef8d499eSDavid van Moolenbroek
1748*ef8d499eSDavid van Moolenbroek switch (IP_GET_TYPE(ipaddr)) {
1749*ef8d499eSDavid van Moolenbroek case IPADDR_TYPE_V4:
1750*ef8d499eSDavid van Moolenbroek return ifaddr_v4_map_by_subnet(ip_2_ip4(ipaddr));
1751*ef8d499eSDavid van Moolenbroek
1752*ef8d499eSDavid van Moolenbroek case IPADDR_TYPE_V6:
1753*ef8d499eSDavid van Moolenbroek if (ip6_addr_isipv4mappedipv6(ip_2_ip6(ipaddr)))
1754*ef8d499eSDavid van Moolenbroek return NULL;
1755*ef8d499eSDavid van Moolenbroek
1756*ef8d499eSDavid van Moolenbroek return ifaddr_v6_map_by_subnet(ipaddr);
1757*ef8d499eSDavid van Moolenbroek
1758*ef8d499eSDavid van Moolenbroek case IPADDR_TYPE_ANY:
1759*ef8d499eSDavid van Moolenbroek return NULL;
1760*ef8d499eSDavid van Moolenbroek
1761*ef8d499eSDavid van Moolenbroek default:
1762*ef8d499eSDavid van Moolenbroek panic("unknown IP address type: %u", IP_GET_TYPE(ipaddr));
1763*ef8d499eSDavid van Moolenbroek }
1764*ef8d499eSDavid van Moolenbroek }
1765*ef8d499eSDavid van Moolenbroek
1766*ef8d499eSDavid van Moolenbroek /*
1767*ef8d499eSDavid van Moolenbroek * Select a local address to use as source address for the given destination
1768*ef8d499eSDavid van Moolenbroek * address. If 'ifdev' is not NULL, it points to the interface from which to
1769*ef8d499eSDavid van Moolenbroek * select a source address. If 'ifdev' is NULL, this function will attempt to
1770*ef8d499eSDavid van Moolenbroek * select an interface as well. On success, return the selected source
1771*ef8d499eSDavid van Moolenbroek * address, and if 'ifdevp' is not NULL, store the selected interface in it.
1772*ef8d499eSDavid van Moolenbroek * On failure, return NULL.
1773*ef8d499eSDavid van Moolenbroek */
1774*ef8d499eSDavid van Moolenbroek const ip_addr_t *
ifaddr_select(const ip_addr_t * dst_addr,struct ifdev * ifdev,struct ifdev ** ifdevp)1775*ef8d499eSDavid van Moolenbroek ifaddr_select(const ip_addr_t * dst_addr, struct ifdev * ifdev,
1776*ef8d499eSDavid van Moolenbroek struct ifdev ** ifdevp)
1777*ef8d499eSDavid van Moolenbroek {
1778*ef8d499eSDavid van Moolenbroek struct route_entry *route;
1779*ef8d499eSDavid van Moolenbroek const ip6_addr_t *ip6addr;
1780*ef8d499eSDavid van Moolenbroek
1781*ef8d499eSDavid van Moolenbroek /*
1782*ef8d499eSDavid van Moolenbroek * If no interface is provided yet, start by determining the interface.
1783*ef8d499eSDavid van Moolenbroek * If the destination address has a zone, this step is easy. Otherwise
1784*ef8d499eSDavid van Moolenbroek * we have to do a routing query on the destination address.
1785*ef8d499eSDavid van Moolenbroek */
1786*ef8d499eSDavid van Moolenbroek if (ifdev == NULL) {
1787*ef8d499eSDavid van Moolenbroek ip6addr = ip_2_ip6(dst_addr);
1788*ef8d499eSDavid van Moolenbroek
1789*ef8d499eSDavid van Moolenbroek if (IP_IS_V6(dst_addr) && ip6_addr_has_zone(ip6addr)) {
1790*ef8d499eSDavid van Moolenbroek ifdev = ifdev_get_by_index(ip6_addr_zone(ip6addr));
1791*ef8d499eSDavid van Moolenbroek
1792*ef8d499eSDavid van Moolenbroek if (ifdev == NULL)
1793*ef8d499eSDavid van Moolenbroek return NULL;
1794*ef8d499eSDavid van Moolenbroek } else {
1795*ef8d499eSDavid van Moolenbroek if ((route = route_lookup(dst_addr)) == NULL)
1796*ef8d499eSDavid van Moolenbroek return NULL;
1797*ef8d499eSDavid van Moolenbroek
1798*ef8d499eSDavid van Moolenbroek ifdev = route_get_ifdev(route);
1799*ef8d499eSDavid van Moolenbroek }
1800*ef8d499eSDavid van Moolenbroek }
1801*ef8d499eSDavid van Moolenbroek
1802*ef8d499eSDavid van Moolenbroek if (ifdevp != NULL)
1803*ef8d499eSDavid van Moolenbroek *ifdevp = ifdev;
1804*ef8d499eSDavid van Moolenbroek
1805*ef8d499eSDavid van Moolenbroek /*
1806*ef8d499eSDavid van Moolenbroek * We have found an interface. Now select an IP address assigned to
1807*ef8d499eSDavid van Moolenbroek * that interface. For IPv4, this is easy: each interface has only one
1808*ef8d499eSDavid van Moolenbroek * local address (if that). For IPv6, we may have to select one of the
1809*ef8d499eSDavid van Moolenbroek * locally assigned addresses: global, link-local, etc.
1810*ef8d499eSDavid van Moolenbroek */
1811*ef8d499eSDavid van Moolenbroek switch (IP_GET_TYPE(dst_addr)) {
1812*ef8d499eSDavid van Moolenbroek case IPADDR_TYPE_V4:
1813*ef8d499eSDavid van Moolenbroek /* Use the IPv4 source address if one is set at all. */
1814*ef8d499eSDavid van Moolenbroek if (!ifdev->ifdev_v4set)
1815*ef8d499eSDavid van Moolenbroek return FALSE;
1816*ef8d499eSDavid van Moolenbroek
1817*ef8d499eSDavid van Moolenbroek return netif_ip_addr4(ifdev_get_netif(ifdev));
1818*ef8d499eSDavid van Moolenbroek
1819*ef8d499eSDavid van Moolenbroek case IPADDR_TYPE_V6:
1820*ef8d499eSDavid van Moolenbroek return ifaddr_v6_select(ifdev, dst_addr);
1821*ef8d499eSDavid van Moolenbroek
1822*ef8d499eSDavid van Moolenbroek default:
1823*ef8d499eSDavid van Moolenbroek panic("unknown IP address type: %u", IP_GET_TYPE(dst_addr));
1824*ef8d499eSDavid van Moolenbroek }
1825*ef8d499eSDavid van Moolenbroek }
1826*ef8d499eSDavid van Moolenbroek
1827*ef8d499eSDavid van Moolenbroek /*
1828*ef8d499eSDavid van Moolenbroek * Check the given IPv6 address for a zone violation against the given
1829*ef8d499eSDavid van Moolenbroek * interface--that is, a scoped address leaving its original zone if used in
1830*ef8d499eSDavid van Moolenbroek * the context of the interface. Return TRUE if the address is zone-
1831*ef8d499eSDavid van Moolenbroek * incompatible with the interface, and thus must not be used in packets sent
1832*ef8d499eSDavid van Moolenbroek * to that interface. Return FALSE if there is no such zone incompatibility.
1833*ef8d499eSDavid van Moolenbroek */
1834*ef8d499eSDavid van Moolenbroek int
ifaddr_is_zone_mismatch(const ip6_addr_t * ipaddr,struct ifdev * ifdev)1835*ef8d499eSDavid van Moolenbroek ifaddr_is_zone_mismatch(const ip6_addr_t * ipaddr, struct ifdev * ifdev)
1836*ef8d499eSDavid van Moolenbroek {
1837*ef8d499eSDavid van Moolenbroek
1838*ef8d499eSDavid van Moolenbroek /*
1839*ef8d499eSDavid van Moolenbroek * The IPv6 loopback address (::1) has an implicit link-local scope,
1840*ef8d499eSDavid van Moolenbroek * with a zone corresponding to the interface it is assigned to. We
1841*ef8d499eSDavid van Moolenbroek * take a shortcut by assuming that the loopback address is assigned to
1842*ef8d499eSDavid van Moolenbroek * the primary loopback interface.
1843*ef8d499eSDavid van Moolenbroek */
1844*ef8d499eSDavid van Moolenbroek if (ip6_addr_isloopback(ipaddr))
1845*ef8d499eSDavid van Moolenbroek return (ifdev != ifdev_get_loopback());
1846*ef8d499eSDavid van Moolenbroek
1847*ef8d499eSDavid van Moolenbroek /* Zoned addresses must not leave their zone. */
1848*ef8d499eSDavid van Moolenbroek if (ip6_addr_has_zone(ipaddr))
1849*ef8d499eSDavid van Moolenbroek return !ip6_addr_test_zone(ipaddr, ifdev_get_netif(ifdev));
1850*ef8d499eSDavid van Moolenbroek
1851*ef8d499eSDavid van Moolenbroek return FALSE;
1852*ef8d499eSDavid van Moolenbroek }
1853*ef8d499eSDavid van Moolenbroek
1854*ef8d499eSDavid van Moolenbroek /*
1855*ef8d499eSDavid van Moolenbroek * Find a data link (hardware) address locally assigned to a interface. The
1856*ef8d499eSDavid van Moolenbroek * address is given as 'addr', and the length of the memory area that contains
1857*ef8d499eSDavid van Moolenbroek * 'addr' is given as 'addr_len'. The interface is given as 'ifdev'. On
1858*ef8d499eSDavid van Moolenbroek * success, return OK, with the data link address number stored in 'num'. For
1859*ef8d499eSDavid van Moolenbroek * interfaces that do not support hardware addresses, if the given address
1860*ef8d499eSDavid van Moolenbroek * provides a zero-length hardware address, always return successfully with 0
1861*ef8d499eSDavid van Moolenbroek * stored in 'nump'. On failure, return a negative error code.
1862*ef8d499eSDavid van Moolenbroek */
1863*ef8d499eSDavid van Moolenbroek int
ifaddr_dl_find(struct ifdev * ifdev,const struct sockaddr_dlx * addr,socklen_t addr_len,ifaddr_dl_num_t * nump)1864*ef8d499eSDavid van Moolenbroek ifaddr_dl_find(struct ifdev * ifdev, const struct sockaddr_dlx * addr,
1865*ef8d499eSDavid van Moolenbroek socklen_t addr_len, ifaddr_dl_num_t * nump)
1866*ef8d499eSDavid van Moolenbroek {
1867*ef8d499eSDavid van Moolenbroek uint8_t hwaddr[NETIF_MAX_HWADDR_LEN];
1868*ef8d499eSDavid van Moolenbroek ifaddr_dl_num_t num;
1869*ef8d499eSDavid van Moolenbroek int r;
1870*ef8d499eSDavid van Moolenbroek
1871*ef8d499eSDavid van Moolenbroek if ((r = addr_get_link((const struct sockaddr *)addr, addr_len,
1872*ef8d499eSDavid van Moolenbroek NULL /*name*/, 0 /*name_max*/, hwaddr,
1873*ef8d499eSDavid van Moolenbroek ifdev_get_hwlen(ifdev))) != OK)
1874*ef8d499eSDavid van Moolenbroek return r;
1875*ef8d499eSDavid van Moolenbroek
1876*ef8d499eSDavid van Moolenbroek /*
1877*ef8d499eSDavid van Moolenbroek * For interfaces without hardware addresses, after passing the above
1878*ef8d499eSDavid van Moolenbroek * sanity checks (which guarantee that the searched-for address is of
1879*ef8d499eSDavid van Moolenbroek * zero length), return the pseudo-entry zero, which yields an entry
1880*ef8d499eSDavid van Moolenbroek * with a zero-sized hardware address once obtained. This is required
1881*ef8d499eSDavid van Moolenbroek * for at least ifconfig(8).
1882*ef8d499eSDavid van Moolenbroek */
1883*ef8d499eSDavid van Moolenbroek if (ifdev->ifdev_ops->iop_set_hwaddr == NULL) {
1884*ef8d499eSDavid van Moolenbroek *nump = 0;
1885*ef8d499eSDavid van Moolenbroek return OK;
1886*ef8d499eSDavid van Moolenbroek }
1887*ef8d499eSDavid van Moolenbroek
1888*ef8d499eSDavid van Moolenbroek for (num = 0; (size_t)num < __arraycount(ifdev->ifdev_hwlist); num++) {
1889*ef8d499eSDavid van Moolenbroek if ((ifdev->ifdev_hwlist[num].ifhwa_flags & IFHWAF_VALID) &&
1890*ef8d499eSDavid van Moolenbroek !memcmp(ifdev->ifdev_hwlist[num].ifhwa_addr, hwaddr,
1891*ef8d499eSDavid van Moolenbroek ifdev_get_hwlen(ifdev))) {
1892*ef8d499eSDavid van Moolenbroek *nump = num;
1893*ef8d499eSDavid van Moolenbroek return OK;
1894*ef8d499eSDavid van Moolenbroek }
1895*ef8d499eSDavid van Moolenbroek }
1896*ef8d499eSDavid van Moolenbroek
1897*ef8d499eSDavid van Moolenbroek return EADDRNOTAVAIL;
1898*ef8d499eSDavid van Moolenbroek }
1899*ef8d499eSDavid van Moolenbroek
1900*ef8d499eSDavid van Moolenbroek /*
1901*ef8d499eSDavid van Moolenbroek * Enumerate data link (hardware) addresses locally assigned to the given
1902*ef8d499eSDavid van Moolenbroek * interface 'ifdev'. The caller should set 'nump' to 0 initially, and
1903*ef8d499eSDavid van Moolenbroek * increase it by one between a successful call and the next enumeration call.
1904*ef8d499eSDavid van Moolenbroek * Return TRUE on success, meaning that starting from the given value of 'nump'
1905*ef8d499eSDavid van Moolenbroek * there is at least one data link address, of which the number is stored in
1906*ef8d499eSDavid van Moolenbroek * 'nump' on return. Return FALSE if there are no more data link addresses
1907*ef8d499eSDavid van Moolenbroek * locally assigned to the interface.
1908*ef8d499eSDavid van Moolenbroek */
1909*ef8d499eSDavid van Moolenbroek int
ifaddr_dl_enum(struct ifdev * ifdev,ifaddr_dl_num_t * num)1910*ef8d499eSDavid van Moolenbroek ifaddr_dl_enum(struct ifdev * ifdev, ifaddr_dl_num_t * num)
1911*ef8d499eSDavid van Moolenbroek {
1912*ef8d499eSDavid van Moolenbroek
1913*ef8d499eSDavid van Moolenbroek /*
1914*ef8d499eSDavid van Moolenbroek * If hardware addresses are not supported, or if no hardware address
1915*ef8d499eSDavid van Moolenbroek * has been added to this interface yet (this shouldn't happen but
1916*ef8d499eSDavid van Moolenbroek * still), there is always one entry with a (zero-sized) address.
1917*ef8d499eSDavid van Moolenbroek * That is required for the IFP (name) entry as used by getifaddrs(3).
1918*ef8d499eSDavid van Moolenbroek */
1919*ef8d499eSDavid van Moolenbroek if (ifdev->ifdev_ops->iop_set_hwaddr == NULL ||
1920*ef8d499eSDavid van Moolenbroek !(ifdev->ifdev_hwlist[0].ifhwa_flags & IFHWAF_VALID))
1921*ef8d499eSDavid van Moolenbroek return (*num == 0);
1922*ef8d499eSDavid van Moolenbroek
1923*ef8d499eSDavid van Moolenbroek for (; (size_t)*num < __arraycount(ifdev->ifdev_hwlist); (*num)++) {
1924*ef8d499eSDavid van Moolenbroek if (ifdev->ifdev_hwlist[*num].ifhwa_flags & IFHWAF_VALID)
1925*ef8d499eSDavid van Moolenbroek return TRUE;
1926*ef8d499eSDavid van Moolenbroek }
1927*ef8d499eSDavid van Moolenbroek
1928*ef8d499eSDavid van Moolenbroek return FALSE;
1929*ef8d499eSDavid van Moolenbroek }
1930*ef8d499eSDavid van Moolenbroek
1931*ef8d499eSDavid van Moolenbroek /*
1932*ef8d499eSDavid van Moolenbroek * Retrieve a data link (hardware) address for an interface. For interfaces
1933*ef8d499eSDavid van Moolenbroek * that support hardware addresses, 'num' must be a number returned by
1934*ef8d499eSDavid van Moolenbroek * ifaddr_dl_find() or ifaddr_dl_enum(). For others, 'num' must be zero, and a
1935*ef8d499eSDavid van Moolenbroek * pseudo-address of zero size will be returned. The address will be stored in
1936*ef8d499eSDavid van Moolenbroek * 'addr'. This function always succeeds.
1937*ef8d499eSDavid van Moolenbroek */
1938*ef8d499eSDavid van Moolenbroek void
ifaddr_dl_get(struct ifdev * ifdev,ifaddr_dl_num_t num,struct sockaddr_dlx * addr)1939*ef8d499eSDavid van Moolenbroek ifaddr_dl_get(struct ifdev * ifdev, ifaddr_dl_num_t num,
1940*ef8d499eSDavid van Moolenbroek struct sockaddr_dlx * addr)
1941*ef8d499eSDavid van Moolenbroek {
1942*ef8d499eSDavid van Moolenbroek const uint8_t *hwaddr;
1943*ef8d499eSDavid van Moolenbroek size_t hwaddr_len;
1944*ef8d499eSDavid van Moolenbroek socklen_t addr_len;
1945*ef8d499eSDavid van Moolenbroek
1946*ef8d499eSDavid van Moolenbroek if ((hwaddr_len = ifdev_get_hwlen(ifdev)) > 0) {
1947*ef8d499eSDavid van Moolenbroek /*
1948*ef8d499eSDavid van Moolenbroek * Note that if we have no hardware addresses yet (which should
1949*ef8d499eSDavid van Moolenbroek * not happen but still), the first entry may not be marked as
1950*ef8d499eSDavid van Moolenbroek * valid yet. Ignore it, and return an all-zeroes address.
1951*ef8d499eSDavid van Moolenbroek */
1952*ef8d499eSDavid van Moolenbroek hwaddr = ifdev->ifdev_hwlist[num].ifhwa_addr;
1953*ef8d499eSDavid van Moolenbroek } else
1954*ef8d499eSDavid van Moolenbroek hwaddr = NULL;
1955*ef8d499eSDavid van Moolenbroek
1956*ef8d499eSDavid van Moolenbroek addr_len = sizeof(*addr);
1957*ef8d499eSDavid van Moolenbroek
1958*ef8d499eSDavid van Moolenbroek addr_put_link((struct sockaddr *)addr, &addr_len,
1959*ef8d499eSDavid van Moolenbroek ifdev_get_index(ifdev), ifdev_get_iftype(ifdev),
1960*ef8d499eSDavid van Moolenbroek ifdev_get_name(ifdev), hwaddr, hwaddr_len);
1961*ef8d499eSDavid van Moolenbroek }
1962*ef8d499eSDavid van Moolenbroek
1963*ef8d499eSDavid van Moolenbroek /*
1964*ef8d499eSDavid van Moolenbroek * Obtain NetBSD-style state flags (IFLR_) for the given local data link
1965*ef8d499eSDavid van Moolenbroek * address. The given number may be 0, in which case that slot's state may not
1966*ef8d499eSDavid van Moolenbroek * be valid. Otherwise, the given number must identify an existing address.
1967*ef8d499eSDavid van Moolenbroek * Return the flags, 0 if the slot was not valid.
1968*ef8d499eSDavid van Moolenbroek */
1969*ef8d499eSDavid van Moolenbroek int
ifaddr_dl_get_flags(struct ifdev * ifdev,ifaddr_dl_num_t num)1970*ef8d499eSDavid van Moolenbroek ifaddr_dl_get_flags(struct ifdev * ifdev, ifaddr_dl_num_t num)
1971*ef8d499eSDavid van Moolenbroek {
1972*ef8d499eSDavid van Moolenbroek int flags;
1973*ef8d499eSDavid van Moolenbroek
1974*ef8d499eSDavid van Moolenbroek assert(num >= 0 && (size_t)num < __arraycount(ifdev->ifdev_hwlist));
1975*ef8d499eSDavid van Moolenbroek
1976*ef8d499eSDavid van Moolenbroek if (!(ifdev->ifdev_hwlist[num].ifhwa_flags & IFHWAF_VALID))
1977*ef8d499eSDavid van Moolenbroek return 0;
1978*ef8d499eSDavid van Moolenbroek
1979*ef8d499eSDavid van Moolenbroek flags = (num == 0) ? IFLR_ACTIVE : 0;
1980*ef8d499eSDavid van Moolenbroek
1981*ef8d499eSDavid van Moolenbroek if (ifdev->ifdev_hwlist[num].ifhwa_flags & IFHWAF_FACTORY)
1982*ef8d499eSDavid van Moolenbroek flags |= IFLR_FACTORY;
1983*ef8d499eSDavid van Moolenbroek
1984*ef8d499eSDavid van Moolenbroek return flags;
1985*ef8d499eSDavid van Moolenbroek }
1986*ef8d499eSDavid van Moolenbroek
1987*ef8d499eSDavid van Moolenbroek /*
1988*ef8d499eSDavid van Moolenbroek * Scan the list of hardware addresses of the given interface for a particular
1989*ef8d499eSDavid van Moolenbroek * hardware address, as well as for an available entry. Return the entry found
1990*ef8d499eSDavid van Moolenbroek * or -1 if the given hardware address was not found. Independently, return an
1991*ef8d499eSDavid van Moolenbroek * available entry in 'availp' or -1 if no entries are available.
1992*ef8d499eSDavid van Moolenbroek */
1993*ef8d499eSDavid van Moolenbroek static ifaddr_dl_num_t
ifaddr_dl_scan(struct ifdev * ifdev,const uint8_t * hwaddr,ifaddr_dl_num_t * availp)1994*ef8d499eSDavid van Moolenbroek ifaddr_dl_scan(struct ifdev * ifdev, const uint8_t * hwaddr,
1995*ef8d499eSDavid van Moolenbroek ifaddr_dl_num_t * availp)
1996*ef8d499eSDavid van Moolenbroek {
1997*ef8d499eSDavid van Moolenbroek ifaddr_dl_num_t num, found, avail;
1998*ef8d499eSDavid van Moolenbroek
1999*ef8d499eSDavid van Moolenbroek found = avail = -1;
2000*ef8d499eSDavid van Moolenbroek
2001*ef8d499eSDavid van Moolenbroek for (num = 0; (size_t)num < __arraycount(ifdev->ifdev_hwlist); num++) {
2002*ef8d499eSDavid van Moolenbroek if (!(ifdev->ifdev_hwlist[num].ifhwa_flags & IFHWAF_VALID)) {
2003*ef8d499eSDavid van Moolenbroek if (avail == -1)
2004*ef8d499eSDavid van Moolenbroek avail = num;
2005*ef8d499eSDavid van Moolenbroek } else if (!memcmp(ifdev->ifdev_hwlist[num].ifhwa_addr, hwaddr,
2006*ef8d499eSDavid van Moolenbroek ifdev_get_hwlen(ifdev)))
2007*ef8d499eSDavid van Moolenbroek found = num;
2008*ef8d499eSDavid van Moolenbroek }
2009*ef8d499eSDavid van Moolenbroek
2010*ef8d499eSDavid van Moolenbroek *availp = avail;
2011*ef8d499eSDavid van Moolenbroek return found;
2012*ef8d499eSDavid van Moolenbroek }
2013*ef8d499eSDavid van Moolenbroek
2014*ef8d499eSDavid van Moolenbroek /*
2015*ef8d499eSDavid van Moolenbroek * Set a hardware address entry in the hardware address list of the given
2016*ef8d499eSDavid van Moolenbroek * interface.
2017*ef8d499eSDavid van Moolenbroek */
2018*ef8d499eSDavid van Moolenbroek static void
ifaddr_dl_set(struct ifdev * ifdev,ifaddr_dl_num_t num,const uint8_t * hwaddr,int is_factory)2019*ef8d499eSDavid van Moolenbroek ifaddr_dl_set(struct ifdev * ifdev, ifaddr_dl_num_t num,
2020*ef8d499eSDavid van Moolenbroek const uint8_t * hwaddr, int is_factory)
2021*ef8d499eSDavid van Moolenbroek {
2022*ef8d499eSDavid van Moolenbroek
2023*ef8d499eSDavid van Moolenbroek memcpy(&ifdev->ifdev_hwlist[num].ifhwa_addr, hwaddr,
2024*ef8d499eSDavid van Moolenbroek ifdev_get_hwlen(ifdev));
2025*ef8d499eSDavid van Moolenbroek
2026*ef8d499eSDavid van Moolenbroek ifdev->ifdev_hwlist[num].ifhwa_flags = IFHWAF_VALID;
2027*ef8d499eSDavid van Moolenbroek if (is_factory)
2028*ef8d499eSDavid van Moolenbroek ifdev->ifdev_hwlist[num].ifhwa_flags |= IFHWAF_FACTORY;
2029*ef8d499eSDavid van Moolenbroek
2030*ef8d499eSDavid van Moolenbroek rtsock_msg_addr_dl(ifdev, RTM_NEWADDR, num);
2031*ef8d499eSDavid van Moolenbroek }
2032*ef8d499eSDavid van Moolenbroek
2033*ef8d499eSDavid van Moolenbroek /*
2034*ef8d499eSDavid van Moolenbroek * Mark a new hardware address as active, after it has already been activated
2035*ef8d499eSDavid van Moolenbroek * on the hardware and in local administration. The active slot is always slot
2036*ef8d499eSDavid van Moolenbroek * zero, so swap slots if needed.
2037*ef8d499eSDavid van Moolenbroek */
2038*ef8d499eSDavid van Moolenbroek static void
ifaddr_dl_activate(struct ifdev * ifdev,ifaddr_dl_num_t num)2039*ef8d499eSDavid van Moolenbroek ifaddr_dl_activate(struct ifdev * ifdev, ifaddr_dl_num_t num)
2040*ef8d499eSDavid van Moolenbroek {
2041*ef8d499eSDavid van Moolenbroek struct ifdev_hwaddr tmp;
2042*ef8d499eSDavid van Moolenbroek struct netif *netif;
2043*ef8d499eSDavid van Moolenbroek size_t sz;
2044*ef8d499eSDavid van Moolenbroek
2045*ef8d499eSDavid van Moolenbroek assert(num != -1);
2046*ef8d499eSDavid van Moolenbroek
2047*ef8d499eSDavid van Moolenbroek /* The given slot may be zero if this is the initial address. */
2048*ef8d499eSDavid van Moolenbroek if (num != 0) {
2049*ef8d499eSDavid van Moolenbroek sz = sizeof(tmp);
2050*ef8d499eSDavid van Moolenbroek memcpy(&tmp, &ifdev->ifdev_hwlist[0], sz);
2051*ef8d499eSDavid van Moolenbroek memcpy(&ifdev->ifdev_hwlist[0], &ifdev->ifdev_hwlist[num], sz);
2052*ef8d499eSDavid van Moolenbroek memcpy(&ifdev->ifdev_hwlist[num], &tmp, sz);
2053*ef8d499eSDavid van Moolenbroek }
2054*ef8d499eSDavid van Moolenbroek
2055*ef8d499eSDavid van Moolenbroek netif = ifdev_get_netif(ifdev);
2056*ef8d499eSDavid van Moolenbroek
2057*ef8d499eSDavid van Moolenbroek /* Tell lwIP and routing sockets. */
2058*ef8d499eSDavid van Moolenbroek memcpy(&netif->hwaddr, &ifdev->ifdev_hwlist[0].ifhwa_addr,
2059*ef8d499eSDavid van Moolenbroek ifdev_get_hwlen(ifdev));
2060*ef8d499eSDavid van Moolenbroek
2061*ef8d499eSDavid van Moolenbroek rtsock_msg_addr_dl(ifdev, RTM_CHGADDR, 0);
2062*ef8d499eSDavid van Moolenbroek
2063*ef8d499eSDavid van Moolenbroek /* See if we can and should generate a link-local IPv6 address now. */
2064*ef8d499eSDavid van Moolenbroek ifaddr_v6_set_linklocal(ifdev);
2065*ef8d499eSDavid van Moolenbroek }
2066*ef8d499eSDavid van Moolenbroek
2067*ef8d499eSDavid van Moolenbroek /*
2068*ef8d499eSDavid van Moolenbroek * Add a data link (hardware) address to an interface, or if it already exists,
2069*ef8d499eSDavid van Moolenbroek * update its associated flags (IFLR_).
2070*ef8d499eSDavid van Moolenbroek */
2071*ef8d499eSDavid van Moolenbroek int
ifaddr_dl_add(struct ifdev * ifdev,const struct sockaddr_dlx * addr,socklen_t addr_len,int flags)2072*ef8d499eSDavid van Moolenbroek ifaddr_dl_add(struct ifdev * ifdev, const struct sockaddr_dlx * addr,
2073*ef8d499eSDavid van Moolenbroek socklen_t addr_len, int flags)
2074*ef8d499eSDavid van Moolenbroek {
2075*ef8d499eSDavid van Moolenbroek uint8_t hwaddr[NETIF_MAX_HWADDR_LEN];
2076*ef8d499eSDavid van Moolenbroek ifaddr_dl_num_t found, avail;
2077*ef8d499eSDavid van Moolenbroek int r;
2078*ef8d499eSDavid van Moolenbroek
2079*ef8d499eSDavid van Moolenbroek /*
2080*ef8d499eSDavid van Moolenbroek * If this interface type does not support setting hardware addresses,
2081*ef8d499eSDavid van Moolenbroek * refuse the call. If the interface type supports it but the
2082*ef8d499eSDavid van Moolenbroek * underlying hardware does not, we cannot report failure here, though.
2083*ef8d499eSDavid van Moolenbroek * In that case, attempts to activate an address will fail instead.
2084*ef8d499eSDavid van Moolenbroek */
2085*ef8d499eSDavid van Moolenbroek if (ifdev->ifdev_ops->iop_set_hwaddr == NULL)
2086*ef8d499eSDavid van Moolenbroek return EINVAL;
2087*ef8d499eSDavid van Moolenbroek
2088*ef8d499eSDavid van Moolenbroek if ((r = addr_get_link((const struct sockaddr *)addr, addr_len,
2089*ef8d499eSDavid van Moolenbroek NULL /*name*/, 0 /*name_max*/, hwaddr,
2090*ef8d499eSDavid van Moolenbroek ifdev_get_hwlen(ifdev))) != OK)
2091*ef8d499eSDavid van Moolenbroek return r;
2092*ef8d499eSDavid van Moolenbroek
2093*ef8d499eSDavid van Moolenbroek /*
2094*ef8d499eSDavid van Moolenbroek * Find the slot for the given hardware address. Also find the slot of
2095*ef8d499eSDavid van Moolenbroek * the active address, and a free slot. All of these may not exist.
2096*ef8d499eSDavid van Moolenbroek */
2097*ef8d499eSDavid van Moolenbroek found = ifaddr_dl_scan(ifdev, hwaddr, &avail);
2098*ef8d499eSDavid van Moolenbroek
2099*ef8d499eSDavid van Moolenbroek if (found == -1) {
2100*ef8d499eSDavid van Moolenbroek if (avail == -1)
2101*ef8d499eSDavid van Moolenbroek return ENOBUFS; /* TODO: a better error code */
2102*ef8d499eSDavid van Moolenbroek found = avail;
2103*ef8d499eSDavid van Moolenbroek }
2104*ef8d499eSDavid van Moolenbroek
2105*ef8d499eSDavid van Moolenbroek /*
2106*ef8d499eSDavid van Moolenbroek * If we are asked to activate this address, try that first: this may
2107*ef8d499eSDavid van Moolenbroek * fail if the network device does not support setting addresses, in
2108*ef8d499eSDavid van Moolenbroek * which case we want to fail without causing routing socket noise.
2109*ef8d499eSDavid van Moolenbroek */
2110*ef8d499eSDavid van Moolenbroek if ((flags & IFLR_ACTIVE) && found != 0 &&
2111*ef8d499eSDavid van Moolenbroek (r = ifdev->ifdev_ops->iop_set_hwaddr(ifdev, hwaddr)) != OK)
2112*ef8d499eSDavid van Moolenbroek return r;
2113*ef8d499eSDavid van Moolenbroek
2114*ef8d499eSDavid van Moolenbroek /*
2115*ef8d499eSDavid van Moolenbroek * If this is a new address, add and announce it. Otherwise, just
2116*ef8d499eSDavid van Moolenbroek * update its flags.
2117*ef8d499eSDavid van Moolenbroek */
2118*ef8d499eSDavid van Moolenbroek if (found == avail) {
2119*ef8d499eSDavid van Moolenbroek ifaddr_dl_set(ifdev, found, hwaddr,
2120*ef8d499eSDavid van Moolenbroek (flags & IFLR_FACTORY));
2121*ef8d499eSDavid van Moolenbroek } else {
2122*ef8d499eSDavid van Moolenbroek ifdev->ifdev_hwlist[found].ifhwa_flags &= ~IFLR_FACTORY;
2123*ef8d499eSDavid van Moolenbroek if (flags & IFLR_FACTORY)
2124*ef8d499eSDavid van Moolenbroek ifdev->ifdev_hwlist[found].ifhwa_flags |= IFLR_FACTORY;
2125*ef8d499eSDavid van Moolenbroek }
2126*ef8d499eSDavid van Moolenbroek
2127*ef8d499eSDavid van Moolenbroek /*
2128*ef8d499eSDavid van Moolenbroek * Activate the address if requested, swapping slots as needed. It is
2129*ef8d499eSDavid van Moolenbroek * not possible to deactivate the active address by changing its flags.
2130*ef8d499eSDavid van Moolenbroek */
2131*ef8d499eSDavid van Moolenbroek if ((flags & IFLR_ACTIVE) && found != 0)
2132*ef8d499eSDavid van Moolenbroek ifaddr_dl_activate(ifdev, found);
2133*ef8d499eSDavid van Moolenbroek
2134*ef8d499eSDavid van Moolenbroek return OK;
2135*ef8d499eSDavid van Moolenbroek }
2136*ef8d499eSDavid van Moolenbroek
2137*ef8d499eSDavid van Moolenbroek /*
2138*ef8d499eSDavid van Moolenbroek * Delete a data link (hardware) address from an interface.
2139*ef8d499eSDavid van Moolenbroek */
2140*ef8d499eSDavid van Moolenbroek int
ifaddr_dl_del(struct ifdev * ifdev,ifaddr_dl_num_t num)2141*ef8d499eSDavid van Moolenbroek ifaddr_dl_del(struct ifdev * ifdev, ifaddr_dl_num_t num)
2142*ef8d499eSDavid van Moolenbroek {
2143*ef8d499eSDavid van Moolenbroek
2144*ef8d499eSDavid van Moolenbroek if (ifdev->ifdev_ops->iop_set_hwaddr == NULL)
2145*ef8d499eSDavid van Moolenbroek return EINVAL;
2146*ef8d499eSDavid van Moolenbroek
2147*ef8d499eSDavid van Moolenbroek assert(num >= 0 && (size_t)num < __arraycount(ifdev->ifdev_hwlist));
2148*ef8d499eSDavid van Moolenbroek assert(ifdev->ifdev_hwlist[num].ifhwa_flags & IFHWAF_VALID);
2149*ef8d499eSDavid van Moolenbroek
2150*ef8d499eSDavid van Moolenbroek /* It is not possible to delete the active address. */
2151*ef8d499eSDavid van Moolenbroek if (num == 0)
2152*ef8d499eSDavid van Moolenbroek return EBUSY;
2153*ef8d499eSDavid van Moolenbroek
2154*ef8d499eSDavid van Moolenbroek rtsock_msg_addr_dl(ifdev, RTM_DELADDR, num);
2155*ef8d499eSDavid van Moolenbroek
2156*ef8d499eSDavid van Moolenbroek ifdev->ifdev_hwlist[num].ifhwa_flags = 0;
2157*ef8d499eSDavid van Moolenbroek
2158*ef8d499eSDavid van Moolenbroek return OK;
2159*ef8d499eSDavid van Moolenbroek }
2160*ef8d499eSDavid van Moolenbroek
2161*ef8d499eSDavid van Moolenbroek /*
2162*ef8d499eSDavid van Moolenbroek * Announce all data link (hardware) addresses associated with the given
2163*ef8d499eSDavid van Moolenbroek * interface as deleted, including the active address. Used (only) right
2164*ef8d499eSDavid van Moolenbroek * before the interface is destroyed.
2165*ef8d499eSDavid van Moolenbroek */
2166*ef8d499eSDavid van Moolenbroek void
ifaddr_dl_clear(struct ifdev * ifdev)2167*ef8d499eSDavid van Moolenbroek ifaddr_dl_clear(struct ifdev * ifdev)
2168*ef8d499eSDavid van Moolenbroek {
2169*ef8d499eSDavid van Moolenbroek ifaddr_dl_num_t num;
2170*ef8d499eSDavid van Moolenbroek
2171*ef8d499eSDavid van Moolenbroek /*
2172*ef8d499eSDavid van Moolenbroek * Do the active address last, because all announcements carry the
2173*ef8d499eSDavid van Moolenbroek * active address's hardware address as well.
2174*ef8d499eSDavid van Moolenbroek */
2175*ef8d499eSDavid van Moolenbroek for (num = 1; ifaddr_dl_enum(ifdev, &num); num++)
2176*ef8d499eSDavid van Moolenbroek rtsock_msg_addr_dl(ifdev, RTM_DELADDR, num);
2177*ef8d499eSDavid van Moolenbroek
2178*ef8d499eSDavid van Moolenbroek if (ifdev->ifdev_hwlist[0].ifhwa_flags & IFHWAF_VALID)
2179*ef8d499eSDavid van Moolenbroek rtsock_msg_addr_dl(ifdev, RTM_DELADDR, (ifaddr_dl_num_t)0);
2180*ef8d499eSDavid van Moolenbroek }
2181*ef8d499eSDavid van Moolenbroek
2182*ef8d499eSDavid van Moolenbroek /*
2183*ef8d499eSDavid van Moolenbroek * Update the interface's active hardware address. If the 'is_factory' flag is
2184*ef8d499eSDavid van Moolenbroek * set, the address is the factory (driver-given) address. This function may
2185*ef8d499eSDavid van Moolenbroek * only be called from ifdev_update_hwaddr().
2186*ef8d499eSDavid van Moolenbroek */
2187*ef8d499eSDavid van Moolenbroek void
ifaddr_dl_update(struct ifdev * ifdev,const uint8_t * hwaddr,int is_factory)2188*ef8d499eSDavid van Moolenbroek ifaddr_dl_update(struct ifdev * ifdev, const uint8_t * hwaddr, int is_factory)
2189*ef8d499eSDavid van Moolenbroek {
2190*ef8d499eSDavid van Moolenbroek ifaddr_dl_num_t found, avail;
2191*ef8d499eSDavid van Moolenbroek
2192*ef8d499eSDavid van Moolenbroek /*
2193*ef8d499eSDavid van Moolenbroek * Find the slot for the given hardware address. Also find the slot of
2194*ef8d499eSDavid van Moolenbroek * the active address, and a free slot. All of these may not exist.
2195*ef8d499eSDavid van Moolenbroek */
2196*ef8d499eSDavid van Moolenbroek found = ifaddr_dl_scan(ifdev, hwaddr, &avail);
2197*ef8d499eSDavid van Moolenbroek
2198*ef8d499eSDavid van Moolenbroek /* If the given address is already the active one, do nothing. */
2199*ef8d499eSDavid van Moolenbroek if (found == 0) {
2200*ef8d499eSDavid van Moolenbroek /* Factory addresses are always added first! */
2201*ef8d499eSDavid van Moolenbroek assert(!is_factory);
2202*ef8d499eSDavid van Moolenbroek
2203*ef8d499eSDavid van Moolenbroek return;
2204*ef8d499eSDavid van Moolenbroek }
2205*ef8d499eSDavid van Moolenbroek
2206*ef8d499eSDavid van Moolenbroek if (found == -1) {
2207*ef8d499eSDavid van Moolenbroek /*
2208*ef8d499eSDavid van Moolenbroek * If the given address is not in the list, add it. If the
2209*ef8d499eSDavid van Moolenbroek * list is full, first remove any non-active address. The user
2210*ef8d499eSDavid van Moolenbroek * won't like this, but it preserves correctness without too
2211*ef8d499eSDavid van Moolenbroek * many complications, because this case is unlikely to happen.
2212*ef8d499eSDavid van Moolenbroek */
2213*ef8d499eSDavid van Moolenbroek if (avail == -1) {
2214*ef8d499eSDavid van Moolenbroek found = 1;
2215*ef8d499eSDavid van Moolenbroek
2216*ef8d499eSDavid van Moolenbroek (void)ifaddr_dl_del(ifdev, found);
2217*ef8d499eSDavid van Moolenbroek } else
2218*ef8d499eSDavid van Moolenbroek found = avail;
2219*ef8d499eSDavid van Moolenbroek
2220*ef8d499eSDavid van Moolenbroek ifaddr_dl_set(ifdev, found, hwaddr, is_factory);
2221*ef8d499eSDavid van Moolenbroek }
2222*ef8d499eSDavid van Moolenbroek
2223*ef8d499eSDavid van Moolenbroek ifaddr_dl_activate(ifdev, found);
2224*ef8d499eSDavid van Moolenbroek }
2225