1ef8d499eSDavid van Moolenbroek /* LWIP service - lldata.c - link-layer (ARP, NDP) data related routines */
2ef8d499eSDavid van Moolenbroek /*
3ef8d499eSDavid van Moolenbroek * This module is largely isolated from the regular routing code. There are
4ef8d499eSDavid van Moolenbroek * two reasons for that. First, mixing link-layer routes with regular routes
5ef8d499eSDavid van Moolenbroek * would not work well due to the fact that lwIP keeps these data structures
6ef8d499eSDavid van Moolenbroek * entirely separate. Second, as of version 8, NetBSD keeps the IP-layer and
7ef8d499eSDavid van Moolenbroek * link-layer routing separate as well.
8ef8d499eSDavid van Moolenbroek *
9ef8d499eSDavid van Moolenbroek * Unfortunately, lwIP does not provide much in the way of implementing the
10ef8d499eSDavid van Moolenbroek * functionality that would be expected for this module. As such, the current
11ef8d499eSDavid van Moolenbroek * implementation is very restricted and simple.
12ef8d499eSDavid van Moolenbroek *
13ef8d499eSDavid van Moolenbroek * For ARP table entries, lwIP only allows for adding and deleting static
14ef8d499eSDavid van Moolenbroek * entries. Non-static entries cannot be deleted. Incomplete (pending)
15ef8d499eSDavid van Moolenbroek * entries cannot even be enumerated, nor can (e.g.) expiry information be
16ef8d499eSDavid van Moolenbroek * obtained. The lwIP ARP datastructures are completely hidden, so there is no
17ef8d499eSDavid van Moolenbroek * way to overcome these limitations without changing lwIP itself. As a
18ef8d499eSDavid van Moolenbroek * result, not all functionality of the arp(8) userland utility is supported.
19ef8d499eSDavid van Moolenbroek *
20ef8d499eSDavid van Moolenbroek * For NDP table entries, lwIP offers no API at all. However, since the data
21ef8d499eSDavid van Moolenbroek * structures are exposed directly, we can use those to implement full support
22ef8d499eSDavid van Moolenbroek * for exposing information in a read-only way. However, manipulating data
23ef8d499eSDavid van Moolenbroek * structures directly from here is too risky, nor does lwIP currently support
24ef8d499eSDavid van Moolenbroek * the concept of static NDP table entries. Therefore, adding, changing, and
25ef8d499eSDavid van Moolenbroek * deleting NDP entries is currently not supported, and will also first require
26ef8d499eSDavid van Moolenbroek * changes to lwIP itself.
27ef8d499eSDavid van Moolenbroek *
28ef8d499eSDavid van Moolenbroek * The ndp(8) userland utility is also able to show and manipulate various
29ef8d499eSDavid van Moolenbroek * other neighbor discovery related tables and settings. We support only a
30ef8d499eSDavid van Moolenbroek * small subset of them. The main reason for this is that the other tables,
31ef8d499eSDavid van Moolenbroek * in particular the prefix and default router lists, are not relevant: on
32ef8d499eSDavid van Moolenbroek * MINIX 3, these are always managed fully in userland (usually dhcpcd(8)), and
33ef8d499eSDavid van Moolenbroek * we even hardcode lwIP not to parse Router Advertisement messages at all, so
34ef8d499eSDavid van Moolenbroek * even though those tables are still part of lwIP, they are always empty.
35ef8d499eSDavid van Moolenbroek * Other ndp(8) functionality are unsupported for similar reasons.
36ef8d499eSDavid van Moolenbroek */
37ef8d499eSDavid van Moolenbroek
38ef8d499eSDavid van Moolenbroek #include "lwip.h"
39ef8d499eSDavid van Moolenbroek #include "lldata.h"
40ef8d499eSDavid van Moolenbroek #include "route.h"
41ef8d499eSDavid van Moolenbroek #include "rtsock.h"
42ef8d499eSDavid van Moolenbroek
43ef8d499eSDavid van Moolenbroek #include "lwip/etharp.h"
44ef8d499eSDavid van Moolenbroek #include "lwip/nd6.h"
45ef8d499eSDavid van Moolenbroek #include "lwip/priv/nd6_priv.h" /* for neighbor_cache */
46ef8d499eSDavid van Moolenbroek
47ef8d499eSDavid van Moolenbroek /*
48ef8d499eSDavid van Moolenbroek * Process a routing command specifically for an ARP table entry. Return OK if
49ef8d499eSDavid van Moolenbroek * the routing command has been processed successfully and a routing socket
50ef8d499eSDavid van Moolenbroek * reply message has already been generated. Return a negative error code on
51ef8d499eSDavid van Moolenbroek * failure, in which case the caller will generate a reply message instead.
52ef8d499eSDavid van Moolenbroek */
53ef8d499eSDavid van Moolenbroek static int
lldata_arp_process(unsigned int type,const ip_addr_t * dst_addr,const struct eth_addr * gw_addr,struct ifdev * ifdev,unsigned int flags,const struct rtsock_request * rtr)54ef8d499eSDavid van Moolenbroek lldata_arp_process(unsigned int type, const ip_addr_t * dst_addr,
55ef8d499eSDavid van Moolenbroek const struct eth_addr * gw_addr, struct ifdev * ifdev,
56ef8d499eSDavid van Moolenbroek unsigned int flags, const struct rtsock_request * rtr)
57ef8d499eSDavid van Moolenbroek {
58ef8d499eSDavid van Moolenbroek const ip4_addr_t *ip4addr;
59ef8d499eSDavid van Moolenbroek struct eth_addr ethaddr, *ethptr;
60ef8d499eSDavid van Moolenbroek struct netif *netif;
61ef8d499eSDavid van Moolenbroek lldata_arp_num_t num;
62ef8d499eSDavid van Moolenbroek err_t err;
63ef8d499eSDavid van Moolenbroek
64ef8d499eSDavid van Moolenbroek netif = (ifdev != NULL) ? ifdev_get_netif(ifdev) : NULL;
65ef8d499eSDavid van Moolenbroek
66ef8d499eSDavid van Moolenbroek num = etharp_find_addr(netif, ip_2_ip4(dst_addr), ðptr, &ip4addr);
67ef8d499eSDavid van Moolenbroek
68ef8d499eSDavid van Moolenbroek if (type != RTM_ADD && num < 0)
69ef8d499eSDavid van Moolenbroek return ESRCH;
70ef8d499eSDavid van Moolenbroek else if (type == RTM_ADD && num >= 0)
71ef8d499eSDavid van Moolenbroek return EEXIST;
72ef8d499eSDavid van Moolenbroek
73ef8d499eSDavid van Moolenbroek switch (type) {
74ef8d499eSDavid van Moolenbroek case RTM_CHANGE:
75ef8d499eSDavid van Moolenbroek /*
76ef8d499eSDavid van Moolenbroek * This request is not used by arp(8), so keep things simple.
77ef8d499eSDavid van Moolenbroek * For RTM_ADD we support only static entries; we support only
78ef8d499eSDavid van Moolenbroek * those too here, and thus we can use delete-and-readd. If
79ef8d499eSDavid van Moolenbroek * the ethernet address is not being changed, try readding the
80ef8d499eSDavid van Moolenbroek * entry with the previous ethernet address.
81ef8d499eSDavid van Moolenbroek */
82ef8d499eSDavid van Moolenbroek if (gw_addr == NULL)
83ef8d499eSDavid van Moolenbroek gw_addr = ethptr;
84ef8d499eSDavid van Moolenbroek
85ef8d499eSDavid van Moolenbroek if (etharp_remove_static_entry(ip_2_ip4(dst_addr)) != ERR_OK)
86ef8d499eSDavid van Moolenbroek return EPERM;
87ef8d499eSDavid van Moolenbroek
88ef8d499eSDavid van Moolenbroek /* FALLTHROUGH */
89ef8d499eSDavid van Moolenbroek case RTM_ADD:
90ef8d499eSDavid van Moolenbroek assert(gw_addr != NULL);
91ef8d499eSDavid van Moolenbroek
92ef8d499eSDavid van Moolenbroek memcpy(ðaddr, gw_addr, sizeof(ethaddr));
93ef8d499eSDavid van Moolenbroek
94ef8d499eSDavid van Moolenbroek /*
95ef8d499eSDavid van Moolenbroek * Adding static, permanent, unpublished, non-proxy entries is
96ef8d499eSDavid van Moolenbroek * all that lwIP supports right now. We also do not get to
97ef8d499eSDavid van Moolenbroek * specify the interface, and the way lwIP picks the interface
98ef8d499eSDavid van Moolenbroek * may in fact result in a different one.
99ef8d499eSDavid van Moolenbroek */
100ef8d499eSDavid van Moolenbroek if ((err = etharp_add_static_entry(ip_2_ip4(dst_addr),
101ef8d499eSDavid van Moolenbroek ðaddr)) != ERR_OK)
102ef8d499eSDavid van Moolenbroek return util_convert_err(err);
103ef8d499eSDavid van Moolenbroek
104ef8d499eSDavid van Moolenbroek if ((num = etharp_find_addr(NULL /*netif*/, ip_2_ip4(dst_addr),
105ef8d499eSDavid van Moolenbroek ðptr, &ip4addr)) < 0)
106ef8d499eSDavid van Moolenbroek panic("unable to find just-added static ARP entry");
107ef8d499eSDavid van Moolenbroek
108ef8d499eSDavid van Moolenbroek /* FALLTHROUGH */
109ef8d499eSDavid van Moolenbroek case RTM_LOCK:
110ef8d499eSDavid van Moolenbroek case RTM_GET:
111ef8d499eSDavid van Moolenbroek rtsock_msg_arp(num, type, rtr);
112ef8d499eSDavid van Moolenbroek
113ef8d499eSDavid van Moolenbroek return OK;
114ef8d499eSDavid van Moolenbroek
115ef8d499eSDavid van Moolenbroek case RTM_DELETE:
116ef8d499eSDavid van Moolenbroek memcpy(ðaddr, ethptr, sizeof(ethaddr));
117ef8d499eSDavid van Moolenbroek
118ef8d499eSDavid van Moolenbroek if (etharp_remove_static_entry(ip_2_ip4(dst_addr)) != ERR_OK)
119ef8d499eSDavid van Moolenbroek return EPERM;
120ef8d499eSDavid van Moolenbroek
121ef8d499eSDavid van Moolenbroek /*
122ef8d499eSDavid van Moolenbroek * FIXME: the following block is a hack, because we cannot
123ef8d499eSDavid van Moolenbroek * predict whether the above removal will succeed, while at the
124ef8d499eSDavid van Moolenbroek * same time we need the entry to be present in order to report
125ef8d499eSDavid van Moolenbroek * the deleted address to the routing socket. We temporarily
126ef8d499eSDavid van Moolenbroek * readd and then remove the entry just for the purpose of
127ef8d499eSDavid van Moolenbroek * generating the routing socket reply. There are other ways
128ef8d499eSDavid van Moolenbroek * to resolve this, but only a better lwIP etharp API would
129ef8d499eSDavid van Moolenbroek * allow us to resolve this problem cleanly.
130ef8d499eSDavid van Moolenbroek */
131ef8d499eSDavid van Moolenbroek (void)etharp_add_static_entry(ip_2_ip4(dst_addr), ðaddr);
132ef8d499eSDavid van Moolenbroek
133ef8d499eSDavid van Moolenbroek num = etharp_find_addr(NULL /*netif*/, ip_2_ip4(dst_addr),
134ef8d499eSDavid van Moolenbroek ðptr, &ip4addr);
135ef8d499eSDavid van Moolenbroek assert(num >= 0);
136ef8d499eSDavid van Moolenbroek
137ef8d499eSDavid van Moolenbroek rtsock_msg_arp(num, type, rtr);
138ef8d499eSDavid van Moolenbroek
139ef8d499eSDavid van Moolenbroek (void)etharp_remove_static_entry(ip_2_ip4(dst_addr));
140ef8d499eSDavid van Moolenbroek
141ef8d499eSDavid van Moolenbroek return OK;
142ef8d499eSDavid van Moolenbroek
143ef8d499eSDavid van Moolenbroek default:
144ef8d499eSDavid van Moolenbroek return EINVAL;
145ef8d499eSDavid van Moolenbroek }
146ef8d499eSDavid van Moolenbroek }
147ef8d499eSDavid van Moolenbroek
148ef8d499eSDavid van Moolenbroek /*
149ef8d499eSDavid van Moolenbroek * Enumerate ARP table entries. Return TRUE if there is at least one more ARP
150ef8d499eSDavid van Moolenbroek * table entry, of which the number is stored in 'num'. The caller should set
151ef8d499eSDavid van Moolenbroek * 'num' to 0 initially, and increase it by one between a successful call and
152ef8d499eSDavid van Moolenbroek * the next call. Return FALSE if there are no more ARP table entries.
153ef8d499eSDavid van Moolenbroek */
154ef8d499eSDavid van Moolenbroek int
lldata_arp_enum(lldata_arp_num_t * num)155ef8d499eSDavid van Moolenbroek lldata_arp_enum(lldata_arp_num_t * num)
156ef8d499eSDavid van Moolenbroek {
157ef8d499eSDavid van Moolenbroek ip4_addr_t *ip4addr;
158ef8d499eSDavid van Moolenbroek struct netif *netif;
159ef8d499eSDavid van Moolenbroek struct eth_addr *ethaddr;
160ef8d499eSDavid van Moolenbroek
161ef8d499eSDavid van Moolenbroek for (; *num < ARP_TABLE_SIZE; ++*num) {
162ef8d499eSDavid van Moolenbroek if (etharp_get_entry(*num, &ip4addr, &netif, ðaddr))
163ef8d499eSDavid van Moolenbroek return TRUE;
164ef8d499eSDavid van Moolenbroek }
165ef8d499eSDavid van Moolenbroek
166ef8d499eSDavid van Moolenbroek return FALSE;
167ef8d499eSDavid van Moolenbroek }
168ef8d499eSDavid van Moolenbroek
169ef8d499eSDavid van Moolenbroek /*
170ef8d499eSDavid van Moolenbroek * Obtain information about the ARP table entry identified by 'num'. The IPv4
171ef8d499eSDavid van Moolenbroek * address of the entry is stored in 'addr'. Its ethernet address is stored in
172ef8d499eSDavid van Moolenbroek * 'gateway'. The associated interface is stored in 'ifdevp', and the entry's
173ef8d499eSDavid van Moolenbroek * routing flags (RTF_) are stored in 'flagsp'.
174ef8d499eSDavid van Moolenbroek */
175ef8d499eSDavid van Moolenbroek void
lldata_arp_get(lldata_arp_num_t num,struct sockaddr_in * addr,struct sockaddr_dlx * gateway,struct ifdev ** ifdevp,unsigned int * flagsp)176ef8d499eSDavid van Moolenbroek lldata_arp_get(lldata_arp_num_t num, struct sockaddr_in * addr,
177ef8d499eSDavid van Moolenbroek struct sockaddr_dlx * gateway, struct ifdev ** ifdevp,
178ef8d499eSDavid van Moolenbroek unsigned int * flagsp)
179ef8d499eSDavid van Moolenbroek {
180ef8d499eSDavid van Moolenbroek ip_addr_t ipaddr;
181ef8d499eSDavid van Moolenbroek ip4_addr_t *ip4addr;
182ef8d499eSDavid van Moolenbroek struct netif *netif;
183ef8d499eSDavid van Moolenbroek struct ifdev *ifdev;
184ef8d499eSDavid van Moolenbroek struct eth_addr *ethaddr;
185ef8d499eSDavid van Moolenbroek socklen_t addr_len;
186ef8d499eSDavid van Moolenbroek
187ef8d499eSDavid van Moolenbroek if (!etharp_get_entry(num, &ip4addr, &netif, ðaddr))
188ef8d499eSDavid van Moolenbroek panic("request for invalid ARP entry");
189ef8d499eSDavid van Moolenbroek
190ef8d499eSDavid van Moolenbroek ip_addr_copy_from_ip4(ipaddr, *ip4addr);
191ef8d499eSDavid van Moolenbroek
192ef8d499eSDavid van Moolenbroek assert(netif != NULL);
193ef8d499eSDavid van Moolenbroek ifdev = netif_get_ifdev(netif);
194ef8d499eSDavid van Moolenbroek
195ef8d499eSDavid van Moolenbroek addr_len = sizeof(*addr);
196ef8d499eSDavid van Moolenbroek
197ef8d499eSDavid van Moolenbroek addr_put_inet((struct sockaddr *)addr, &addr_len, &ipaddr,
198ef8d499eSDavid van Moolenbroek TRUE /*kame*/, 0 /*port*/);
199ef8d499eSDavid van Moolenbroek
200ef8d499eSDavid van Moolenbroek addr_len = sizeof(*gateway);
201ef8d499eSDavid van Moolenbroek
202ef8d499eSDavid van Moolenbroek addr_put_link((struct sockaddr *)gateway, &addr_len,
203ef8d499eSDavid van Moolenbroek ifdev_get_index(ifdev), ifdev_get_iftype(ifdev), NULL /*name*/,
204ef8d499eSDavid van Moolenbroek ethaddr->addr, sizeof(ethaddr->addr));
205ef8d499eSDavid van Moolenbroek
206ef8d499eSDavid van Moolenbroek *ifdevp = ifdev;
207ef8d499eSDavid van Moolenbroek
208ef8d499eSDavid van Moolenbroek /*
209ef8d499eSDavid van Moolenbroek * TODO: this is not necessarily accurate, but lwIP does not provide us
210ef8d499eSDavid van Moolenbroek * with information as to whether this is a static entry or not..
211ef8d499eSDavid van Moolenbroek */
212ef8d499eSDavid van Moolenbroek *flagsp = RTF_HOST | RTF_LLINFO | RTF_LLDATA | RTF_STATIC | RTF_CLONED;
213ef8d499eSDavid van Moolenbroek }
214ef8d499eSDavid van Moolenbroek
215ef8d499eSDavid van Moolenbroek /*
216ef8d499eSDavid van Moolenbroek * Obtain information about the ND6 neighbor cache entry 'i', which must be a
217ef8d499eSDavid van Moolenbroek * number between 0 (inclusive) and LWIP_ND6_NUM_NEIGHBORS (exclusive). If an
218ef8d499eSDavid van Moolenbroek * entry with this number exists, return a pointer to its IPv6 address, and
219ef8d499eSDavid van Moolenbroek * additional information in each of the given pointers if not NULL. The
220ef8d499eSDavid van Moolenbroek * associated interface is stored in 'netif'. If the entry has an associated
221ef8d499eSDavid van Moolenbroek * link-layer address, a pointer to it is stored in 'lladdr'. The entry's
222ef8d499eSDavid van Moolenbroek * state (ND6_{INCOMPLETE,REACHABLE,STALE,DELAY,PROBE}) is stored in 'state'.
223ef8d499eSDavid van Moolenbroek * The 'isrouter' parameter is filled with a boolean value indicating whether
224ef8d499eSDavid van Moolenbroek * the entry is for a router. For ND6_INCOMPLETE and ND6_PROBE, the number of
225ef8d499eSDavid van Moolenbroek * probes sent so far is stored in 'probes_sent'; for other states, the value
226ef8d499eSDavid van Moolenbroek * is set to zero. For ND6_REACHABLE and ND6_DELAY, the time until expiration
227ef8d499eSDavid van Moolenbroek * in ND6_TMR_INTERVAL-millisecond units is stored in 'expire_time'; for other
228ef8d499eSDavid van Moolenbroek * states, the value is set to zero. If an entry with number 'i' does not
229ef8d499eSDavid van Moolenbroek * exist, NULL is returned.
230ef8d499eSDavid van Moolenbroek *
231ef8d499eSDavid van Moolenbroek * TODO: upstream this function to lwIP.
232ef8d499eSDavid van Moolenbroek */
233ef8d499eSDavid van Moolenbroek static const ip6_addr_t *
nd6_get_neighbor_cache_entry(int8_t i,struct netif ** netif,const uint8_t ** lladdr,uint8_t * state,uint8_t * isrouter,uint32_t * probes_sent,uint32_t * expire_time)234ef8d499eSDavid van Moolenbroek nd6_get_neighbor_cache_entry(int8_t i, struct netif ** netif,
235ef8d499eSDavid van Moolenbroek const uint8_t ** lladdr, uint8_t * state, uint8_t * isrouter,
236ef8d499eSDavid van Moolenbroek uint32_t * probes_sent, uint32_t * expire_time)
237ef8d499eSDavid van Moolenbroek {
238ef8d499eSDavid van Moolenbroek
239ef8d499eSDavid van Moolenbroek if (i < 0 || i >= LWIP_ND6_NUM_NEIGHBORS ||
240ef8d499eSDavid van Moolenbroek neighbor_cache[i].state == ND6_NO_ENTRY)
241ef8d499eSDavid van Moolenbroek return NULL;
242ef8d499eSDavid van Moolenbroek
243ef8d499eSDavid van Moolenbroek if (netif != NULL)
244ef8d499eSDavid van Moolenbroek *netif = neighbor_cache[i].netif;
245ef8d499eSDavid van Moolenbroek
246ef8d499eSDavid van Moolenbroek if (lladdr != NULL) {
247ef8d499eSDavid van Moolenbroek if (neighbor_cache[i].state != ND6_INCOMPLETE)
248ef8d499eSDavid van Moolenbroek *lladdr = neighbor_cache[i].lladdr;
249ef8d499eSDavid van Moolenbroek else
250ef8d499eSDavid van Moolenbroek *lladdr = NULL;
251ef8d499eSDavid van Moolenbroek }
252ef8d499eSDavid van Moolenbroek
253ef8d499eSDavid van Moolenbroek if (state != NULL)
254ef8d499eSDavid van Moolenbroek *state = neighbor_cache[i].state;
255ef8d499eSDavid van Moolenbroek
256ef8d499eSDavid van Moolenbroek if (isrouter != NULL)
257ef8d499eSDavid van Moolenbroek *isrouter = neighbor_cache[i].isrouter;
258ef8d499eSDavid van Moolenbroek
259ef8d499eSDavid van Moolenbroek if (probes_sent != NULL) {
260ef8d499eSDavid van Moolenbroek if (neighbor_cache[i].state == ND6_INCOMPLETE ||
261ef8d499eSDavid van Moolenbroek neighbor_cache[i].state == ND6_PROBE)
262ef8d499eSDavid van Moolenbroek *probes_sent = neighbor_cache[i].counter.probes_sent;
263ef8d499eSDavid van Moolenbroek else
264ef8d499eSDavid van Moolenbroek *probes_sent = 0;
265ef8d499eSDavid van Moolenbroek }
266ef8d499eSDavid van Moolenbroek
267ef8d499eSDavid van Moolenbroek if (expire_time != NULL) {
268ef8d499eSDavid van Moolenbroek switch (neighbor_cache[i].state) {
269ef8d499eSDavid van Moolenbroek case ND6_REACHABLE:
270ef8d499eSDavid van Moolenbroek *expire_time =
271ef8d499eSDavid van Moolenbroek neighbor_cache[i].counter.reachable_time /
272ef8d499eSDavid van Moolenbroek ND6_TMR_INTERVAL;
273ef8d499eSDavid van Moolenbroek break;
274ef8d499eSDavid van Moolenbroek case ND6_DELAY:
275ef8d499eSDavid van Moolenbroek *expire_time = neighbor_cache[i].counter.delay_time;
276ef8d499eSDavid van Moolenbroek break;
277ef8d499eSDavid van Moolenbroek case ND6_INCOMPLETE:
278ef8d499eSDavid van Moolenbroek case ND6_PROBE:
279ef8d499eSDavid van Moolenbroek /* Probes are sent once per timer tick. */
280ef8d499eSDavid van Moolenbroek *expire_time = (LWIP_ND6_MAX_MULTICAST_SOLICIT + 1 -
281ef8d499eSDavid van Moolenbroek neighbor_cache[i].counter.probes_sent) *
282ef8d499eSDavid van Moolenbroek (ND6_TMR_INTERVAL / 1000);
283ef8d499eSDavid van Moolenbroek break;
284ef8d499eSDavid van Moolenbroek default:
285ef8d499eSDavid van Moolenbroek /* Stale entries do not expire; they get replaced. */
286ef8d499eSDavid van Moolenbroek *expire_time = 0;
287ef8d499eSDavid van Moolenbroek break;
288ef8d499eSDavid van Moolenbroek }
289ef8d499eSDavid van Moolenbroek }
290ef8d499eSDavid van Moolenbroek
291ef8d499eSDavid van Moolenbroek return &neighbor_cache[i].next_hop_address;
292ef8d499eSDavid van Moolenbroek }
293ef8d499eSDavid van Moolenbroek
294ef8d499eSDavid van Moolenbroek /*
295ef8d499eSDavid van Moolenbroek * Find a neighbor cache entry by IPv6 address. Return its index number if
296ef8d499eSDavid van Moolenbroek * found, or -1 if not. This is a reimplementation of the exact same function
297ef8d499eSDavid van Moolenbroek * internal to lwIP.
298ef8d499eSDavid van Moolenbroek *
299ef8d499eSDavid van Moolenbroek * TODO: make this function public in lwIP.
300ef8d499eSDavid van Moolenbroek */
301ef8d499eSDavid van Moolenbroek static int8_t
nd6_find_neighbor_cache_entry(const ip6_addr_t * addr)302ef8d499eSDavid van Moolenbroek nd6_find_neighbor_cache_entry(const ip6_addr_t * addr)
303ef8d499eSDavid van Moolenbroek {
304ef8d499eSDavid van Moolenbroek int8_t i;
305ef8d499eSDavid van Moolenbroek
306ef8d499eSDavid van Moolenbroek for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) {
307ef8d499eSDavid van Moolenbroek if (ip6_addr_cmp(addr, &neighbor_cache[i].next_hop_address))
308ef8d499eSDavid van Moolenbroek return i;
309ef8d499eSDavid van Moolenbroek }
310ef8d499eSDavid van Moolenbroek
311ef8d499eSDavid van Moolenbroek return -1;
312ef8d499eSDavid van Moolenbroek }
313ef8d499eSDavid van Moolenbroek
314ef8d499eSDavid van Moolenbroek /*
315ef8d499eSDavid van Moolenbroek * Find an NDP table entry based on the given interface and IPv6 address. On
316ef8d499eSDavid van Moolenbroek * success, return OK, with the entry's index number stored in 'nump'. On
317ef8d499eSDavid van Moolenbroek * failure, return an appropriate error code.
318ef8d499eSDavid van Moolenbroek */
319ef8d499eSDavid van Moolenbroek int
lldata_ndp_find(struct ifdev * ifdev,const struct sockaddr_in6 * addr,lldata_ndp_num_t * nump)320ef8d499eSDavid van Moolenbroek lldata_ndp_find(struct ifdev * ifdev, const struct sockaddr_in6 * addr,
321ef8d499eSDavid van Moolenbroek lldata_ndp_num_t * nump)
322ef8d499eSDavid van Moolenbroek {
323ef8d499eSDavid van Moolenbroek ip_addr_t ipaddr;
324ef8d499eSDavid van Moolenbroek int8_t i;
325ef8d499eSDavid van Moolenbroek int r;
326ef8d499eSDavid van Moolenbroek
327ef8d499eSDavid van Moolenbroek if ((r = addr_get_inet((const struct sockaddr *)addr, sizeof(*addr),
328ef8d499eSDavid van Moolenbroek IPADDR_TYPE_V6, &ipaddr, TRUE /*kame*/, NULL /*port*/)) != OK)
329ef8d499eSDavid van Moolenbroek return r;
330ef8d499eSDavid van Moolenbroek
331ef8d499eSDavid van Moolenbroek /*
332ef8d499eSDavid van Moolenbroek * For given link-local addresses, no zone may be provided in the
333ef8d499eSDavid van Moolenbroek * address at all. In such cases, add the zone ourselves, using the
334ef8d499eSDavid van Moolenbroek * given interface.
335ef8d499eSDavid van Moolenbroek */
336ef8d499eSDavid van Moolenbroek if (ip6_addr_lacks_zone(ip_2_ip6(&ipaddr), IP6_UNKNOWN))
337ef8d499eSDavid van Moolenbroek ip6_addr_assign_zone(ip_2_ip6(&ipaddr), IP6_UNKNOWN,
338ef8d499eSDavid van Moolenbroek ifdev_get_netif(ifdev));
339ef8d499eSDavid van Moolenbroek
340ef8d499eSDavid van Moolenbroek i = nd6_find_neighbor_cache_entry(ip_2_ip6(&ipaddr));
341ef8d499eSDavid van Moolenbroek if (i < 0)
342ef8d499eSDavid van Moolenbroek return ESRCH;
343ef8d499eSDavid van Moolenbroek
344ef8d499eSDavid van Moolenbroek /*
345ef8d499eSDavid van Moolenbroek * We should compare the neighbor cache entry's associated netif to
346ef8d499eSDavid van Moolenbroek * the given ifdev, but since the lwIP neighbor cache is currently not
347ef8d499eSDavid van Moolenbroek * keyed by netif anyway (i.e. the internal lookups are purely by IPv6
348ef8d499eSDavid van Moolenbroek * address as well), doing so makes little sense in practice.
349ef8d499eSDavid van Moolenbroek */
350ef8d499eSDavid van Moolenbroek
351ef8d499eSDavid van Moolenbroek *nump = (lldata_ndp_num_t)i;
352ef8d499eSDavid van Moolenbroek return OK;
353ef8d499eSDavid van Moolenbroek }
354ef8d499eSDavid van Moolenbroek
355ef8d499eSDavid van Moolenbroek /*
356ef8d499eSDavid van Moolenbroek * Process a routing command specifically for an NDP table entry. Return OK if
357ef8d499eSDavid van Moolenbroek * the routing command has been processed successfully and a routing socket
358ef8d499eSDavid van Moolenbroek * reply message has already been generated. Return a negative error code on
359ef8d499eSDavid van Moolenbroek * failure, in which case the caller will generate a reply message instead.
360ef8d499eSDavid van Moolenbroek */
361ef8d499eSDavid van Moolenbroek static int
lldata_ndp_process(unsigned int type,const ip_addr_t * dst_addr,const struct eth_addr * gw_addr,struct ifdev * ifdev,unsigned int flags,const struct rtsock_request * rtr)362ef8d499eSDavid van Moolenbroek lldata_ndp_process(unsigned int type, const ip_addr_t * dst_addr,
363ef8d499eSDavid van Moolenbroek const struct eth_addr * gw_addr,
364ef8d499eSDavid van Moolenbroek struct ifdev * ifdev, unsigned int flags,
365ef8d499eSDavid van Moolenbroek const struct rtsock_request * rtr)
366ef8d499eSDavid van Moolenbroek {
367ef8d499eSDavid van Moolenbroek lldata_ndp_num_t num;
368ef8d499eSDavid van Moolenbroek
369ef8d499eSDavid van Moolenbroek num = (lldata_ndp_num_t)
370ef8d499eSDavid van Moolenbroek nd6_find_neighbor_cache_entry(ip_2_ip6(dst_addr));
371ef8d499eSDavid van Moolenbroek
372ef8d499eSDavid van Moolenbroek if (type != RTM_ADD && num < 0)
373ef8d499eSDavid van Moolenbroek return ESRCH;
374ef8d499eSDavid van Moolenbroek else if (type == RTM_ADD && num >= 0)
375ef8d499eSDavid van Moolenbroek return EEXIST;
376ef8d499eSDavid van Moolenbroek
377ef8d499eSDavid van Moolenbroek switch (type) {
378ef8d499eSDavid van Moolenbroek case RTM_LOCK:
379ef8d499eSDavid van Moolenbroek case RTM_GET:
380ef8d499eSDavid van Moolenbroek rtsock_msg_arp(num, type, rtr);
381ef8d499eSDavid van Moolenbroek
382ef8d499eSDavid van Moolenbroek return OK;
383ef8d499eSDavid van Moolenbroek
384ef8d499eSDavid van Moolenbroek case RTM_ADD:
385ef8d499eSDavid van Moolenbroek case RTM_CHANGE:
386ef8d499eSDavid van Moolenbroek case RTM_DELETE:
387ef8d499eSDavid van Moolenbroek /* TODO: add lwIP support to implement these commands. */
388ef8d499eSDavid van Moolenbroek return ENOSYS;
389ef8d499eSDavid van Moolenbroek
390ef8d499eSDavid van Moolenbroek default:
391ef8d499eSDavid van Moolenbroek return EINVAL;
392ef8d499eSDavid van Moolenbroek }
393ef8d499eSDavid van Moolenbroek }
394ef8d499eSDavid van Moolenbroek
395ef8d499eSDavid van Moolenbroek /*
396ef8d499eSDavid van Moolenbroek * Enumerate NDP table entries. Return TRUE if there is at least one more NDP
397ef8d499eSDavid van Moolenbroek * table entry, of which the number is stored in 'num'. The caller should set
398ef8d499eSDavid van Moolenbroek * 'num' to 0 initially, and increase it by one between a successful call and
399ef8d499eSDavid van Moolenbroek * the next call. Return FALSE if there are no more NDP table entries.
400ef8d499eSDavid van Moolenbroek */
401ef8d499eSDavid van Moolenbroek int
lldata_ndp_enum(lldata_ndp_num_t * num)402ef8d499eSDavid van Moolenbroek lldata_ndp_enum(lldata_ndp_num_t * num)
403ef8d499eSDavid van Moolenbroek {
404ef8d499eSDavid van Moolenbroek
405ef8d499eSDavid van Moolenbroek for (; *num < LWIP_ND6_NUM_NEIGHBORS; ++*num) {
406ef8d499eSDavid van Moolenbroek if (nd6_get_neighbor_cache_entry(*num, NULL /*netif*/,
407ef8d499eSDavid van Moolenbroek NULL /*lladdr*/, NULL /*state*/, NULL /*isrouter*/,
408ef8d499eSDavid van Moolenbroek NULL /*probes_sent*/, NULL /*expire_time*/) != NULL)
409ef8d499eSDavid van Moolenbroek return TRUE;
410ef8d499eSDavid van Moolenbroek }
411ef8d499eSDavid van Moolenbroek
412ef8d499eSDavid van Moolenbroek return FALSE;
413ef8d499eSDavid van Moolenbroek }
414ef8d499eSDavid van Moolenbroek
415ef8d499eSDavid van Moolenbroek /*
416ef8d499eSDavid van Moolenbroek * Obtain information about the NDP table entry identified by 'num'. The IPv6
417ef8d499eSDavid van Moolenbroek * address of the entry is stored in 'addr'. Its ethernet address is stored in
418ef8d499eSDavid van Moolenbroek * 'gateway'. The associated interface is stored in 'ifdevp', and the entry's
419ef8d499eSDavid van Moolenbroek * routing flags (RTF_) are stored in 'flagsp'.
420ef8d499eSDavid van Moolenbroek */
421ef8d499eSDavid van Moolenbroek void
lldata_ndp_get(lldata_ndp_num_t num,struct sockaddr_in6 * addr,struct sockaddr_dlx * gateway,struct ifdev ** ifdevp,unsigned int * flagsp)422ef8d499eSDavid van Moolenbroek lldata_ndp_get(lldata_ndp_num_t num, struct sockaddr_in6 * addr,
423ef8d499eSDavid van Moolenbroek struct sockaddr_dlx * gateway, struct ifdev ** ifdevp,
424ef8d499eSDavid van Moolenbroek unsigned int * flagsp)
425ef8d499eSDavid van Moolenbroek {
426ef8d499eSDavid van Moolenbroek const ip6_addr_t *ip6addr;
427ef8d499eSDavid van Moolenbroek ip_addr_t ipaddr;
428*03ac74edSLionel Sambuc struct netif *netif = NULL /*gcc*/;
429ef8d499eSDavid van Moolenbroek struct ifdev *ifdev;
430*03ac74edSLionel Sambuc const uint8_t *lladdr = NULL /*gcc*/;
431ef8d499eSDavid van Moolenbroek socklen_t addr_len;
432ef8d499eSDavid van Moolenbroek
433ef8d499eSDavid van Moolenbroek ip6addr = nd6_get_neighbor_cache_entry(num, &netif, &lladdr,
434ef8d499eSDavid van Moolenbroek NULL /*state*/, NULL /*isrouter*/, NULL /*probes_sent*/,
435ef8d499eSDavid van Moolenbroek NULL /*expire_time*/);
436ef8d499eSDavid van Moolenbroek assert(ip6addr != NULL);
437ef8d499eSDavid van Moolenbroek
438ef8d499eSDavid van Moolenbroek ip_addr_copy_from_ip6(ipaddr, *ip6addr);
439ef8d499eSDavid van Moolenbroek
440ef8d499eSDavid van Moolenbroek ifdev = netif_get_ifdev(netif);
441ef8d499eSDavid van Moolenbroek assert(ifdev != NULL);
442ef8d499eSDavid van Moolenbroek
443ef8d499eSDavid van Moolenbroek addr_len = sizeof(*addr);
444ef8d499eSDavid van Moolenbroek
445ef8d499eSDavid van Moolenbroek addr_put_inet((struct sockaddr *)addr, &addr_len, &ipaddr,
446ef8d499eSDavid van Moolenbroek TRUE /*kame*/, 0 /*port*/);
447ef8d499eSDavid van Moolenbroek
448ef8d499eSDavid van Moolenbroek addr_len = sizeof(*gateway);
449ef8d499eSDavid van Moolenbroek
450ef8d499eSDavid van Moolenbroek addr_put_link((struct sockaddr *)gateway, &addr_len,
451ef8d499eSDavid van Moolenbroek ifdev_get_index(ifdev), ifdev_get_iftype(ifdev), NULL /*name*/,
452ef8d499eSDavid van Moolenbroek lladdr, ifdev_get_hwlen(ifdev));
453ef8d499eSDavid van Moolenbroek
454ef8d499eSDavid van Moolenbroek *ifdevp = ifdev;
455ef8d499eSDavid van Moolenbroek *flagsp = RTF_HOST | RTF_LLINFO | RTF_LLDATA | RTF_CLONED;
456ef8d499eSDavid van Moolenbroek }
457ef8d499eSDavid van Moolenbroek
458ef8d499eSDavid van Moolenbroek /*
459ef8d499eSDavid van Moolenbroek * Obtain information about the NDP table entry with the number 'num', which
460ef8d499eSDavid van Moolenbroek * must be obtained through a previous call to lldata_ndp_find(). On return,
461ef8d499eSDavid van Moolenbroek * 'asked' is filled with the number of probes sent so far (0 if inapplicable),
462ef8d499eSDavid van Moolenbroek * 'isrouter' is set to 1 or 0 depending on whether the entry is for a router,
463ef8d499eSDavid van Moolenbroek * 'state' is set to the entry's state (ND6_LLINFO_), and 'expire' is set to
464ef8d499eSDavid van Moolenbroek * either the UNIX timestamp of expiry for the entry; 0 for permanent entries.
465ef8d499eSDavid van Moolenbroek * None of the given pointers must be NULL. This function always succeeds.
466ef8d499eSDavid van Moolenbroek */
467ef8d499eSDavid van Moolenbroek void
lldata_ndp_get_info(lldata_ndp_num_t num,long * asked,int * isrouter,int * state,int * expire)468ef8d499eSDavid van Moolenbroek lldata_ndp_get_info(lldata_ndp_num_t num, long * asked, int * isrouter,
469ef8d499eSDavid van Moolenbroek int * state, int * expire)
470ef8d499eSDavid van Moolenbroek {
471ef8d499eSDavid van Moolenbroek uint32_t nd6_probes_sent = 0 /*gcc*/, nd6_expire_time = 0 /*gcc*/;
472ef8d499eSDavid van Moolenbroek uint8_t nd6_state = 0 /*gcc*/, nd6_isrouter = 0 /*gcc*/;
473ef8d499eSDavid van Moolenbroek
474ef8d499eSDavid van Moolenbroek (void)nd6_get_neighbor_cache_entry(num, NULL /*netif*/,
475ef8d499eSDavid van Moolenbroek NULL /*lladdr*/, &nd6_state, &nd6_isrouter, &nd6_probes_sent,
476ef8d499eSDavid van Moolenbroek &nd6_expire_time);
477ef8d499eSDavid van Moolenbroek
478ef8d499eSDavid van Moolenbroek *asked = (long)nd6_probes_sent;
479ef8d499eSDavid van Moolenbroek
480ef8d499eSDavid van Moolenbroek *isrouter = !!nd6_isrouter;
481ef8d499eSDavid van Moolenbroek
482ef8d499eSDavid van Moolenbroek switch (nd6_state) {
483ef8d499eSDavid van Moolenbroek case ND6_INCOMPLETE: *state = ND6_LLINFO_INCOMPLETE; break;
484ef8d499eSDavid van Moolenbroek case ND6_REACHABLE: *state = ND6_LLINFO_REACHABLE; break;
485ef8d499eSDavid van Moolenbroek case ND6_STALE: *state = ND6_LLINFO_STALE; break;
486ef8d499eSDavid van Moolenbroek case ND6_DELAY: *state = ND6_LLINFO_DELAY; break;
487ef8d499eSDavid van Moolenbroek case ND6_PROBE: *state = ND6_LLINFO_PROBE; break;
488ef8d499eSDavid van Moolenbroek default: panic("unknown ND6 state %u", nd6_state);
489ef8d499eSDavid van Moolenbroek }
490ef8d499eSDavid van Moolenbroek
491ef8d499eSDavid van Moolenbroek if (nd6_expire_time != 0)
492ef8d499eSDavid van Moolenbroek *expire = clock_time(NULL) +
493ef8d499eSDavid van Moolenbroek (int)nd6_expire_time * (ND6_TMR_INTERVAL / 1000);
494ef8d499eSDavid van Moolenbroek else
495ef8d499eSDavid van Moolenbroek *expire = 0;
496ef8d499eSDavid van Moolenbroek }
497ef8d499eSDavid van Moolenbroek
498ef8d499eSDavid van Moolenbroek /*
499ef8d499eSDavid van Moolenbroek * Process a routing command specifically for a link-layer route, as one of the
500ef8d499eSDavid van Moolenbroek * specific continuations of processing started by route_process(). The RTM_
501ef8d499eSDavid van Moolenbroek * routing command is given as 'type'. The route destination is given as
502ef8d499eSDavid van Moolenbroek * 'dst_addr'; its address type determines whether the operation is for ARP or
503ef8d499eSDavid van Moolenbroek * NDP. The sockaddr structure for 'gateway' is passed on as is and may have
504ef8d499eSDavid van Moolenbroek * to be parsed here if not NULL. 'ifdev' is the interface to be associated
505ef8d499eSDavid van Moolenbroek * with the route; it is non-NULL only if an interface name (IFP) or address
506ef8d499eSDavid van Moolenbroek * (IFA) was given. The RTF_ flags field has been checked against the globally
507ef8d499eSDavid van Moolenbroek * supported flags, but may have to be checked for flags that do not apply to
508ef8d499eSDavid van Moolenbroek * ARP/NDP routes. Return OK or a negative error code, following the same
509ef8d499eSDavid van Moolenbroek * semantics as route_process().
510ef8d499eSDavid van Moolenbroek */
511ef8d499eSDavid van Moolenbroek int
lldata_process(unsigned int type,const ip_addr_t * dst_addr,const struct sockaddr * gateway,struct ifdev * ifdev,unsigned int flags,const struct rtsock_request * rtr)512ef8d499eSDavid van Moolenbroek lldata_process(unsigned int type, const ip_addr_t * dst_addr,
513ef8d499eSDavid van Moolenbroek const struct sockaddr * gateway, struct ifdev * ifdev,
514ef8d499eSDavid van Moolenbroek unsigned int flags, const struct rtsock_request * rtr)
515ef8d499eSDavid van Moolenbroek {
516ef8d499eSDavid van Moolenbroek const struct route_entry *route;
517ef8d499eSDavid van Moolenbroek struct eth_addr ethaddr, *gw_addr;
518ef8d499eSDavid van Moolenbroek int r;
519ef8d499eSDavid van Moolenbroek
520ef8d499eSDavid van Moolenbroek assert(flags & RTF_LLDATA);
521ef8d499eSDavid van Moolenbroek
522ef8d499eSDavid van Moolenbroek /*
523ef8d499eSDavid van Moolenbroek * It seems that RTF_UP does not apply to link-layer routing entries.
524ef8d499eSDavid van Moolenbroek * We basically accept any flags that we can return, but we do not
525ef8d499eSDavid van Moolenbroek * actually check most of them anywhere.
526ef8d499eSDavid van Moolenbroek */
527ef8d499eSDavid van Moolenbroek if ((flags & ~(RTF_HOST | RTF_LLINFO | RTF_LLDATA | RTF_STATIC |
528ef8d499eSDavid van Moolenbroek RTF_CLONED | RTF_ANNOUNCE)) != 0)
529ef8d499eSDavid van Moolenbroek return EINVAL;
530ef8d499eSDavid van Moolenbroek
531ef8d499eSDavid van Moolenbroek gw_addr = NULL;
532ef8d499eSDavid van Moolenbroek
533ef8d499eSDavid van Moolenbroek if (type == RTM_ADD || type == RTM_CHANGE) {
534ef8d499eSDavid van Moolenbroek /*
535ef8d499eSDavid van Moolenbroek * Link-layer entries are always host entries. Not all
536ef8d499eSDavid van Moolenbroek * requests pass in this flag though, so check only when the
537ef8d499eSDavid van Moolenbroek * flags are supposed to be set.
538ef8d499eSDavid van Moolenbroek */
539ef8d499eSDavid van Moolenbroek if ((type == RTM_ADD || type == RTM_CHANGE) &&
540ef8d499eSDavid van Moolenbroek !(flags & RTF_HOST))
541ef8d499eSDavid van Moolenbroek return EINVAL;
542ef8d499eSDavid van Moolenbroek
543ef8d499eSDavid van Moolenbroek /* lwIP does not support publishing custom entries. */
544ef8d499eSDavid van Moolenbroek if (flags & RTF_ANNOUNCE)
545ef8d499eSDavid van Moolenbroek return ENOSYS;
546ef8d499eSDavid van Moolenbroek
547ef8d499eSDavid van Moolenbroek /* RTF_GATEWAY is always cleared for link-layer entries. */
548ef8d499eSDavid van Moolenbroek if (gateway != NULL) {
549ef8d499eSDavid van Moolenbroek if ((r = addr_get_link(gateway, gateway->sa_len,
550ef8d499eSDavid van Moolenbroek NULL /*name*/, 0 /*name_max*/, ethaddr.addr,
551ef8d499eSDavid van Moolenbroek sizeof(ethaddr.addr))) != OK)
552ef8d499eSDavid van Moolenbroek return r;
553ef8d499eSDavid van Moolenbroek
554ef8d499eSDavid van Moolenbroek gw_addr = ðaddr;
555ef8d499eSDavid van Moolenbroek }
556ef8d499eSDavid van Moolenbroek
557ef8d499eSDavid van Moolenbroek if (type == RTM_ADD) {
558ef8d499eSDavid van Moolenbroek if (gateway == NULL)
559ef8d499eSDavid van Moolenbroek return EINVAL;
560ef8d499eSDavid van Moolenbroek
561ef8d499eSDavid van Moolenbroek /*
562ef8d499eSDavid van Moolenbroek * If no interface has been specified, see if the
563ef8d499eSDavid van Moolenbroek * destination address is on a locally connected
564ef8d499eSDavid van Moolenbroek * network. If so, use that network's interface.
565ef8d499eSDavid van Moolenbroek * Otherwise reject the request altogether: we must
566ef8d499eSDavid van Moolenbroek * have an interface to which to associate the entry.
567ef8d499eSDavid van Moolenbroek */
568ef8d499eSDavid van Moolenbroek if (ifdev == NULL) {
569ef8d499eSDavid van Moolenbroek if ((route = route_lookup(dst_addr)) != NULL &&
570ef8d499eSDavid van Moolenbroek !(route_get_flags(route) & RTF_GATEWAY))
571ef8d499eSDavid van Moolenbroek ifdev = route_get_ifdev(route);
572ef8d499eSDavid van Moolenbroek else
573ef8d499eSDavid van Moolenbroek return ENETUNREACH;
574ef8d499eSDavid van Moolenbroek }
575ef8d499eSDavid van Moolenbroek }
576ef8d499eSDavid van Moolenbroek }
577ef8d499eSDavid van Moolenbroek
578ef8d499eSDavid van Moolenbroek if (IP_IS_V4(dst_addr))
579ef8d499eSDavid van Moolenbroek return lldata_arp_process(type, dst_addr, gw_addr, ifdev,
580ef8d499eSDavid van Moolenbroek flags, rtr);
581ef8d499eSDavid van Moolenbroek else
582ef8d499eSDavid van Moolenbroek return lldata_ndp_process(type, dst_addr, gw_addr, ifdev,
583ef8d499eSDavid van Moolenbroek flags, rtr);
584ef8d499eSDavid van Moolenbroek }
585