BadVPN – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | /** |
2 | * @file |
||
3 | * |
||
4 | * Neighbor discovery and stateless address autoconfiguration for IPv6. |
||
5 | * Aims to be compliant with RFC 4861 (Neighbor discovery) and RFC 4862 |
||
6 | * (Address autoconfiguration). |
||
7 | */ |
||
8 | |||
9 | /* |
||
10 | * Copyright (c) 2010 Inico Technologies Ltd. |
||
11 | * All rights reserved. |
||
12 | * |
||
13 | * Redistribution and use in source and binary forms, with or without modification, |
||
14 | * are permitted provided that the following conditions are met: |
||
15 | * |
||
16 | * 1. Redistributions of source code must retain the above copyright notice, |
||
17 | * this list of conditions and the following disclaimer. |
||
18 | * 2. Redistributions in binary form must reproduce the above copyright notice, |
||
19 | * this list of conditions and the following disclaimer in the documentation |
||
20 | * and/or other materials provided with the distribution. |
||
21 | * 3. The name of the author may not be used to endorse or promote products |
||
22 | * derived from this software without specific prior written permission. |
||
23 | * |
||
24 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED |
||
25 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
||
26 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT |
||
27 | * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||
28 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT |
||
29 | * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
||
30 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
||
31 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING |
||
32 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY |
||
33 | * OF SUCH DAMAGE. |
||
34 | * |
||
35 | * This file is part of the lwIP TCP/IP stack. |
||
36 | * |
||
37 | * Author: Ivan Delamer <delamer@inicotech.com> |
||
38 | * |
||
39 | * |
||
40 | * Please coordinate changes and requests with Ivan Delamer |
||
41 | * <delamer@inicotech.com> |
||
42 | */ |
||
43 | |||
44 | #include "lwip/opt.h" |
||
45 | |||
46 | #if LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */ |
||
47 | |||
48 | #include "lwip/nd6.h" |
||
49 | #include "lwip/priv/nd6_priv.h" |
||
50 | #include "lwip/prot/nd6.h" |
||
51 | #include "lwip/prot/icmp6.h" |
||
52 | #include "lwip/pbuf.h" |
||
53 | #include "lwip/mem.h" |
||
54 | #include "lwip/memp.h" |
||
55 | #include "lwip/ip6.h" |
||
56 | #include "lwip/ip6_addr.h" |
||
57 | #include "lwip/inet_chksum.h" |
||
58 | #include "lwip/netif.h" |
||
59 | #include "lwip/icmp6.h" |
||
60 | #include "lwip/mld6.h" |
||
61 | #include "lwip/ip.h" |
||
62 | #include "lwip/stats.h" |
||
63 | #include "lwip/dns.h" |
||
64 | |||
65 | #include <string.h> |
||
66 | |||
67 | #ifdef LWIP_HOOK_FILENAME |
||
68 | #include LWIP_HOOK_FILENAME |
||
69 | #endif |
||
70 | |||
71 | #if LWIP_IPV6_DUP_DETECT_ATTEMPTS > IP6_ADDR_TENTATIVE_COUNT_MASK |
||
72 | #error LWIP_IPV6_DUP_DETECT_ATTEMPTS > IP6_ADDR_TENTATIVE_COUNT_MASK |
||
73 | #endif |
||
74 | |||
75 | /* Router tables. */ |
||
76 | struct nd6_neighbor_cache_entry neighbor_cache[LWIP_ND6_NUM_NEIGHBORS]; |
||
77 | struct nd6_destination_cache_entry destination_cache[LWIP_ND6_NUM_DESTINATIONS]; |
||
78 | struct nd6_prefix_list_entry prefix_list[LWIP_ND6_NUM_PREFIXES]; |
||
79 | struct nd6_router_list_entry default_router_list[LWIP_ND6_NUM_ROUTERS]; |
||
80 | |||
81 | /* Default values, can be updated by a RA message. */ |
||
82 | u32_t reachable_time = LWIP_ND6_REACHABLE_TIME; |
||
83 | u32_t retrans_timer = LWIP_ND6_RETRANS_TIMER; /* @todo implement this value in timer */ |
||
84 | |||
85 | /* Index for cache entries. */ |
||
86 | static u8_t nd6_cached_neighbor_index; |
||
87 | static u8_t nd6_cached_destination_index; |
||
88 | |||
89 | /* Multicast address holder. */ |
||
90 | static ip6_addr_t multicast_address; |
||
91 | |||
92 | /* Static buffer to parse RA packet options */ |
||
93 | union ra_options { |
||
94 | struct lladdr_option lladdr; |
||
95 | struct mtu_option mtu; |
||
96 | struct prefix_option prefix; |
||
97 | #if LWIP_ND6_RDNSS_MAX_DNS_SERVERS |
||
98 | struct rdnss_option rdnss; |
||
99 | #endif |
||
100 | }; |
||
101 | static union ra_options nd6_ra_buffer; |
||
102 | |||
103 | /* Forward declarations. */ |
||
104 | static s8_t nd6_find_neighbor_cache_entry(const ip6_addr_t *ip6addr); |
||
105 | static s8_t nd6_new_neighbor_cache_entry(void); |
||
106 | static void nd6_free_neighbor_cache_entry(s8_t i); |
||
107 | static s8_t nd6_find_destination_cache_entry(const ip6_addr_t *ip6addr); |
||
108 | static s8_t nd6_new_destination_cache_entry(void); |
||
109 | static s8_t nd6_is_prefix_in_netif(const ip6_addr_t *ip6addr, struct netif *netif); |
||
110 | static s8_t nd6_select_router(const ip6_addr_t *ip6addr, struct netif *netif); |
||
111 | static s8_t nd6_get_router(const ip6_addr_t *router_addr, struct netif *netif); |
||
112 | static s8_t nd6_new_router(const ip6_addr_t *router_addr, struct netif *netif); |
||
113 | static s8_t nd6_get_onlink_prefix(const ip6_addr_t *prefix, struct netif *netif); |
||
114 | static s8_t nd6_new_onlink_prefix(const ip6_addr_t *prefix, struct netif *netif); |
||
115 | static s8_t nd6_get_next_hop_entry(const ip6_addr_t *ip6addr, struct netif *netif); |
||
116 | static err_t nd6_queue_packet(s8_t neighbor_index, struct pbuf *q); |
||
117 | |||
118 | #define ND6_SEND_FLAG_MULTICAST_DEST 0x01 |
||
119 | #define ND6_SEND_FLAG_ALLNODES_DEST 0x02 |
||
120 | #define ND6_SEND_FLAG_ANY_SRC 0x04 |
||
121 | static void nd6_send_ns(struct netif *netif, const ip6_addr_t *target_addr, u8_t flags); |
||
122 | static void nd6_send_na(struct netif *netif, const ip6_addr_t *target_addr, u8_t flags); |
||
123 | static void nd6_send_neighbor_cache_probe(struct nd6_neighbor_cache_entry *entry, u8_t flags); |
||
124 | #if LWIP_IPV6_SEND_ROUTER_SOLICIT |
||
125 | static err_t nd6_send_rs(struct netif *netif); |
||
126 | #endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */ |
||
127 | |||
128 | #if LWIP_ND6_QUEUEING |
||
129 | static void nd6_free_q(struct nd6_q_entry *q); |
||
130 | #else /* LWIP_ND6_QUEUEING */ |
||
131 | #define nd6_free_q(q) pbuf_free(q) |
||
132 | #endif /* LWIP_ND6_QUEUEING */ |
||
133 | static void nd6_send_q(s8_t i); |
||
134 | |||
135 | |||
136 | /** |
||
137 | * A local address has been determined to be a duplicate. Take the appropriate |
||
138 | * action(s) on the address and the interface as a whole. |
||
139 | * |
||
140 | * @param netif the netif that owns the address |
||
141 | * @param addr_idx the index of the address detected to be a duplicate |
||
142 | */ |
||
143 | static void |
||
144 | nd6_duplicate_addr_detected(struct netif *netif, s8_t addr_idx) |
||
145 | { |
||
146 | |||
147 | /* Mark the address as duplicate, but leave its lifetimes alone. If this was |
||
148 | * a manually assigned address, it will remain in existence as duplicate, and |
||
149 | * as such be unusable for any practical purposes until manual intervention. |
||
150 | * If this was an autogenerated address, the address will follow normal |
||
151 | * expiration rules, and thus disappear once its valid lifetime expires. */ |
||
152 | netif_ip6_addr_set_state(netif, addr_idx, IP6_ADDR_DUPLICATED); |
||
153 | |||
154 | #if LWIP_IPV6_AUTOCONFIG |
||
155 | /* If the affected address was the link-local address that we use to generate |
||
156 | * all other addresses, then we should not continue to use those derived |
||
157 | * addresses either, so mark them as duplicate as well. For autoconfig-only |
||
158 | * setups, this will make the interface effectively unusable, approaching the |
||
159 | * intention of RFC 4862 Sec. 5.4.5. @todo implement the full requirements */ |
||
160 | if (addr_idx == 0) { |
||
161 | s8_t i; |
||
162 | for (i = 1; i < LWIP_IPV6_NUM_ADDRESSES; i++) { |
||
163 | if (!ip6_addr_isinvalid(netif_ip6_addr_state(netif, i)) && |
||
164 | !netif_ip6_addr_isstatic(netif, i)) { |
||
165 | netif_ip6_addr_set_state(netif, i, IP6_ADDR_DUPLICATED); |
||
166 | } |
||
167 | } |
||
168 | } |
||
169 | #endif /* LWIP_IPV6_AUTOCONFIG */ |
||
170 | } |
||
171 | |||
172 | #if LWIP_IPV6_AUTOCONFIG |
||
173 | /** |
||
174 | * We received a router advertisement that contains a prefix with the |
||
175 | * autoconfiguration flag set. Add or update an associated autogenerated |
||
176 | * address. |
||
177 | * |
||
178 | * @param netif the netif on which the router advertisement arrived |
||
179 | * @param prefix_opt a pointer to the prefix option data |
||
180 | * @param prefix_addr an aligned copy of the prefix address |
||
181 | */ |
||
182 | static void |
||
183 | nd6_process_autoconfig_prefix(struct netif *netif, |
||
184 | struct prefix_option *prefix_opt, const ip6_addr_t *prefix_addr) |
||
185 | { |
||
186 | ip6_addr_t ip6addr; |
||
187 | u32_t valid_life, pref_life; |
||
188 | u8_t addr_state; |
||
189 | s8_t i, free_idx; |
||
190 | |||
191 | /* The caller already checks RFC 4862 Sec. 5.5.3 points (a) and (b). We do |
||
192 | * the rest, starting with checks for (c) and (d) here. */ |
||
193 | valid_life = lwip_htonl(prefix_opt->valid_lifetime); |
||
194 | pref_life = lwip_htonl(prefix_opt->preferred_lifetime); |
||
195 | if (pref_life > valid_life || prefix_opt->prefix_length != 64) { |
||
196 | return; /* silently ignore this prefix for autoconfiguration purposes */ |
||
197 | } |
||
198 | |||
199 | /* If an autogenerated address already exists for this prefix, update its |
||
200 | * lifetimes. An address is considered autogenerated if 1) it is not static |
||
201 | * (i.e., manually assigned), and 2) there is an advertised autoconfiguration |
||
202 | * prefix for it (the one we are processing here). This does not necessarily |
||
203 | * exclude the possibility that the address was actually assigned by, say, |
||
204 | * DHCPv6. If that distinction becomes important in the future, more state |
||
205 | * must be kept. As explained elsewhere we also update lifetimes of tentative |
||
206 | * and duplicate addresses. Skip address slot 0 (the link-local address). */ |
||
207 | for (i = 1; i < LWIP_IPV6_NUM_ADDRESSES; i++) { |
||
208 | addr_state = netif_ip6_addr_state(netif, i); |
||
209 | if (!ip6_addr_isinvalid(addr_state) && !netif_ip6_addr_isstatic(netif, i) && |
||
210 | ip6_addr_netcmp(prefix_addr, netif_ip6_addr(netif, i))) { |
||
211 | /* Update the valid lifetime, as per RFC 4862 Sec. 5.5.3 point (e). |
||
212 | * The valid lifetime will never drop to zero as a result of this. */ |
||
213 | u32_t remaining_life = netif_ip6_addr_valid_life(netif, i); |
||
214 | if (valid_life > ND6_2HRS || valid_life > remaining_life) { |
||
215 | netif_ip6_addr_set_valid_life(netif, i, valid_life); |
||
216 | } else if (remaining_life > ND6_2HRS) { |
||
217 | netif_ip6_addr_set_valid_life(netif, i, ND6_2HRS); |
||
218 | } |
||
219 | LWIP_ASSERT("bad valid lifetime", !netif_ip6_addr_isstatic(netif, i)); |
||
220 | /* Update the preferred lifetime. No bounds checks are needed here. In |
||
221 | * rare cases the advertisement may un-deprecate the address, though. |
||
222 | * Deprecation is left to the timer code where it is handled anyway. */ |
||
223 | if (pref_life > 0 && addr_state == IP6_ADDR_DEPRECATED) { |
||
224 | netif_ip6_addr_set_state(netif, i, IP6_ADDR_PREFERRED); |
||
225 | } |
||
226 | netif_ip6_addr_set_pref_life(netif, i, pref_life); |
||
227 | return; /* there should be at most one matching address */ |
||
228 | } |
||
229 | } |
||
230 | |||
231 | /* No autogenerated address exists for this prefix yet. See if we can add a |
||
232 | * new one. However, if IPv6 autoconfiguration is administratively disabled, |
||
233 | * do not generate new addresses, but do keep updating lifetimes for existing |
||
234 | * addresses. Also, when adding new addresses, we must protect explicitly |
||
235 | * against a valid lifetime of zero, because again, we use that as a special |
||
236 | * value. The generated address would otherwise expire immediately anyway. |
||
237 | * Finally, the original link-local address must be usable at all. We start |
||
238 | * creating addresses even if the link-local address is still in tentative |
||
239 | * state though, and deal with the fallout of that upon DAD collision. */ |
||
240 | addr_state = netif_ip6_addr_state(netif, 0); |
||
241 | if (!netif->ip6_autoconfig_enabled || valid_life == IP6_ADDR_LIFE_STATIC || |
||
242 | ip6_addr_isinvalid(addr_state) || ip6_addr_isduplicated(addr_state)) { |
||
243 | return; |
||
244 | } |
||
245 | |||
246 | /* Construct the new address that we intend to use, and then see if that |
||
247 | * address really does not exist. It might have been added manually, after |
||
248 | * all. As a side effect, find a free slot. Note that we cannot use |
||
249 | * netif_add_ip6_address() here, as it would return ERR_OK if the address |
||
250 | * already did exist, resulting in that address being given lifetimes. */ |
||
251 | IP6_ADDR(&ip6addr, prefix_addr->addr[0], prefix_addr->addr[1], |
||
252 | netif_ip6_addr(netif, 0)->addr[2], netif_ip6_addr(netif, 0)->addr[3]); |
||
253 | ip6_addr_assign_zone(&ip6addr, IP6_UNICAST, netif); |
||
254 | |||
255 | free_idx = 0; |
||
256 | for (i = 1; i < LWIP_IPV6_NUM_ADDRESSES; i++) { |
||
257 | if (!ip6_addr_isinvalid(netif_ip6_addr_state(netif, i))) { |
||
258 | if (ip6_addr_cmp(&ip6addr, netif_ip6_addr(netif, i))) { |
||
259 | return; /* formed address already exists */ |
||
260 | } |
||
261 | } else if (free_idx == 0) { |
||
262 | free_idx = i; |
||
263 | } |
||
264 | } |
||
265 | if (free_idx == 0) { |
||
266 | return; /* no address slots available, try again on next advertisement */ |
||
267 | } |
||
268 | |||
269 | /* Assign the new address to the interface. */ |
||
270 | ip_addr_copy_from_ip6(netif->ip6_addr[free_idx], ip6addr); |
||
271 | netif_ip6_addr_set_valid_life(netif, free_idx, valid_life); |
||
272 | netif_ip6_addr_set_pref_life(netif, free_idx, pref_life); |
||
273 | netif_ip6_addr_set_state(netif, free_idx, IP6_ADDR_TENTATIVE); |
||
274 | } |
||
275 | #endif /* LWIP_IPV6_AUTOCONFIG */ |
||
276 | |||
277 | /** |
||
278 | * Process an incoming neighbor discovery message |
||
279 | * |
||
280 | * @param p the nd packet, p->payload pointing to the icmpv6 header |
||
281 | * @param inp the netif on which this packet was received |
||
282 | */ |
||
283 | void |
||
284 | nd6_input(struct pbuf *p, struct netif *inp) |
||
285 | { |
||
286 | u8_t msg_type; |
||
287 | s8_t i; |
||
288 | |||
289 | ND6_STATS_INC(nd6.recv); |
||
290 | |||
291 | msg_type = *((u8_t *)p->payload); |
||
292 | switch (msg_type) { |
||
293 | case ICMP6_TYPE_NA: /* Neighbor Advertisement. */ |
||
294 | { |
||
295 | struct na_header *na_hdr; |
||
296 | struct lladdr_option *lladdr_opt; |
||
297 | ip6_addr_t target_address; |
||
298 | |||
299 | /* Check that na header fits in packet. */ |
||
300 | if (p->len < (sizeof(struct na_header))) { |
||
301 | /* @todo debug message */ |
||
302 | pbuf_free(p); |
||
303 | ND6_STATS_INC(nd6.lenerr); |
||
304 | ND6_STATS_INC(nd6.drop); |
||
305 | return; |
||
306 | } |
||
307 | |||
308 | na_hdr = (struct na_header *)p->payload; |
||
309 | |||
310 | /* Create an aligned, zoned copy of the target address. */ |
||
311 | ip6_addr_copy_from_packed(target_address, na_hdr->target_address); |
||
312 | ip6_addr_assign_zone(&target_address, IP6_UNICAST, inp); |
||
313 | |||
314 | /* Check a subset of the other RFC 4861 Sec. 7.1.2 requirements. */ |
||
315 | if (IP6H_HOPLIM(ip6_current_header()) != ND6_HOPLIM || na_hdr->code != 0 || |
||
316 | ip6_addr_ismulticast(&target_address)) { |
||
317 | pbuf_free(p); |
||
318 | ND6_STATS_INC(nd6.proterr); |
||
319 | ND6_STATS_INC(nd6.drop); |
||
320 | return; |
||
321 | } |
||
322 | |||
323 | /* @todo RFC MUST: if IP destination is multicast, Solicited flag is zero */ |
||
324 | /* @todo RFC MUST: all included options have a length greater than zero */ |
||
325 | |||
326 | /* Unsolicited NA?*/ |
||
327 | if (ip6_addr_ismulticast(ip6_current_dest_addr())) { |
||
328 | /* This is an unsolicited NA. |
||
329 | * link-layer changed? |
||
330 | * part of DAD mechanism? */ |
||
331 | |||
332 | #if LWIP_IPV6_DUP_DETECT_ATTEMPTS |
||
333 | /* If the target address matches this netif, it is a DAD response. */ |
||
334 | for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { |
||
335 | if (!ip6_addr_isinvalid(netif_ip6_addr_state(inp, i)) && |
||
336 | !ip6_addr_isduplicated(netif_ip6_addr_state(inp, i)) && |
||
337 | ip6_addr_cmp(&target_address, netif_ip6_addr(inp, i))) { |
||
338 | /* We are using a duplicate address. */ |
||
339 | nd6_duplicate_addr_detected(inp, i); |
||
340 | |||
341 | pbuf_free(p); |
||
342 | return; |
||
343 | } |
||
344 | } |
||
345 | #endif /* LWIP_IPV6_DUP_DETECT_ATTEMPTS */ |
||
346 | |||
347 | /* Check that link-layer address option also fits in packet. */ |
||
348 | if (p->len < (sizeof(struct na_header) + 2)) { |
||
349 | /* @todo debug message */ |
||
350 | pbuf_free(p); |
||
351 | ND6_STATS_INC(nd6.lenerr); |
||
352 | ND6_STATS_INC(nd6.drop); |
||
353 | return; |
||
354 | } |
||
355 | |||
356 | lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct na_header)); |
||
357 | |||
358 | if (p->len < (sizeof(struct na_header) + (lladdr_opt->length << 3))) { |
||
359 | /* @todo debug message */ |
||
360 | pbuf_free(p); |
||
361 | ND6_STATS_INC(nd6.lenerr); |
||
362 | ND6_STATS_INC(nd6.drop); |
||
363 | return; |
||
364 | } |
||
365 | |||
366 | /* This is an unsolicited NA, most likely there was a LLADDR change. */ |
||
367 | i = nd6_find_neighbor_cache_entry(&target_address); |
||
368 | if (i >= 0) { |
||
369 | if (na_hdr->flags & ND6_FLAG_OVERRIDE) { |
||
370 | MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len); |
||
371 | } |
||
372 | } |
||
373 | } else { |
||
374 | /* This is a solicited NA. |
||
375 | * neighbor address resolution response? |
||
376 | * neighbor unreachability detection response? */ |
||
377 | |||
378 | /* Find the cache entry corresponding to this na. */ |
||
379 | i = nd6_find_neighbor_cache_entry(&target_address); |
||
380 | if (i < 0) { |
||
381 | /* We no longer care about this target address. drop it. */ |
||
382 | pbuf_free(p); |
||
383 | return; |
||
384 | } |
||
385 | |||
386 | /* Update cache entry. */ |
||
387 | if ((na_hdr->flags & ND6_FLAG_OVERRIDE) || |
||
388 | (neighbor_cache[i].state == ND6_INCOMPLETE)) { |
||
389 | /* Check that link-layer address option also fits in packet. */ |
||
390 | if (p->len < (sizeof(struct na_header) + 2)) { |
||
391 | /* @todo debug message */ |
||
392 | pbuf_free(p); |
||
393 | ND6_STATS_INC(nd6.lenerr); |
||
394 | ND6_STATS_INC(nd6.drop); |
||
395 | return; |
||
396 | } |
||
397 | |||
398 | lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct na_header)); |
||
399 | |||
400 | if (p->len < (sizeof(struct na_header) + (lladdr_opt->length << 3))) { |
||
401 | /* @todo debug message */ |
||
402 | pbuf_free(p); |
||
403 | ND6_STATS_INC(nd6.lenerr); |
||
404 | ND6_STATS_INC(nd6.drop); |
||
405 | return; |
||
406 | } |
||
407 | |||
408 | MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len); |
||
409 | } |
||
410 | |||
411 | neighbor_cache[i].netif = inp; |
||
412 | neighbor_cache[i].state = ND6_REACHABLE; |
||
413 | neighbor_cache[i].counter.reachable_time = reachable_time; |
||
414 | |||
415 | /* Send queued packets, if any. */ |
||
416 | if (neighbor_cache[i].q != NULL) { |
||
417 | nd6_send_q(i); |
||
418 | } |
||
419 | } |
||
420 | |||
421 | break; /* ICMP6_TYPE_NA */ |
||
422 | } |
||
423 | case ICMP6_TYPE_NS: /* Neighbor solicitation. */ |
||
424 | { |
||
425 | struct ns_header *ns_hdr; |
||
426 | struct lladdr_option *lladdr_opt; |
||
427 | ip6_addr_t target_address; |
||
428 | u8_t accepted; |
||
429 | |||
430 | /* Check that ns header fits in packet. */ |
||
431 | if (p->len < sizeof(struct ns_header)) { |
||
432 | /* @todo debug message */ |
||
433 | pbuf_free(p); |
||
434 | ND6_STATS_INC(nd6.lenerr); |
||
435 | ND6_STATS_INC(nd6.drop); |
||
436 | return; |
||
437 | } |
||
438 | |||
439 | ns_hdr = (struct ns_header *)p->payload; |
||
440 | |||
441 | /* Create an aligned, zoned copy of the target address. */ |
||
442 | ip6_addr_copy_from_packed(target_address, ns_hdr->target_address); |
||
443 | ip6_addr_assign_zone(&target_address, IP6_UNICAST, inp); |
||
444 | |||
445 | /* Check a subset of the other RFC 4861 Sec. 7.1.1 requirements. */ |
||
446 | if (IP6H_HOPLIM(ip6_current_header()) != ND6_HOPLIM || ns_hdr->code != 0 || |
||
447 | ip6_addr_ismulticast(&target_address)) { |
||
448 | pbuf_free(p); |
||
449 | ND6_STATS_INC(nd6.proterr); |
||
450 | ND6_STATS_INC(nd6.drop); |
||
451 | return; |
||
452 | } |
||
453 | |||
454 | /* @todo RFC MUST: all included options have a length greater than zero */ |
||
455 | /* @todo RFC MUST: if IP source is 'any', destination is solicited-node multicast address */ |
||
456 | /* @todo RFC MUST: if IP source is 'any', there is no source LL address option */ |
||
457 | |||
458 | /* Check if there is a link-layer address provided. Only point to it if in this buffer. */ |
||
459 | if (p->len >= (sizeof(struct ns_header) + 2)) { |
||
460 | lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct ns_header)); |
||
461 | if (p->len < (sizeof(struct ns_header) + (lladdr_opt->length << 3))) { |
||
462 | lladdr_opt = NULL; |
||
463 | } |
||
464 | } else { |
||
465 | lladdr_opt = NULL; |
||
466 | } |
||
467 | |||
468 | /* Check if the target address is configured on the receiving netif. */ |
||
469 | accepted = 0; |
||
470 | for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; ++i) { |
||
471 | if ((ip6_addr_isvalid(netif_ip6_addr_state(inp, i)) || |
||
472 | (ip6_addr_istentative(netif_ip6_addr_state(inp, i)) && |
||
473 | ip6_addr_isany(ip6_current_src_addr()))) && |
||
474 | ip6_addr_cmp(&target_address, netif_ip6_addr(inp, i))) { |
||
475 | accepted = 1; |
||
476 | break; |
||
477 | } |
||
478 | } |
||
479 | |||
480 | /* NS not for us? */ |
||
481 | if (!accepted) { |
||
482 | pbuf_free(p); |
||
483 | return; |
||
484 | } |
||
485 | |||
486 | /* Check for ANY address in src (DAD algorithm). */ |
||
487 | if (ip6_addr_isany(ip6_current_src_addr())) { |
||
488 | /* Sender is validating this address. */ |
||
489 | for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; ++i) { |
||
490 | if (!ip6_addr_isinvalid(netif_ip6_addr_state(inp, i)) && |
||
491 | ip6_addr_cmp(&target_address, netif_ip6_addr(inp, i))) { |
||
492 | /* Send a NA back so that the sender does not use this address. */ |
||
493 | nd6_send_na(inp, netif_ip6_addr(inp, i), ND6_FLAG_OVERRIDE | ND6_SEND_FLAG_ALLNODES_DEST); |
||
494 | if (ip6_addr_istentative(netif_ip6_addr_state(inp, i))) { |
||
495 | /* We shouldn't use this address either. */ |
||
496 | nd6_duplicate_addr_detected(inp, i); |
||
497 | } |
||
498 | } |
||
499 | } |
||
500 | } else { |
||
501 | /* Sender is trying to resolve our address. */ |
||
502 | /* Verify that they included their own link-layer address. */ |
||
503 | if (lladdr_opt == NULL) { |
||
504 | /* Not a valid message. */ |
||
505 | pbuf_free(p); |
||
506 | ND6_STATS_INC(nd6.proterr); |
||
507 | ND6_STATS_INC(nd6.drop); |
||
508 | return; |
||
509 | } |
||
510 | |||
511 | i = nd6_find_neighbor_cache_entry(ip6_current_src_addr()); |
||
512 | if (i>= 0) { |
||
513 | /* We already have a record for the solicitor. */ |
||
514 | if (neighbor_cache[i].state == ND6_INCOMPLETE) { |
||
515 | neighbor_cache[i].netif = inp; |
||
516 | MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len); |
||
517 | |||
518 | /* Delay probe in case we get confirmation of reachability from upper layer (TCP). */ |
||
519 | neighbor_cache[i].state = ND6_DELAY; |
||
520 | neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME / ND6_TMR_INTERVAL; |
||
521 | } |
||
522 | } else { |
||
523 | /* Add their IPv6 address and link-layer address to neighbor cache. |
||
524 | * We will need it at least to send a unicast NA message, but most |
||
525 | * likely we will also be communicating with this node soon. */ |
||
526 | i = nd6_new_neighbor_cache_entry(); |
||
527 | if (i < 0) { |
||
528 | /* We couldn't assign a cache entry for this neighbor. |
||
529 | * we won't be able to reply. drop it. */ |
||
530 | pbuf_free(p); |
||
531 | ND6_STATS_INC(nd6.memerr); |
||
532 | return; |
||
533 | } |
||
534 | neighbor_cache[i].netif = inp; |
||
535 | MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len); |
||
536 | ip6_addr_set(&(neighbor_cache[i].next_hop_address), ip6_current_src_addr()); |
||
537 | |||
538 | /* Receiving a message does not prove reachability: only in one direction. |
||
539 | * Delay probe in case we get confirmation of reachability from upper layer (TCP). */ |
||
540 | neighbor_cache[i].state = ND6_DELAY; |
||
541 | neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME / ND6_TMR_INTERVAL; |
||
542 | } |
||
543 | |||
544 | /* Send back a NA for us. Allocate the reply pbuf. */ |
||
545 | nd6_send_na(inp, &target_address, ND6_FLAG_SOLICITED | ND6_FLAG_OVERRIDE); |
||
546 | } |
||
547 | |||
548 | break; /* ICMP6_TYPE_NS */ |
||
549 | } |
||
550 | case ICMP6_TYPE_RA: /* Router Advertisement. */ |
||
551 | { |
||
552 | struct ra_header *ra_hdr; |
||
553 | u8_t *buffer; /* Used to copy options. */ |
||
554 | u16_t offset; |
||
555 | #if LWIP_ND6_RDNSS_MAX_DNS_SERVERS |
||
556 | /* There can be multiple RDNSS options per RA */ |
||
557 | u8_t rdnss_server_idx = 0; |
||
558 | #endif /* LWIP_ND6_RDNSS_MAX_DNS_SERVERS */ |
||
559 | |||
560 | /* Check that RA header fits in packet. */ |
||
561 | if (p->len < sizeof(struct ra_header)) { |
||
562 | /* @todo debug message */ |
||
563 | pbuf_free(p); |
||
564 | ND6_STATS_INC(nd6.lenerr); |
||
565 | ND6_STATS_INC(nd6.drop); |
||
566 | return; |
||
567 | } |
||
568 | |||
569 | ra_hdr = (struct ra_header *)p->payload; |
||
570 | |||
571 | /* Check a subset of the other RFC 4861 Sec. 6.1.2 requirements. */ |
||
572 | if (!ip6_addr_islinklocal(ip6_current_src_addr()) || |
||
573 | IP6H_HOPLIM(ip6_current_header()) != ND6_HOPLIM || ra_hdr->code != 0) { |
||
574 | pbuf_free(p); |
||
575 | ND6_STATS_INC(nd6.proterr); |
||
576 | ND6_STATS_INC(nd6.drop); |
||
577 | return; |
||
578 | } |
||
579 | |||
580 | /* @todo RFC MUST: all included options have a length greater than zero */ |
||
581 | |||
582 | /* If we are sending RS messages, stop. */ |
||
583 | #if LWIP_IPV6_SEND_ROUTER_SOLICIT |
||
584 | /* ensure at least one solicitation is sent */ |
||
585 | if ((inp->rs_count < LWIP_ND6_MAX_MULTICAST_SOLICIT) || |
||
586 | (nd6_send_rs(inp) == ERR_OK)) { |
||
587 | inp->rs_count = 0; |
||
588 | } |
||
589 | #endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */ |
||
590 | |||
591 | /* Get the matching default router entry. */ |
||
592 | i = nd6_get_router(ip6_current_src_addr(), inp); |
||
593 | if (i < 0) { |
||
594 | /* Create a new router entry. */ |
||
595 | i = nd6_new_router(ip6_current_src_addr(), inp); |
||
596 | } |
||
597 | |||
598 | if (i < 0) { |
||
599 | /* Could not create a new router entry. */ |
||
600 | pbuf_free(p); |
||
601 | ND6_STATS_INC(nd6.memerr); |
||
602 | return; |
||
603 | } |
||
604 | |||
605 | /* Re-set invalidation timer. */ |
||
606 | default_router_list[i].invalidation_timer = lwip_htons(ra_hdr->router_lifetime); |
||
607 | |||
608 | /* Re-set default timer values. */ |
||
609 | #if LWIP_ND6_ALLOW_RA_UPDATES |
||
610 | if (ra_hdr->retrans_timer > 0) { |
||
611 | retrans_timer = lwip_htonl(ra_hdr->retrans_timer); |
||
612 | } |
||
613 | if (ra_hdr->reachable_time > 0) { |
||
614 | reachable_time = lwip_htonl(ra_hdr->reachable_time); |
||
615 | } |
||
616 | #endif /* LWIP_ND6_ALLOW_RA_UPDATES */ |
||
617 | |||
618 | /* @todo set default hop limit... */ |
||
619 | /* ra_hdr->current_hop_limit;*/ |
||
620 | |||
621 | /* Update flags in local entry (incl. preference). */ |
||
622 | default_router_list[i].flags = ra_hdr->flags; |
||
623 | |||
624 | /* Offset to options. */ |
||
625 | offset = sizeof(struct ra_header); |
||
626 | |||
627 | /* Process each option. */ |
||
628 | while ((p->tot_len - offset) >= 2) { |
||
629 | u8_t option_type; |
||
630 | u16_t option_len; |
||
631 | int option_len8 = pbuf_try_get_at(p, offset + 1); |
||
632 | if (option_len8 <= 0) { |
||
633 | /* read beyond end or zero length */ |
||
634 | goto lenerr_drop_free_return; |
||
635 | } |
||
636 | option_len = ((u8_t)option_len8) << 3; |
||
637 | if (option_len > p->tot_len - offset) { |
||
638 | /* short packet (option does not fit in) */ |
||
639 | goto lenerr_drop_free_return; |
||
640 | } |
||
641 | if (p->len == p->tot_len) { |
||
642 | /* no need to copy from contiguous pbuf */ |
||
643 | buffer = &((u8_t*)p->payload)[offset]; |
||
644 | } else { |
||
645 | /* check if this option fits into our buffer */ |
||
646 | if (option_len > sizeof(nd6_ra_buffer)) { |
||
647 | option_type = pbuf_get_at(p, offset); |
||
648 | /* invalid option length */ |
||
649 | if (option_type != ND6_OPTION_TYPE_RDNSS) { |
||
650 | goto lenerr_drop_free_return; |
||
651 | } |
||
652 | /* we allow RDNSS option to be longer - we'll just drop some servers */ |
||
653 | option_len = sizeof(nd6_ra_buffer); |
||
654 | } |
||
655 | buffer = (u8_t*)&nd6_ra_buffer; |
||
656 | option_len = pbuf_copy_partial(p, &nd6_ra_buffer, option_len, offset); |
||
657 | } |
||
658 | option_type = buffer[0]; |
||
659 | switch (option_type) { |
||
660 | case ND6_OPTION_TYPE_SOURCE_LLADDR: |
||
661 | { |
||
662 | struct lladdr_option *lladdr_opt; |
||
663 | if (option_len < sizeof(struct lladdr_option)) { |
||
664 | goto lenerr_drop_free_return; |
||
665 | } |
||
666 | lladdr_opt = (struct lladdr_option *)buffer; |
||
667 | if ((default_router_list[i].neighbor_entry != NULL) && |
||
668 | (default_router_list[i].neighbor_entry->state == ND6_INCOMPLETE)) { |
||
669 | SMEMCPY(default_router_list[i].neighbor_entry->lladdr, lladdr_opt->addr, inp->hwaddr_len); |
||
670 | default_router_list[i].neighbor_entry->state = ND6_REACHABLE; |
||
671 | default_router_list[i].neighbor_entry->counter.reachable_time = reachable_time; |
||
672 | } |
||
673 | break; |
||
674 | } |
||
675 | case ND6_OPTION_TYPE_MTU: |
||
676 | { |
||
677 | struct mtu_option *mtu_opt; |
||
678 | if (option_len < sizeof(struct mtu_option)) { |
||
679 | goto lenerr_drop_free_return; |
||
680 | } |
||
681 | mtu_opt = (struct mtu_option *)buffer; |
||
682 | if (lwip_htonl(mtu_opt->mtu) >= 1280) { |
||
683 | #if LWIP_ND6_ALLOW_RA_UPDATES |
||
684 | inp->mtu = (u16_t)lwip_htonl(mtu_opt->mtu); |
||
685 | #endif /* LWIP_ND6_ALLOW_RA_UPDATES */ |
||
686 | } |
||
687 | break; |
||
688 | } |
||
689 | case ND6_OPTION_TYPE_PREFIX_INFO: |
||
690 | { |
||
691 | struct prefix_option *prefix_opt; |
||
692 | ip6_addr_t prefix_addr; |
||
693 | if (option_len < sizeof(struct prefix_option)) { |
||
694 | goto lenerr_drop_free_return; |
||
695 | } |
||
696 | |||
697 | prefix_opt = (struct prefix_option *)buffer; |
||
698 | |||
699 | /* Get a memory-aligned copy of the prefix. */ |
||
700 | ip6_addr_copy_from_packed(prefix_addr, prefix_opt->prefix); |
||
701 | ip6_addr_assign_zone(&prefix_addr, IP6_UNICAST, inp); |
||
702 | |||
703 | if (!ip6_addr_islinklocal(&prefix_addr)) { |
||
704 | if ((prefix_opt->flags & ND6_PREFIX_FLAG_ON_LINK) && |
||
705 | (prefix_opt->prefix_length == 64)) { |
||
706 | /* Add to on-link prefix list. */ |
||
707 | u32_t valid_life; |
||
708 | s8_t prefix; |
||
709 | |||
710 | valid_life = lwip_htonl(prefix_opt->valid_lifetime); |
||
711 | |||
712 | /* find cache entry for this prefix. */ |
||
713 | prefix = nd6_get_onlink_prefix(&prefix_addr, inp); |
||
714 | if (prefix < 0 && valid_life > 0) { |
||
715 | /* Create a new cache entry. */ |
||
716 | prefix = nd6_new_onlink_prefix(&prefix_addr, inp); |
||
717 | } |
||
718 | if (prefix >= 0) { |
||
719 | prefix_list[prefix].invalidation_timer = valid_life; |
||
720 | } |
||
721 | } |
||
722 | #if LWIP_IPV6_AUTOCONFIG |
||
723 | if (prefix_opt->flags & ND6_PREFIX_FLAG_AUTONOMOUS) { |
||
724 | /* Perform processing for autoconfiguration. */ |
||
725 | nd6_process_autoconfig_prefix(inp, prefix_opt, &prefix_addr); |
||
726 | } |
||
727 | #endif /* LWIP_IPV6_AUTOCONFIG */ |
||
728 | } |
||
729 | |||
730 | break; |
||
731 | } |
||
732 | case ND6_OPTION_TYPE_ROUTE_INFO: |
||
733 | /* @todo implement preferred routes. |
||
734 | struct route_option * route_opt; |
||
735 | route_opt = (struct route_option *)buffer;*/ |
||
736 | |||
737 | break; |
||
738 | #if LWIP_ND6_RDNSS_MAX_DNS_SERVERS |
||
739 | case ND6_OPTION_TYPE_RDNSS: |
||
740 | { |
||
741 | u8_t num, n; |
||
742 | u16_t copy_offset = offset + SIZEOF_RDNSS_OPTION_BASE; |
||
743 | struct rdnss_option * rdnss_opt; |
||
744 | if (option_len < SIZEOF_RDNSS_OPTION_BASE) { |
||
745 | goto lenerr_drop_free_return; |
||
746 | } |
||
747 | |||
748 | rdnss_opt = (struct rdnss_option *)buffer; |
||
749 | num = (rdnss_opt->length - 1) / 2; |
||
750 | for (n = 0; (rdnss_server_idx < DNS_MAX_SERVERS) && (n < num); n++) { |
||
751 | ip_addr_t rdnss_address; |
||
752 | |||
753 | /* Copy directly from pbuf to get an aligned, zoned copy of the prefix. */ |
||
754 | if (pbuf_copy_partial(p, &rdnss_address, sizeof(ip6_addr_p_t), copy_offset) == sizeof(ip6_addr_p_t)) { |
||
755 | IP_SET_TYPE_VAL(rdnss_address, IPADDR_TYPE_V6); |
||
756 | ip6_addr_assign_zone(ip_2_ip6(&rdnss_address), IP6_UNKNOWN, inp); |
||
757 | |||
758 | if (htonl(rdnss_opt->lifetime) > 0) { |
||
759 | /* TODO implement Lifetime > 0 */ |
||
760 | dns_setserver(rdnss_server_idx++, &rdnss_address); |
||
761 | } else { |
||
762 | /* TODO implement DNS removal in dns.c */ |
||
763 | u8_t s; |
||
764 | for (s = 0; s < DNS_MAX_SERVERS; s++) { |
||
765 | const ip_addr_t *addr = dns_getserver(s); |
||
766 | if(ip_addr_cmp(addr, &rdnss_address)) { |
||
767 | dns_setserver(s, NULL); |
||
768 | } |
||
769 | } |
||
770 | } |
||
771 | } |
||
772 | } |
||
773 | break; |
||
774 | } |
||
775 | #endif /* LWIP_ND6_RDNSS_MAX_DNS_SERVERS */ |
||
776 | default: |
||
777 | /* Unrecognized option, abort. */ |
||
778 | ND6_STATS_INC(nd6.proterr); |
||
779 | break; |
||
780 | } |
||
781 | /* option length is checked earlier to be non-zero to make sure loop ends */ |
||
782 | offset += 8 * (u8_t)option_len8; |
||
783 | } |
||
784 | |||
785 | break; /* ICMP6_TYPE_RA */ |
||
786 | } |
||
787 | case ICMP6_TYPE_RD: /* Redirect */ |
||
788 | { |
||
789 | struct redirect_header *redir_hdr; |
||
790 | struct lladdr_option *lladdr_opt; |
||
791 | ip6_addr_t destination_address, target_address; |
||
792 | |||
793 | /* Check that Redir header fits in packet. */ |
||
794 | if (p->len < sizeof(struct redirect_header)) { |
||
795 | /* @todo debug message */ |
||
796 | pbuf_free(p); |
||
797 | ND6_STATS_INC(nd6.lenerr); |
||
798 | ND6_STATS_INC(nd6.drop); |
||
799 | return; |
||
800 | } |
||
801 | |||
802 | redir_hdr = (struct redirect_header *)p->payload; |
||
803 | |||
804 | /* Create an aligned, zoned copy of the destination address. */ |
||
805 | ip6_addr_copy_from_packed(destination_address, redir_hdr->destination_address); |
||
806 | ip6_addr_assign_zone(&destination_address, IP6_UNICAST, inp); |
||
807 | |||
808 | /* Check a subset of the other RFC 4861 Sec. 8.1 requirements. */ |
||
809 | if (!ip6_addr_islinklocal(ip6_current_src_addr()) || |
||
810 | IP6H_HOPLIM(ip6_current_header()) != ND6_HOPLIM || |
||
811 | redir_hdr->code != 0 || ip6_addr_ismulticast(&destination_address)) { |
||
812 | pbuf_free(p); |
||
813 | ND6_STATS_INC(nd6.proterr); |
||
814 | ND6_STATS_INC(nd6.drop); |
||
815 | return; |
||
816 | } |
||
817 | |||
818 | /* @todo RFC MUST: IP source address equals first-hop router for destination_address */ |
||
819 | /* @todo RFC MUST: ICMP target address is either link-local address or same as destination_address */ |
||
820 | /* @todo RFC MUST: all included options have a length greater than zero */ |
||
821 | |||
822 | if (p->len >= (sizeof(struct redirect_header) + 2)) { |
||
823 | lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct redirect_header)); |
||
824 | if (p->len < (sizeof(struct redirect_header) + (lladdr_opt->length << 3))) { |
||
825 | lladdr_opt = NULL; |
||
826 | } |
||
827 | } else { |
||
828 | lladdr_opt = NULL; |
||
829 | } |
||
830 | |||
831 | /* Find dest address in cache */ |
||
832 | i = nd6_find_destination_cache_entry(&destination_address); |
||
833 | if (i < 0) { |
||
834 | /* Destination not in cache, drop packet. */ |
||
835 | pbuf_free(p); |
||
836 | return; |
||
837 | } |
||
838 | |||
839 | /* Create an aligned, zoned copy of the target address. */ |
||
840 | ip6_addr_copy_from_packed(target_address, redir_hdr->target_address); |
||
841 | ip6_addr_assign_zone(&target_address, IP6_UNICAST, inp); |
||
842 | |||
843 | /* Set the new target address. */ |
||
844 | ip6_addr_copy(destination_cache[i].next_hop_addr, target_address); |
||
845 | |||
846 | /* If Link-layer address of other router is given, try to add to neighbor cache. */ |
||
847 | if (lladdr_opt != NULL) { |
||
848 | if (lladdr_opt->type == ND6_OPTION_TYPE_TARGET_LLADDR) { |
||
849 | i = nd6_find_neighbor_cache_entry(&target_address); |
||
850 | if (i < 0) { |
||
851 | i = nd6_new_neighbor_cache_entry(); |
||
852 | if (i >= 0) { |
||
853 | neighbor_cache[i].netif = inp; |
||
854 | MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len); |
||
855 | ip6_addr_copy(neighbor_cache[i].next_hop_address, target_address); |
||
856 | |||
857 | /* Receiving a message does not prove reachability: only in one direction. |
||
858 | * Delay probe in case we get confirmation of reachability from upper layer (TCP). */ |
||
859 | neighbor_cache[i].state = ND6_DELAY; |
||
860 | neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME / ND6_TMR_INTERVAL; |
||
861 | } |
||
862 | } |
||
863 | if (i >= 0) { |
||
864 | if (neighbor_cache[i].state == ND6_INCOMPLETE) { |
||
865 | MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len); |
||
866 | /* Receiving a message does not prove reachability: only in one direction. |
||
867 | * Delay probe in case we get confirmation of reachability from upper layer (TCP). */ |
||
868 | neighbor_cache[i].state = ND6_DELAY; |
||
869 | neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME / ND6_TMR_INTERVAL; |
||
870 | } |
||
871 | } |
||
872 | } |
||
873 | } |
||
874 | break; /* ICMP6_TYPE_RD */ |
||
875 | } |
||
876 | case ICMP6_TYPE_PTB: /* Packet too big */ |
||
877 | { |
||
878 | struct icmp6_hdr *icmp6hdr; /* Packet too big message */ |
||
879 | struct ip6_hdr *ip6hdr; /* IPv6 header of the packet which caused the error */ |
||
880 | u32_t pmtu; |
||
881 | ip6_addr_t destination_address; |
||
882 | |||
883 | /* Check that ICMPv6 header + IPv6 header fit in payload */ |
||
884 | if (p->len < (sizeof(struct icmp6_hdr) + IP6_HLEN)) { |
||
885 | /* drop short packets */ |
||
886 | pbuf_free(p); |
||
887 | ND6_STATS_INC(nd6.lenerr); |
||
888 | ND6_STATS_INC(nd6.drop); |
||
889 | return; |
||
890 | } |
||
891 | |||
892 | icmp6hdr = (struct icmp6_hdr *)p->payload; |
||
893 | ip6hdr = (struct ip6_hdr *)((u8_t*)p->payload + sizeof(struct icmp6_hdr)); |
||
894 | |||
895 | /* Create an aligned, zoned copy of the destination address. */ |
||
896 | ip6_addr_copy_from_packed(destination_address, ip6hdr->dest); |
||
897 | ip6_addr_assign_zone(&destination_address, IP6_UNKNOWN, inp); |
||
898 | |||
899 | /* Look for entry in destination cache. */ |
||
900 | i = nd6_find_destination_cache_entry(&destination_address); |
||
901 | if (i < 0) { |
||
902 | /* Destination not in cache, drop packet. */ |
||
903 | pbuf_free(p); |
||
904 | return; |
||
905 | } |
||
906 | |||
907 | /* Change the Path MTU. */ |
||
908 | pmtu = lwip_htonl(icmp6hdr->data); |
||
909 | destination_cache[i].pmtu = (u16_t)LWIP_MIN(pmtu, 0xFFFF); |
||
910 | |||
911 | break; /* ICMP6_TYPE_PTB */ |
||
912 | } |
||
913 | |||
914 | default: |
||
915 | ND6_STATS_INC(nd6.proterr); |
||
916 | ND6_STATS_INC(nd6.drop); |
||
917 | break; /* default */ |
||
918 | } |
||
919 | |||
920 | pbuf_free(p); |
||
921 | return; |
||
922 | lenerr_drop_free_return: |
||
923 | ND6_STATS_INC(nd6.lenerr); |
||
924 | ND6_STATS_INC(nd6.drop); |
||
925 | pbuf_free(p); |
||
926 | } |
||
927 | |||
928 | |||
929 | /** |
||
930 | * Periodic timer for Neighbor discovery functions: |
||
931 | * |
||
932 | * - Update neighbor reachability states |
||
933 | * - Update destination cache entries age |
||
934 | * - Update invalidation timers of default routers and on-link prefixes |
||
935 | * - Update lifetimes of our addresses |
||
936 | * - Perform duplicate address detection (DAD) for our addresses |
||
937 | * - Send router solicitations |
||
938 | */ |
||
939 | void |
||
940 | nd6_tmr(void) |
||
941 | { |
||
942 | s8_t i; |
||
943 | struct netif *netif; |
||
944 | |||
945 | /* Process neighbor entries. */ |
||
946 | for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { |
||
947 | switch (neighbor_cache[i].state) { |
||
948 | case ND6_INCOMPLETE: |
||
949 | if ((neighbor_cache[i].counter.probes_sent >= LWIP_ND6_MAX_MULTICAST_SOLICIT) && |
||
950 | (!neighbor_cache[i].isrouter)) { |
||
951 | /* Retries exceeded. */ |
||
952 | nd6_free_neighbor_cache_entry(i); |
||
953 | } else { |
||
954 | /* Send a NS for this entry. */ |
||
955 | neighbor_cache[i].counter.probes_sent++; |
||
956 | nd6_send_neighbor_cache_probe(&neighbor_cache[i], ND6_SEND_FLAG_MULTICAST_DEST); |
||
957 | } |
||
958 | break; |
||
959 | case ND6_REACHABLE: |
||
960 | /* Send queued packets, if any are left. Should have been sent already. */ |
||
961 | if (neighbor_cache[i].q != NULL) { |
||
962 | nd6_send_q(i); |
||
963 | } |
||
964 | if (neighbor_cache[i].counter.reachable_time <= ND6_TMR_INTERVAL) { |
||
965 | /* Change to stale state. */ |
||
966 | neighbor_cache[i].state = ND6_STALE; |
||
967 | neighbor_cache[i].counter.stale_time = 0; |
||
968 | } else { |
||
969 | neighbor_cache[i].counter.reachable_time -= ND6_TMR_INTERVAL; |
||
970 | } |
||
971 | break; |
||
972 | case ND6_STALE: |
||
973 | neighbor_cache[i].counter.stale_time++; |
||
974 | break; |
||
975 | case ND6_DELAY: |
||
976 | if (neighbor_cache[i].counter.delay_time <= 1) { |
||
977 | /* Change to PROBE state. */ |
||
978 | neighbor_cache[i].state = ND6_PROBE; |
||
979 | neighbor_cache[i].counter.probes_sent = 0; |
||
980 | } else { |
||
981 | neighbor_cache[i].counter.delay_time--; |
||
982 | } |
||
983 | break; |
||
984 | case ND6_PROBE: |
||
985 | if ((neighbor_cache[i].counter.probes_sent >= LWIP_ND6_MAX_MULTICAST_SOLICIT) && |
||
986 | (!neighbor_cache[i].isrouter)) { |
||
987 | /* Retries exceeded. */ |
||
988 | nd6_free_neighbor_cache_entry(i); |
||
989 | } else { |
||
990 | /* Send a NS for this entry. */ |
||
991 | neighbor_cache[i].counter.probes_sent++; |
||
992 | nd6_send_neighbor_cache_probe(&neighbor_cache[i], 0); |
||
993 | } |
||
994 | break; |
||
995 | case ND6_NO_ENTRY: |
||
996 | default: |
||
997 | /* Do nothing. */ |
||
998 | break; |
||
999 | } |
||
1000 | } |
||
1001 | |||
1002 | /* Process destination entries. */ |
||
1003 | for (i = 0; i < LWIP_ND6_NUM_DESTINATIONS; i++) { |
||
1004 | destination_cache[i].age++; |
||
1005 | } |
||
1006 | |||
1007 | /* Process router entries. */ |
||
1008 | for (i = 0; i < LWIP_ND6_NUM_ROUTERS; i++) { |
||
1009 | if (default_router_list[i].neighbor_entry != NULL) { |
||
1010 | /* Active entry. */ |
||
1011 | if (default_router_list[i].invalidation_timer <= ND6_TMR_INTERVAL / 1000) { |
||
1012 | /* No more than 1 second remaining. Clear this entry. Also clear any of |
||
1013 | * its destination cache entries, as per RFC 4861 Sec. 5.3 and 6.3.5. */ |
||
1014 | s8_t j; |
||
1015 | for (j = 0; j < LWIP_ND6_NUM_DESTINATIONS; j++) { |
||
1016 | if (ip6_addr_cmp(&destination_cache[j].next_hop_addr, |
||
1017 | &default_router_list[i].neighbor_entry->next_hop_address)) { |
||
1018 | ip6_addr_set_any(&destination_cache[j].destination_addr); |
||
1019 | } |
||
1020 | } |
||
1021 | default_router_list[i].neighbor_entry->isrouter = 0; |
||
1022 | default_router_list[i].neighbor_entry = NULL; |
||
1023 | default_router_list[i].invalidation_timer = 0; |
||
1024 | default_router_list[i].flags = 0; |
||
1025 | } else { |
||
1026 | default_router_list[i].invalidation_timer -= ND6_TMR_INTERVAL / 1000; |
||
1027 | } |
||
1028 | } |
||
1029 | } |
||
1030 | |||
1031 | /* Process prefix entries. */ |
||
1032 | for (i = 0; i < LWIP_ND6_NUM_PREFIXES; i++) { |
||
1033 | if (prefix_list[i].netif != NULL) { |
||
1034 | if (prefix_list[i].invalidation_timer <= ND6_TMR_INTERVAL / 1000) { |
||
1035 | /* Entry timed out, remove it */ |
||
1036 | prefix_list[i].invalidation_timer = 0; |
||
1037 | prefix_list[i].netif = NULL; |
||
1038 | } else { |
||
1039 | prefix_list[i].invalidation_timer -= ND6_TMR_INTERVAL / 1000; |
||
1040 | } |
||
1041 | } |
||
1042 | } |
||
1043 | |||
1044 | /* Process our own addresses, updating address lifetimes and/or DAD state. */ |
||
1045 | NETIF_FOREACH(netif) { |
||
1046 | for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; ++i) { |
||
1047 | u8_t addr_state; |
||
1048 | #if LWIP_IPV6_ADDRESS_LIFETIMES |
||
1049 | /* Step 1: update address lifetimes (valid and preferred). */ |
||
1050 | addr_state = netif_ip6_addr_state(netif, i); |
||
1051 | /* RFC 4862 is not entirely clear as to whether address lifetimes affect |
||
1052 | * tentative addresses, and is even less clear as to what should happen |
||
1053 | * with duplicate addresses. We choose to track and update lifetimes for |
||
1054 | * both those types, although for different reasons: |
||
1055 | * - for tentative addresses, the line of thought of Sec. 5.7 combined |
||
1056 | * with the potentially long period that an address may be in tentative |
||
1057 | * state (due to the interface being down) suggests that lifetimes |
||
1058 | * should be independent of external factors which would include DAD; |
||
1059 | * - for duplicate addresses, retiring them early could result in a new |
||
1060 | * but unwanted attempt at marking them as valid, while retiring them |
||
1061 | * late/never could clog up address slots on the netif. |
||
1062 | * As a result, we may end up expiring addresses of either type here. |
||
1063 | */ |
||
1064 | if (!ip6_addr_isinvalid(addr_state) && |
||
1065 | !netif_ip6_addr_isstatic(netif, i)) { |
||
1066 | u32_t life = netif_ip6_addr_valid_life(netif, i); |
||
1067 | if (life <= ND6_TMR_INTERVAL / 1000) { |
||
1068 | /* The address has expired. */ |
||
1069 | netif_ip6_addr_set_valid_life(netif, i, 0); |
||
1070 | netif_ip6_addr_set_pref_life(netif, i, 0); |
||
1071 | netif_ip6_addr_set_state(netif, i, IP6_ADDR_INVALID); |
||
1072 | } else { |
||
1073 | if (!ip6_addr_life_isinfinite(life)) { |
||
1074 | life -= ND6_TMR_INTERVAL / 1000; |
||
1075 | LWIP_ASSERT("bad valid lifetime", life != IP6_ADDR_LIFE_STATIC); |
||
1076 | netif_ip6_addr_set_valid_life(netif, i, life); |
||
1077 | } |
||
1078 | /* The address is still here. Update the preferred lifetime too. */ |
||
1079 | life = netif_ip6_addr_pref_life(netif, i); |
||
1080 | if (life <= ND6_TMR_INTERVAL / 1000) { |
||
1081 | /* This case must also trigger if 'life' was already zero, so as to |
||
1082 | * deal correctly with advertised preferred-lifetime reductions. */ |
||
1083 | netif_ip6_addr_set_pref_life(netif, i, 0); |
||
1084 | if (addr_state == IP6_ADDR_PREFERRED) |
||
1085 | netif_ip6_addr_set_state(netif, i, IP6_ADDR_DEPRECATED); |
||
1086 | } else if (!ip6_addr_life_isinfinite(life)) { |
||
1087 | life -= ND6_TMR_INTERVAL / 1000; |
||
1088 | netif_ip6_addr_set_pref_life(netif, i, life); |
||
1089 | } |
||
1090 | } |
||
1091 | } |
||
1092 | /* The address state may now have changed, so reobtain it next. */ |
||
1093 | #endif /* LWIP_IPV6_ADDRESS_LIFETIMES */ |
||
1094 | /* Step 2: update DAD state. */ |
||
1095 | addr_state = netif_ip6_addr_state(netif, i); |
||
1096 | if (ip6_addr_istentative(addr_state)) { |
||
1097 | if ((addr_state & IP6_ADDR_TENTATIVE_COUNT_MASK) >= LWIP_IPV6_DUP_DETECT_ATTEMPTS) { |
||
1098 | /* No NA received in response. Mark address as valid. For dynamic |
||
1099 | * addresses with an expired preferred lifetime, the state is set to |
||
1100 | * deprecated right away. That should almost never happen, though. */ |
||
1101 | addr_state = IP6_ADDR_PREFERRED; |
||
1102 | #if LWIP_IPV6_ADDRESS_LIFETIMES |
||
1103 | if (!netif_ip6_addr_isstatic(netif, i) && |
||
1104 | netif_ip6_addr_pref_life(netif, i) == 0) { |
||
1105 | addr_state = IP6_ADDR_DEPRECATED; |
||
1106 | } |
||
1107 | #endif /* LWIP_IPV6_ADDRESS_LIFETIMES */ |
||
1108 | netif_ip6_addr_set_state(netif, i, addr_state); |
||
1109 | } else if (netif_is_up(netif) && netif_is_link_up(netif)) { |
||
1110 | /* tentative: set next state by increasing by one */ |
||
1111 | netif_ip6_addr_set_state(netif, i, addr_state + 1); |
||
1112 | /* Send a NS for this address. Use the unspecified address as source |
||
1113 | * address in all cases (RFC 4862 Sec. 5.4.2), not in the least |
||
1114 | * because as it is, we only consider multicast replies for DAD. */ |
||
1115 | nd6_send_ns(netif, netif_ip6_addr(netif, i), |
||
1116 | ND6_SEND_FLAG_MULTICAST_DEST | ND6_SEND_FLAG_ANY_SRC); |
||
1117 | } |
||
1118 | } |
||
1119 | } |
||
1120 | } |
||
1121 | |||
1122 | #if LWIP_IPV6_SEND_ROUTER_SOLICIT |
||
1123 | /* Send router solicitation messages, if necessary. */ |
||
1124 | NETIF_FOREACH(netif) { |
||
1125 | if ((netif->rs_count > 0) && netif_is_up(netif) && |
||
1126 | netif_is_link_up(netif) && |
||
1127 | !ip6_addr_isinvalid(netif_ip6_addr_state(netif, 0)) && |
||
1128 | !ip6_addr_isduplicated(netif_ip6_addr_state(netif, 0))) { |
||
1129 | if (nd6_send_rs(netif) == ERR_OK) { |
||
1130 | netif->rs_count--; |
||
1131 | } |
||
1132 | } |
||
1133 | } |
||
1134 | #endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */ |
||
1135 | |||
1136 | } |
||
1137 | |||
1138 | /** Send a neighbor solicitation message for a specific neighbor cache entry |
||
1139 | * |
||
1140 | * @param entry the neightbor cache entry for wich to send the message |
||
1141 | * @param flags one of ND6_SEND_FLAG_* |
||
1142 | */ |
||
1143 | static void |
||
1144 | nd6_send_neighbor_cache_probe(struct nd6_neighbor_cache_entry *entry, u8_t flags) |
||
1145 | { |
||
1146 | nd6_send_ns(entry->netif, &entry->next_hop_address, flags); |
||
1147 | } |
||
1148 | |||
1149 | /** |
||
1150 | * Send a neighbor solicitation message |
||
1151 | * |
||
1152 | * @param netif the netif on which to send the message |
||
1153 | * @param target_addr the IPv6 target address for the ND message |
||
1154 | * @param flags one of ND6_SEND_FLAG_* |
||
1155 | */ |
||
1156 | static void |
||
1157 | nd6_send_ns(struct netif *netif, const ip6_addr_t *target_addr, u8_t flags) |
||
1158 | { |
||
1159 | struct ns_header *ns_hdr; |
||
1160 | struct pbuf *p; |
||
1161 | const ip6_addr_t *src_addr; |
||
1162 | u16_t lladdr_opt_len; |
||
1163 | |||
1164 | LWIP_ASSERT("target address is required", target_addr != NULL); |
||
1165 | |||
1166 | if (!(flags & ND6_SEND_FLAG_ANY_SRC) && |
||
1167 | ip6_addr_isvalid(netif_ip6_addr_state(netif,0))) { |
||
1168 | /* Use link-local address as source address. */ |
||
1169 | src_addr = netif_ip6_addr(netif, 0); |
||
1170 | /* calculate option length (in 8-byte-blocks) */ |
||
1171 | lladdr_opt_len = ((netif->hwaddr_len + 2) + 7) >> 3; |
||
1172 | } else { |
||
1173 | src_addr = IP6_ADDR_ANY6; |
||
1174 | /* Option "MUST NOT be included when the source IP address is the unspecified address." */ |
||
1175 | lladdr_opt_len = 0; |
||
1176 | } |
||
1177 | |||
1178 | /* Allocate a packet. */ |
||
1179 | p = pbuf_alloc(PBUF_IP, sizeof(struct ns_header) + (lladdr_opt_len << 3), PBUF_RAM); |
||
1180 | if (p == NULL) { |
||
1181 | ND6_STATS_INC(nd6.memerr); |
||
1182 | return; |
||
1183 | } |
||
1184 | |||
1185 | /* Set fields. */ |
||
1186 | ns_hdr = (struct ns_header *)p->payload; |
||
1187 | |||
1188 | ns_hdr->type = ICMP6_TYPE_NS; |
||
1189 | ns_hdr->code = 0; |
||
1190 | ns_hdr->chksum = 0; |
||
1191 | ns_hdr->reserved = 0; |
||
1192 | ip6_addr_copy_to_packed(ns_hdr->target_address, *target_addr); |
||
1193 | |||
1194 | if (lladdr_opt_len != 0) { |
||
1195 | struct lladdr_option *lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct ns_header)); |
||
1196 | lladdr_opt->type = ND6_OPTION_TYPE_SOURCE_LLADDR; |
||
1197 | lladdr_opt->length = (u8_t)lladdr_opt_len; |
||
1198 | SMEMCPY(lladdr_opt->addr, netif->hwaddr, netif->hwaddr_len); |
||
1199 | } |
||
1200 | |||
1201 | /* Generate the solicited node address for the target address. */ |
||
1202 | if (flags & ND6_SEND_FLAG_MULTICAST_DEST) { |
||
1203 | ip6_addr_set_solicitednode(&multicast_address, target_addr->addr[3]); |
||
1204 | ip6_addr_assign_zone(&multicast_address, IP6_MULTICAST, netif); |
||
1205 | target_addr = &multicast_address; |
||
1206 | } |
||
1207 | |||
1208 | #if CHECKSUM_GEN_ICMP6 |
||
1209 | IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_ICMP6) { |
||
1210 | ns_hdr->chksum = ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->len, src_addr, |
||
1211 | target_addr); |
||
1212 | } |
||
1213 | #endif /* CHECKSUM_GEN_ICMP6 */ |
||
1214 | |||
1215 | /* Send the packet out. */ |
||
1216 | ND6_STATS_INC(nd6.xmit); |
||
1217 | ip6_output_if(p, (src_addr == IP6_ADDR_ANY6) ? NULL : src_addr, target_addr, |
||
1218 | ND6_HOPLIM, 0, IP6_NEXTH_ICMP6, netif); |
||
1219 | pbuf_free(p); |
||
1220 | } |
||
1221 | |||
1222 | /** |
||
1223 | * Send a neighbor advertisement message |
||
1224 | * |
||
1225 | * @param netif the netif on which to send the message |
||
1226 | * @param target_addr the IPv6 target address for the ND message |
||
1227 | * @param flags one of ND6_SEND_FLAG_* |
||
1228 | */ |
||
1229 | static void |
||
1230 | nd6_send_na(struct netif *netif, const ip6_addr_t *target_addr, u8_t flags) |
||
1231 | { |
||
1232 | struct na_header *na_hdr; |
||
1233 | struct lladdr_option *lladdr_opt; |
||
1234 | struct pbuf *p; |
||
1235 | const ip6_addr_t *src_addr; |
||
1236 | const ip6_addr_t *dest_addr; |
||
1237 | u16_t lladdr_opt_len; |
||
1238 | |||
1239 | LWIP_ASSERT("target address is required", target_addr != NULL); |
||
1240 | |||
1241 | /* Use link-local address as source address. */ |
||
1242 | /* src_addr = netif_ip6_addr(netif, 0); */ |
||
1243 | /* Use target address as source address. */ |
||
1244 | src_addr = target_addr; |
||
1245 | |||
1246 | /* Allocate a packet. */ |
||
1247 | lladdr_opt_len = ((netif->hwaddr_len + 2) >> 3) + (((netif->hwaddr_len + 2) & 0x07) ? 1 : 0); |
||
1248 | p = pbuf_alloc(PBUF_IP, sizeof(struct na_header) + (lladdr_opt_len << 3), PBUF_RAM); |
||
1249 | if (p == NULL) { |
||
1250 | ND6_STATS_INC(nd6.memerr); |
||
1251 | return; |
||
1252 | } |
||
1253 | |||
1254 | /* Set fields. */ |
||
1255 | na_hdr = (struct na_header *)p->payload; |
||
1256 | lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct na_header)); |
||
1257 | |||
1258 | na_hdr->type = ICMP6_TYPE_NA; |
||
1259 | na_hdr->code = 0; |
||
1260 | na_hdr->chksum = 0; |
||
1261 | na_hdr->flags = flags & 0xf0; |
||
1262 | na_hdr->reserved[0] = 0; |
||
1263 | na_hdr->reserved[1] = 0; |
||
1264 | na_hdr->reserved[2] = 0; |
||
1265 | ip6_addr_copy_to_packed(na_hdr->target_address, *target_addr); |
||
1266 | |||
1267 | lladdr_opt->type = ND6_OPTION_TYPE_TARGET_LLADDR; |
||
1268 | lladdr_opt->length = (u8_t)lladdr_opt_len; |
||
1269 | SMEMCPY(lladdr_opt->addr, netif->hwaddr, netif->hwaddr_len); |
||
1270 | |||
1271 | /* Generate the solicited node address for the target address. */ |
||
1272 | if (flags & ND6_SEND_FLAG_MULTICAST_DEST) { |
||
1273 | ip6_addr_set_solicitednode(&multicast_address, target_addr->addr[3]); |
||
1274 | ip6_addr_assign_zone(&multicast_address, IP6_MULTICAST, netif); |
||
1275 | dest_addr = &multicast_address; |
||
1276 | } else if (flags & ND6_SEND_FLAG_ALLNODES_DEST) { |
||
1277 | ip6_addr_set_allnodes_linklocal(&multicast_address); |
||
1278 | ip6_addr_assign_zone(&multicast_address, IP6_MULTICAST, netif); |
||
1279 | dest_addr = &multicast_address; |
||
1280 | } else { |
||
1281 | dest_addr = ip6_current_src_addr(); |
||
1282 | } |
||
1283 | |||
1284 | #if CHECKSUM_GEN_ICMP6 |
||
1285 | IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_ICMP6) { |
||
1286 | na_hdr->chksum = ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->len, src_addr, |
||
1287 | dest_addr); |
||
1288 | } |
||
1289 | #endif /* CHECKSUM_GEN_ICMP6 */ |
||
1290 | |||
1291 | /* Send the packet out. */ |
||
1292 | ND6_STATS_INC(nd6.xmit); |
||
1293 | ip6_output_if(p, src_addr, dest_addr, |
||
1294 | ND6_HOPLIM, 0, IP6_NEXTH_ICMP6, netif); |
||
1295 | pbuf_free(p); |
||
1296 | } |
||
1297 | |||
1298 | #if LWIP_IPV6_SEND_ROUTER_SOLICIT |
||
1299 | /** |
||
1300 | * Send a router solicitation message |
||
1301 | * |
||
1302 | * @param netif the netif on which to send the message |
||
1303 | */ |
||
1304 | static err_t |
||
1305 | nd6_send_rs(struct netif *netif) |
||
1306 | { |
||
1307 | struct rs_header *rs_hdr; |
||
1308 | struct lladdr_option *lladdr_opt; |
||
1309 | struct pbuf *p; |
||
1310 | const ip6_addr_t *src_addr; |
||
1311 | err_t err; |
||
1312 | u16_t lladdr_opt_len = 0; |
||
1313 | |||
1314 | /* Link-local source address, or unspecified address? */ |
||
1315 | if (ip6_addr_isvalid(netif_ip6_addr_state(netif, 0))) { |
||
1316 | src_addr = netif_ip6_addr(netif, 0); |
||
1317 | } else { |
||
1318 | src_addr = IP6_ADDR_ANY6; |
||
1319 | } |
||
1320 | |||
1321 | /* Generate the all routers target address. */ |
||
1322 | ip6_addr_set_allrouters_linklocal(&multicast_address); |
||
1323 | ip6_addr_assign_zone(&multicast_address, IP6_MULTICAST, netif); |
||
1324 | |||
1325 | /* Allocate a packet. */ |
||
1326 | if (src_addr != IP6_ADDR_ANY6) { |
||
1327 | lladdr_opt_len = ((netif->hwaddr_len + 2) >> 3) + (((netif->hwaddr_len + 2) & 0x07) ? 1 : 0); |
||
1328 | } |
||
1329 | p = pbuf_alloc(PBUF_IP, sizeof(struct rs_header) + (lladdr_opt_len << 3), PBUF_RAM); |
||
1330 | if (p == NULL) { |
||
1331 | ND6_STATS_INC(nd6.memerr); |
||
1332 | return ERR_BUF; |
||
1333 | } |
||
1334 | |||
1335 | /* Set fields. */ |
||
1336 | rs_hdr = (struct rs_header *)p->payload; |
||
1337 | |||
1338 | rs_hdr->type = ICMP6_TYPE_RS; |
||
1339 | rs_hdr->code = 0; |
||
1340 | rs_hdr->chksum = 0; |
||
1341 | rs_hdr->reserved = 0; |
||
1342 | |||
1343 | if (src_addr != IP6_ADDR_ANY6) { |
||
1344 | /* Include our hw address. */ |
||
1345 | lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct rs_header)); |
||
1346 | lladdr_opt->type = ND6_OPTION_TYPE_SOURCE_LLADDR; |
||
1347 | lladdr_opt->length = (u8_t)lladdr_opt_len; |
||
1348 | SMEMCPY(lladdr_opt->addr, netif->hwaddr, netif->hwaddr_len); |
||
1349 | } |
||
1350 | |||
1351 | #if CHECKSUM_GEN_ICMP6 |
||
1352 | IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_ICMP6) { |
||
1353 | rs_hdr->chksum = ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->len, src_addr, |
||
1354 | &multicast_address); |
||
1355 | } |
||
1356 | #endif /* CHECKSUM_GEN_ICMP6 */ |
||
1357 | |||
1358 | /* Send the packet out. */ |
||
1359 | ND6_STATS_INC(nd6.xmit); |
||
1360 | |||
1361 | err = ip6_output_if(p, (src_addr == IP6_ADDR_ANY6) ? NULL : src_addr, &multicast_address, |
||
1362 | ND6_HOPLIM, 0, IP6_NEXTH_ICMP6, netif); |
||
1363 | pbuf_free(p); |
||
1364 | |||
1365 | return err; |
||
1366 | } |
||
1367 | #endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */ |
||
1368 | |||
1369 | /** |
||
1370 | * Search for a neighbor cache entry |
||
1371 | * |
||
1372 | * @param ip6addr the IPv6 address of the neighbor |
||
1373 | * @return The neighbor cache entry index that matched, -1 if no |
||
1374 | * entry is found |
||
1375 | */ |
||
1376 | static s8_t |
||
1377 | nd6_find_neighbor_cache_entry(const ip6_addr_t *ip6addr) |
||
1378 | { |
||
1379 | s8_t i; |
||
1380 | for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { |
||
1381 | if (ip6_addr_cmp(ip6addr, &(neighbor_cache[i].next_hop_address))) { |
||
1382 | return i; |
||
1383 | } |
||
1384 | } |
||
1385 | return -1; |
||
1386 | } |
||
1387 | |||
1388 | /** |
||
1389 | * Create a new neighbor cache entry. |
||
1390 | * |
||
1391 | * If no unused entry is found, will try to recycle an old entry |
||
1392 | * according to ad-hoc "age" heuristic. |
||
1393 | * |
||
1394 | * @return The neighbor cache entry index that was created, -1 if no |
||
1395 | * entry could be created |
||
1396 | */ |
||
1397 | static s8_t |
||
1398 | nd6_new_neighbor_cache_entry(void) |
||
1399 | { |
||
1400 | s8_t i; |
||
1401 | s8_t j; |
||
1402 | u32_t time; |
||
1403 | |||
1404 | |||
1405 | /* First, try to find an empty entry. */ |
||
1406 | for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { |
||
1407 | if (neighbor_cache[i].state == ND6_NO_ENTRY) { |
||
1408 | return i; |
||
1409 | } |
||
1410 | } |
||
1411 | |||
1412 | /* We need to recycle an entry. in general, do not recycle if it is a router. */ |
||
1413 | |||
1414 | /* Next, try to find a Stale entry. */ |
||
1415 | for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { |
||
1416 | if ((neighbor_cache[i].state == ND6_STALE) && |
||
1417 | (!neighbor_cache[i].isrouter)) { |
||
1418 | nd6_free_neighbor_cache_entry(i); |
||
1419 | return i; |
||
1420 | } |
||
1421 | } |
||
1422 | |||
1423 | /* Next, try to find a Probe entry. */ |
||
1424 | for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { |
||
1425 | if ((neighbor_cache[i].state == ND6_PROBE) && |
||
1426 | (!neighbor_cache[i].isrouter)) { |
||
1427 | nd6_free_neighbor_cache_entry(i); |
||
1428 | return i; |
||
1429 | } |
||
1430 | } |
||
1431 | |||
1432 | /* Next, try to find a Delayed entry. */ |
||
1433 | for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { |
||
1434 | if ((neighbor_cache[i].state == ND6_DELAY) && |
||
1435 | (!neighbor_cache[i].isrouter)) { |
||
1436 | nd6_free_neighbor_cache_entry(i); |
||
1437 | return i; |
||
1438 | } |
||
1439 | } |
||
1440 | |||
1441 | /* Next, try to find the oldest reachable entry. */ |
||
1442 | time = 0xfffffffful; |
||
1443 | j = -1; |
||
1444 | for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { |
||
1445 | if ((neighbor_cache[i].state == ND6_REACHABLE) && |
||
1446 | (!neighbor_cache[i].isrouter)) { |
||
1447 | if (neighbor_cache[i].counter.reachable_time < time) { |
||
1448 | j = i; |
||
1449 | time = neighbor_cache[i].counter.reachable_time; |
||
1450 | } |
||
1451 | } |
||
1452 | } |
||
1453 | if (j >= 0) { |
||
1454 | nd6_free_neighbor_cache_entry(j); |
||
1455 | return j; |
||
1456 | } |
||
1457 | |||
1458 | /* Next, find oldest incomplete entry without queued packets. */ |
||
1459 | time = 0; |
||
1460 | j = -1; |
||
1461 | for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { |
||
1462 | if ( |
||
1463 | (neighbor_cache[i].q == NULL) && |
||
1464 | (neighbor_cache[i].state == ND6_INCOMPLETE) && |
||
1465 | (!neighbor_cache[i].isrouter)) { |
||
1466 | if (neighbor_cache[i].counter.probes_sent >= time) { |
||
1467 | j = i; |
||
1468 | time = neighbor_cache[i].counter.probes_sent; |
||
1469 | } |
||
1470 | } |
||
1471 | } |
||
1472 | if (j >= 0) { |
||
1473 | nd6_free_neighbor_cache_entry(j); |
||
1474 | return j; |
||
1475 | } |
||
1476 | |||
1477 | /* Next, find oldest incomplete entry with queued packets. */ |
||
1478 | time = 0; |
||
1479 | j = -1; |
||
1480 | for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { |
||
1481 | if ((neighbor_cache[i].state == ND6_INCOMPLETE) && |
||
1482 | (!neighbor_cache[i].isrouter)) { |
||
1483 | if (neighbor_cache[i].counter.probes_sent >= time) { |
||
1484 | j = i; |
||
1485 | time = neighbor_cache[i].counter.probes_sent; |
||
1486 | } |
||
1487 | } |
||
1488 | } |
||
1489 | if (j >= 0) { |
||
1490 | nd6_free_neighbor_cache_entry(j); |
||
1491 | return j; |
||
1492 | } |
||
1493 | |||
1494 | /* No more entries to try. */ |
||
1495 | return -1; |
||
1496 | } |
||
1497 | |||
1498 | /** |
||
1499 | * Will free any resources associated with a neighbor cache |
||
1500 | * entry, and will mark it as unused. |
||
1501 | * |
||
1502 | * @param i the neighbor cache entry index to free |
||
1503 | */ |
||
1504 | static void |
||
1505 | nd6_free_neighbor_cache_entry(s8_t i) |
||
1506 | { |
||
1507 | if ((i < 0) || (i >= LWIP_ND6_NUM_NEIGHBORS)) { |
||
1508 | return; |
||
1509 | } |
||
1510 | if (neighbor_cache[i].isrouter) { |
||
1511 | /* isrouter needs to be cleared before deleting a neighbor cache entry */ |
||
1512 | return; |
||
1513 | } |
||
1514 | |||
1515 | /* Free any queued packets. */ |
||
1516 | if (neighbor_cache[i].q != NULL) { |
||
1517 | nd6_free_q(neighbor_cache[i].q); |
||
1518 | neighbor_cache[i].q = NULL; |
||
1519 | } |
||
1520 | |||
1521 | neighbor_cache[i].state = ND6_NO_ENTRY; |
||
1522 | neighbor_cache[i].isrouter = 0; |
||
1523 | neighbor_cache[i].netif = NULL; |
||
1524 | neighbor_cache[i].counter.reachable_time = 0; |
||
1525 | ip6_addr_set_zero(&(neighbor_cache[i].next_hop_address)); |
||
1526 | } |
||
1527 | |||
1528 | /** |
||
1529 | * Search for a destination cache entry |
||
1530 | * |
||
1531 | * @param ip6addr the IPv6 address of the destination |
||
1532 | * @return The destination cache entry index that matched, -1 if no |
||
1533 | * entry is found |
||
1534 | */ |
||
1535 | static s8_t |
||
1536 | nd6_find_destination_cache_entry(const ip6_addr_t *ip6addr) |
||
1537 | { |
||
1538 | s8_t i; |
||
1539 | |||
1540 | IP6_ADDR_ZONECHECK(ip6addr); |
||
1541 | |||
1542 | for (i = 0; i < LWIP_ND6_NUM_DESTINATIONS; i++) { |
||
1543 | if (ip6_addr_cmp(ip6addr, &(destination_cache[i].destination_addr))) { |
||
1544 | return i; |
||
1545 | } |
||
1546 | } |
||
1547 | return -1; |
||
1548 | } |
||
1549 | |||
1550 | /** |
||
1551 | * Create a new destination cache entry. If no unused entry is found, |
||
1552 | * will recycle oldest entry. |
||
1553 | * |
||
1554 | * @return The destination cache entry index that was created, -1 if no |
||
1555 | * entry was created |
||
1556 | */ |
||
1557 | static s8_t |
||
1558 | nd6_new_destination_cache_entry(void) |
||
1559 | { |
||
1560 | s8_t i, j; |
||
1561 | u32_t age; |
||
1562 | |||
1563 | /* Find an empty entry. */ |
||
1564 | for (i = 0; i < LWIP_ND6_NUM_DESTINATIONS; i++) { |
||
1565 | if (ip6_addr_isany(&(destination_cache[i].destination_addr))) { |
||
1566 | return i; |
||
1567 | } |
||
1568 | } |
||
1569 | |||
1570 | /* Find oldest entry. */ |
||
1571 | age = 0; |
||
1572 | j = LWIP_ND6_NUM_DESTINATIONS - 1; |
||
1573 | for (i = 0; i < LWIP_ND6_NUM_DESTINATIONS; i++) { |
||
1574 | if (destination_cache[i].age > age) { |
||
1575 | j = i; |
||
1576 | } |
||
1577 | } |
||
1578 | |||
1579 | return j; |
||
1580 | } |
||
1581 | |||
1582 | /** |
||
1583 | * Clear the destination cache. |
||
1584 | * |
||
1585 | * This operation may be necessary for consistency in the light of changing |
||
1586 | * local addresses and/or use of the gateway hook. |
||
1587 | */ |
||
1588 | void |
||
1589 | nd6_clear_destination_cache(void) |
||
1590 | { |
||
1591 | int i; |
||
1592 | |||
1593 | for (i = 0; i < LWIP_ND6_NUM_DESTINATIONS; i++) { |
||
1594 | ip6_addr_set_any(&destination_cache[i].destination_addr); |
||
1595 | } |
||
1596 | } |
||
1597 | |||
1598 | /** |
||
1599 | * Determine whether an address matches an on-link prefix or the subnet of a |
||
1600 | * statically assigned address. |
||
1601 | * |
||
1602 | * @param ip6addr the IPv6 address to match |
||
1603 | * @return 1 if the address is on-link, 0 otherwise |
||
1604 | */ |
||
1605 | static s8_t |
||
1606 | nd6_is_prefix_in_netif(const ip6_addr_t *ip6addr, struct netif *netif) |
||
1607 | { |
||
1608 | s8_t i; |
||
1609 | |||
1610 | /* Check to see if the address matches an on-link prefix. */ |
||
1611 | for (i = 0; i < LWIP_ND6_NUM_PREFIXES; i++) { |
||
1612 | if ((prefix_list[i].netif == netif) && |
||
1613 | (prefix_list[i].invalidation_timer > 0) && |
||
1614 | ip6_addr_netcmp(ip6addr, &(prefix_list[i].prefix))) { |
||
1615 | return 1; |
||
1616 | } |
||
1617 | } |
||
1618 | /* Check to see if address prefix matches a manually configured (= static) |
||
1619 | * address. Static addresses have an implied /64 subnet assignment. Dynamic |
||
1620 | * addresses (from autoconfiguration) have no implied subnet assignment, and |
||
1621 | * are thus effectively /128 assignments. See RFC 5942 for more on this. */ |
||
1622 | for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { |
||
1623 | if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && |
||
1624 | netif_ip6_addr_isstatic(netif, i) && |
||
1625 | ip6_addr_netcmp(ip6addr, netif_ip6_addr(netif, i))) { |
||
1626 | return 1; |
||
1627 | } |
||
1628 | } |
||
1629 | return 0; |
||
1630 | } |
||
1631 | |||
1632 | /** |
||
1633 | * Select a default router for a destination. |
||
1634 | * |
||
1635 | * This function is used both for routing and for finding a next-hop target for |
||
1636 | * a packet. In the former case, the given netif is NULL, and the returned |
||
1637 | * router entry must be for a netif suitable for sending packets (up, link up). |
||
1638 | * In the latter case, the given netif is not NULL and restricts router choice. |
||
1639 | * |
||
1640 | * @param ip6addr the destination address |
||
1641 | * @param netif the netif for the outgoing packet, if known |
||
1642 | * @return the default router entry index, or -1 if no suitable |
||
1643 | * router is found |
||
1644 | */ |
||
1645 | static s8_t |
||
1646 | nd6_select_router(const ip6_addr_t *ip6addr, struct netif *netif) |
||
1647 | { |
||
1648 | struct netif *router_netif; |
||
1649 | s8_t i, j, valid_router; |
||
1650 | static s8_t last_router; |
||
1651 | |||
1652 | LWIP_UNUSED_ARG(ip6addr); /* @todo match preferred routes!! (must implement ND6_OPTION_TYPE_ROUTE_INFO) */ |
||
1653 | |||
1654 | /* @todo: implement default router preference */ |
||
1655 | |||
1656 | /* Look for valid routers. A reachable router is preferred. */ |
||
1657 | valid_router = -1; |
||
1658 | for (i = 0; i < LWIP_ND6_NUM_ROUTERS; i++) { |
||
1659 | /* Is the router netif both set and apppropriate? */ |
||
1660 | if (default_router_list[i].neighbor_entry != NULL) { |
||
1661 | router_netif = default_router_list[i].neighbor_entry->netif; |
||
1662 | if ((router_netif != NULL) && (netif != NULL ? netif == router_netif : |
||
1663 | (netif_is_up(router_netif) && netif_is_link_up(router_netif)))) { |
||
1664 | /* Is the router valid, i.e., reachable or probably reachable as per |
||
1665 | * RFC 4861 Sec. 6.3.6? Note that we will never return a router that |
||
1666 | * has no neighbor cache entry, due to the netif association tests. */ |
||
1667 | if (default_router_list[i].neighbor_entry->state != ND6_INCOMPLETE) { |
||
1668 | /* Is the router known to be reachable? */ |
||
1669 | if (default_router_list[i].neighbor_entry->state == ND6_REACHABLE) { |
||
1670 | return i; /* valid and reachable - done! */ |
||
1671 | } else if (valid_router < 0) { |
||
1672 | valid_router = i; /* valid but not known to be reachable */ |
||
1673 | } |
||
1674 | } |
||
1675 | } |
||
1676 | } |
||
1677 | } |
||
1678 | if (valid_router >= 0) { |
||
1679 | return valid_router; |
||
1680 | } |
||
1681 | |||
1682 | /* Look for any router for which we have any information at all. */ |
||
1683 | /* last_router is used for round-robin selection of incomplete routers, as |
||
1684 | * recommended in RFC 4861 Sec. 6.3.6 point (2). Advance only when picking a |
||
1685 | * route, to select the same router as next-hop target in the common case. */ |
||
1686 | if ((netif == NULL) && (++last_router >= LWIP_ND6_NUM_ROUTERS)) { |
||
1687 | last_router = 0; |
||
1688 | } |
||
1689 | i = last_router; |
||
1690 | for (j = 0; j < LWIP_ND6_NUM_ROUTERS; j++) { |
||
1691 | if (default_router_list[i].neighbor_entry != NULL) { |
||
1692 | router_netif = default_router_list[i].neighbor_entry->netif; |
||
1693 | if ((router_netif != NULL) && (netif != NULL ? netif == router_netif : |
||
1694 | (netif_is_up(router_netif) && netif_is_link_up(router_netif)))) { |
||
1695 | return i; |
||
1696 | } |
||
1697 | } |
||
1698 | if (++i >= LWIP_ND6_NUM_ROUTERS) { |
||
1699 | i = 0; |
||
1700 | } |
||
1701 | } |
||
1702 | |||
1703 | /* no suitable router found. */ |
||
1704 | return -1; |
||
1705 | } |
||
1706 | |||
1707 | /** |
||
1708 | * Find a router-announced route to the given destination. This route may be |
||
1709 | * based on an on-link prefix or a default router. |
||
1710 | * |
||
1711 | * If a suitable route is found, the returned netif is guaranteed to be in a |
||
1712 | * suitable state (up, link up) to be used for packet transmission. |
||
1713 | * |
||
1714 | * @param ip6addr the destination IPv6 address |
||
1715 | * @return the netif to use for the destination, or NULL if none found |
||
1716 | */ |
||
1717 | struct netif * |
||
1718 | nd6_find_route(const ip6_addr_t *ip6addr) |
||
1719 | { |
||
1720 | struct netif *netif; |
||
1721 | s8_t i; |
||
1722 | |||
1723 | /* @todo decide if it makes sense to check the destination cache first */ |
||
1724 | |||
1725 | /* Check if there is a matching on-link prefix. There may be multiple |
||
1726 | * matches. Pick the first one that is associated with a suitable netif. */ |
||
1727 | for (i = 0; i < LWIP_ND6_NUM_PREFIXES; ++i) { |
||
1728 | netif = prefix_list[i].netif; |
||
1729 | if ((netif != NULL) && ip6_addr_netcmp(&prefix_list[i].prefix, ip6addr) && |
||
1730 | netif_is_up(netif) && netif_is_link_up(netif)) { |
||
1731 | return netif; |
||
1732 | } |
||
1733 | } |
||
1734 | |||
1735 | /* No on-link prefix match. Find a router that can forward the packet. */ |
||
1736 | i = nd6_select_router(ip6addr, NULL); |
||
1737 | if (i >= 0) { |
||
1738 | LWIP_ASSERT("selected router must have a neighbor entry", |
||
1739 | default_router_list[i].neighbor_entry != NULL); |
||
1740 | return default_router_list[i].neighbor_entry->netif; |
||
1741 | } |
||
1742 | |||
1743 | return NULL; |
||
1744 | } |
||
1745 | |||
1746 | /** |
||
1747 | * Find an entry for a default router. |
||
1748 | * |
||
1749 | * @param router_addr the IPv6 address of the router |
||
1750 | * @param netif the netif on which the router is found, if known |
||
1751 | * @return the index of the router entry, or -1 if not found |
||
1752 | */ |
||
1753 | static s8_t |
||
1754 | nd6_get_router(const ip6_addr_t *router_addr, struct netif *netif) |
||
1755 | { |
||
1756 | s8_t i; |
||
1757 | |||
1758 | IP6_ADDR_ZONECHECK_NETIF(router_addr, netif); |
||
1759 | |||
1760 | /* Look for router. */ |
||
1761 | for (i = 0; i < LWIP_ND6_NUM_ROUTERS; i++) { |
||
1762 | if ((default_router_list[i].neighbor_entry != NULL) && |
||
1763 | ((netif != NULL) ? netif == default_router_list[i].neighbor_entry->netif : 1) && |
||
1764 | ip6_addr_cmp(router_addr, &(default_router_list[i].neighbor_entry->next_hop_address))) { |
||
1765 | return i; |
||
1766 | } |
||
1767 | } |
||
1768 | |||
1769 | /* router not found. */ |
||
1770 | return -1; |
||
1771 | } |
||
1772 | |||
1773 | /** |
||
1774 | * Create a new entry for a default router. |
||
1775 | * |
||
1776 | * @param router_addr the IPv6 address of the router |
||
1777 | * @param netif the netif on which the router is connected, if known |
||
1778 | * @return the index on the router table, or -1 if could not be created |
||
1779 | */ |
||
1780 | static s8_t |
||
1781 | nd6_new_router(const ip6_addr_t *router_addr, struct netif *netif) |
||
1782 | { |
||
1783 | s8_t router_index; |
||
1784 | s8_t free_router_index; |
||
1785 | s8_t neighbor_index; |
||
1786 | |||
1787 | IP6_ADDR_ZONECHECK_NETIF(router_addr, netif); |
||
1788 | |||
1789 | /* Do we have a neighbor entry for this router? */ |
||
1790 | neighbor_index = nd6_find_neighbor_cache_entry(router_addr); |
||
1791 | if (neighbor_index < 0) { |
||
1792 | /* Create a neighbor entry for this router. */ |
||
1793 | neighbor_index = nd6_new_neighbor_cache_entry(); |
||
1794 | if (neighbor_index < 0) { |
||
1795 | /* Could not create neighbor entry for this router. */ |
||
1796 | return -1; |
||
1797 | } |
||
1798 | ip6_addr_set(&(neighbor_cache[neighbor_index].next_hop_address), router_addr); |
||
1799 | neighbor_cache[neighbor_index].netif = netif; |
||
1800 | neighbor_cache[neighbor_index].q = NULL; |
||
1801 | neighbor_cache[neighbor_index].state = ND6_INCOMPLETE; |
||
1802 | neighbor_cache[neighbor_index].counter.probes_sent = 1; |
||
1803 | nd6_send_neighbor_cache_probe(&neighbor_cache[neighbor_index], ND6_SEND_FLAG_MULTICAST_DEST); |
||
1804 | } |
||
1805 | |||
1806 | /* Mark neighbor as router. */ |
||
1807 | neighbor_cache[neighbor_index].isrouter = 1; |
||
1808 | |||
1809 | /* Look for empty entry. */ |
||
1810 | free_router_index = LWIP_ND6_NUM_ROUTERS; |
||
1811 | for (router_index = LWIP_ND6_NUM_ROUTERS - 1; router_index >= 0; router_index--) { |
||
1812 | /* check if router already exists (this is a special case for 2 netifs on the same subnet |
||
1813 | - e.g. wifi and cable) */ |
||
1814 | if(default_router_list[router_index].neighbor_entry == &(neighbor_cache[neighbor_index])){ |
||
1815 | return router_index; |
||
1816 | } |
||
1817 | if (default_router_list[router_index].neighbor_entry == NULL) { |
||
1818 | /* remember lowest free index to create a new entry */ |
||
1819 | free_router_index = router_index; |
||
1820 | } |
||
1821 | } |
||
1822 | if (free_router_index < LWIP_ND6_NUM_ROUTERS) { |
||
1823 | default_router_list[free_router_index].neighbor_entry = &(neighbor_cache[neighbor_index]); |
||
1824 | return free_router_index; |
||
1825 | } |
||
1826 | |||
1827 | /* Could not create a router entry. */ |
||
1828 | |||
1829 | /* Mark neighbor entry as not-router. Entry might be useful as neighbor still. */ |
||
1830 | neighbor_cache[neighbor_index].isrouter = 0; |
||
1831 | |||
1832 | /* router not found. */ |
||
1833 | return -1; |
||
1834 | } |
||
1835 | |||
1836 | /** |
||
1837 | * Find the cached entry for an on-link prefix. |
||
1838 | * |
||
1839 | * @param prefix the IPv6 prefix that is on-link |
||
1840 | * @param netif the netif on which the prefix is on-link |
||
1841 | * @return the index on the prefix table, or -1 if not found |
||
1842 | */ |
||
1843 | static s8_t |
||
1844 | nd6_get_onlink_prefix(const ip6_addr_t *prefix, struct netif *netif) |
||
1845 | { |
||
1846 | s8_t i; |
||
1847 | |||
1848 | /* Look for prefix in list. */ |
||
1849 | for (i = 0; i < LWIP_ND6_NUM_PREFIXES; ++i) { |
||
1850 | if ((ip6_addr_netcmp(&(prefix_list[i].prefix), prefix)) && |
||
1851 | (prefix_list[i].netif == netif)) { |
||
1852 | return i; |
||
1853 | } |
||
1854 | } |
||
1855 | |||
1856 | /* Entry not available. */ |
||
1857 | return -1; |
||
1858 | } |
||
1859 | |||
1860 | /** |
||
1861 | * Creates a new entry for an on-link prefix. |
||
1862 | * |
||
1863 | * @param prefix the IPv6 prefix that is on-link |
||
1864 | * @param netif the netif on which the prefix is on-link |
||
1865 | * @return the index on the prefix table, or -1 if not created |
||
1866 | */ |
||
1867 | static s8_t |
||
1868 | nd6_new_onlink_prefix(const ip6_addr_t *prefix, struct netif *netif) |
||
1869 | { |
||
1870 | s8_t i; |
||
1871 | |||
1872 | /* Create new entry. */ |
||
1873 | for (i = 0; i < LWIP_ND6_NUM_PREFIXES; ++i) { |
||
1874 | if ((prefix_list[i].netif == NULL) || |
||
1875 | (prefix_list[i].invalidation_timer == 0)) { |
||
1876 | /* Found empty prefix entry. */ |
||
1877 | prefix_list[i].netif = netif; |
||
1878 | ip6_addr_set(&(prefix_list[i].prefix), prefix); |
||
1879 | return i; |
||
1880 | } |
||
1881 | } |
||
1882 | |||
1883 | /* Entry not available. */ |
||
1884 | return -1; |
||
1885 | } |
||
1886 | |||
1887 | /** |
||
1888 | * Determine the next hop for a destination. Will determine if the |
||
1889 | * destination is on-link, else a suitable on-link router is selected. |
||
1890 | * |
||
1891 | * The last entry index is cached for fast entry search. |
||
1892 | * |
||
1893 | * @param ip6addr the destination address |
||
1894 | * @param netif the netif on which the packet will be sent |
||
1895 | * @return the neighbor cache entry for the next hop, ERR_RTE if no |
||
1896 | * suitable next hop was found, ERR_MEM if no cache entry |
||
1897 | * could be created |
||
1898 | */ |
||
1899 | static s8_t |
||
1900 | nd6_get_next_hop_entry(const ip6_addr_t *ip6addr, struct netif *netif) |
||
1901 | { |
||
1902 | #ifdef LWIP_HOOK_ND6_GET_GW |
||
1903 | const ip6_addr_t *next_hop_addr; |
||
1904 | #endif /* LWIP_HOOK_ND6_GET_GW */ |
||
1905 | s8_t i; |
||
1906 | |||
1907 | IP6_ADDR_ZONECHECK_NETIF(ip6addr, netif); |
||
1908 | |||
1909 | #if LWIP_NETIF_HWADDRHINT |
||
1910 | if (netif->hints != NULL) { |
||
1911 | /* per-pcb cached entry was given */ |
||
1912 | u8_t addr_hint = netif->hints->addr_hint; |
||
1913 | if (addr_hint < LWIP_ND6_NUM_DESTINATIONS) { |
||
1914 | nd6_cached_destination_index = addr_hint; |
||
1915 | } |
||
1916 | } |
||
1917 | #endif /* LWIP_NETIF_HWADDRHINT */ |
||
1918 | |||
1919 | /* Look for ip6addr in destination cache. */ |
||
1920 | if (ip6_addr_cmp(ip6addr, &(destination_cache[nd6_cached_destination_index].destination_addr))) { |
||
1921 | /* the cached entry index is the right one! */ |
||
1922 | /* do nothing. */ |
||
1923 | ND6_STATS_INC(nd6.cachehit); |
||
1924 | } else { |
||
1925 | /* Search destination cache. */ |
||
1926 | i = nd6_find_destination_cache_entry(ip6addr); |
||
1927 | if (i >= 0) { |
||
1928 | /* found destination entry. make it our new cached index. */ |
||
1929 | nd6_cached_destination_index = i; |
||
1930 | } else { |
||
1931 | /* Not found. Create a new destination entry. */ |
||
1932 | i = nd6_new_destination_cache_entry(); |
||
1933 | if (i >= 0) { |
||
1934 | /* got new destination entry. make it our new cached index. */ |
||
1935 | nd6_cached_destination_index = i; |
||
1936 | } else { |
||
1937 | /* Could not create a destination cache entry. */ |
||
1938 | return ERR_MEM; |
||
1939 | } |
||
1940 | |||
1941 | /* Copy dest address to destination cache. */ |
||
1942 | ip6_addr_set(&(destination_cache[nd6_cached_destination_index].destination_addr), ip6addr); |
||
1943 | |||
1944 | /* Now find the next hop. is it a neighbor? */ |
||
1945 | if (ip6_addr_islinklocal(ip6addr) || |
||
1946 | nd6_is_prefix_in_netif(ip6addr, netif)) { |
||
1947 | /* Destination in local link. */ |
||
1948 | destination_cache[nd6_cached_destination_index].pmtu = netif->mtu; |
||
1949 | ip6_addr_copy(destination_cache[nd6_cached_destination_index].next_hop_addr, destination_cache[nd6_cached_destination_index].destination_addr); |
||
1950 | #ifdef LWIP_HOOK_ND6_GET_GW |
||
1951 | } else if ((next_hop_addr = LWIP_HOOK_ND6_GET_GW(netif, ip6addr)) != NULL) { |
||
1952 | /* Next hop for destination provided by hook function. */ |
||
1953 | destination_cache[nd6_cached_destination_index].pmtu = netif->mtu; |
||
1954 | ip6_addr_set(&destination_cache[nd6_cached_destination_index].next_hop_addr, next_hop_addr); |
||
1955 | #endif /* LWIP_HOOK_ND6_GET_GW */ |
||
1956 | } else { |
||
1957 | /* We need to select a router. */ |
||
1958 | i = nd6_select_router(ip6addr, netif); |
||
1959 | if (i < 0) { |
||
1960 | /* No router found. */ |
||
1961 | ip6_addr_set_any(&(destination_cache[nd6_cached_destination_index].destination_addr)); |
||
1962 | return ERR_RTE; |
||
1963 | } |
||
1964 | destination_cache[nd6_cached_destination_index].pmtu = netif->mtu; /* Start with netif mtu, correct through ICMPv6 if necessary */ |
||
1965 | ip6_addr_copy(destination_cache[nd6_cached_destination_index].next_hop_addr, default_router_list[i].neighbor_entry->next_hop_address); |
||
1966 | } |
||
1967 | } |
||
1968 | } |
||
1969 | |||
1970 | #if LWIP_NETIF_HWADDRHINT |
||
1971 | if (netif->hints != NULL) { |
||
1972 | /* per-pcb cached entry was given */ |
||
1973 | netif->hints->addr_hint = nd6_cached_destination_index; |
||
1974 | } |
||
1975 | #endif /* LWIP_NETIF_HWADDRHINT */ |
||
1976 | |||
1977 | /* Look in neighbor cache for the next-hop address. */ |
||
1978 | if (ip6_addr_cmp(&(destination_cache[nd6_cached_destination_index].next_hop_addr), |
||
1979 | &(neighbor_cache[nd6_cached_neighbor_index].next_hop_address))) { |
||
1980 | /* Cache hit. */ |
||
1981 | /* Do nothing. */ |
||
1982 | ND6_STATS_INC(nd6.cachehit); |
||
1983 | } else { |
||
1984 | i = nd6_find_neighbor_cache_entry(&(destination_cache[nd6_cached_destination_index].next_hop_addr)); |
||
1985 | if (i >= 0) { |
||
1986 | /* Found a matching record, make it new cached entry. */ |
||
1987 | nd6_cached_neighbor_index = i; |
||
1988 | } else { |
||
1989 | /* Neighbor not in cache. Make a new entry. */ |
||
1990 | i = nd6_new_neighbor_cache_entry(); |
||
1991 | if (i >= 0) { |
||
1992 | /* got new neighbor entry. make it our new cached index. */ |
||
1993 | nd6_cached_neighbor_index = i; |
||
1994 | } else { |
||
1995 | /* Could not create a neighbor cache entry. */ |
||
1996 | return ERR_MEM; |
||
1997 | } |
||
1998 | |||
1999 | /* Initialize fields. */ |
||
2000 | ip6_addr_copy(neighbor_cache[i].next_hop_address, |
||
2001 | destination_cache[nd6_cached_destination_index].next_hop_addr); |
||
2002 | neighbor_cache[i].isrouter = 0; |
||
2003 | neighbor_cache[i].netif = netif; |
||
2004 | neighbor_cache[i].state = ND6_INCOMPLETE; |
||
2005 | neighbor_cache[i].counter.probes_sent = 1; |
||
2006 | nd6_send_neighbor_cache_probe(&neighbor_cache[i], ND6_SEND_FLAG_MULTICAST_DEST); |
||
2007 | } |
||
2008 | } |
||
2009 | |||
2010 | /* Reset this destination's age. */ |
||
2011 | destination_cache[nd6_cached_destination_index].age = 0; |
||
2012 | |||
2013 | return nd6_cached_neighbor_index; |
||
2014 | } |
||
2015 | |||
2016 | /** |
||
2017 | * Queue a packet for a neighbor. |
||
2018 | * |
||
2019 | * @param neighbor_index the index in the neighbor cache table |
||
2020 | * @param q packet to be queued |
||
2021 | * @return ERR_OK if succeeded, ERR_MEM if out of memory |
||
2022 | */ |
||
2023 | static err_t |
||
2024 | nd6_queue_packet(s8_t neighbor_index, struct pbuf *q) |
||
2025 | { |
||
2026 | err_t result = ERR_MEM; |
||
2027 | struct pbuf *p; |
||
2028 | int copy_needed = 0; |
||
2029 | #if LWIP_ND6_QUEUEING |
||
2030 | struct nd6_q_entry *new_entry, *r; |
||
2031 | #endif /* LWIP_ND6_QUEUEING */ |
||
2032 | |||
2033 | if ((neighbor_index < 0) || (neighbor_index >= LWIP_ND6_NUM_NEIGHBORS)) { |
||
2034 | return ERR_ARG; |
||
2035 | } |
||
2036 | |||
2037 | /* IF q includes a pbuf that must be copied, we have to copy the whole chain |
||
2038 | * into a new PBUF_RAM. See the definition of PBUF_NEEDS_COPY for details. */ |
||
2039 | p = q; |
||
2040 | while (p) { |
||
2041 | if (PBUF_NEEDS_COPY(p)) { |
||
2042 | copy_needed = 1; |
||
2043 | break; |
||
2044 | } |
||
2045 | p = p->next; |
||
2046 | } |
||
2047 | if (copy_needed) { |
||
2048 | /* copy the whole packet into new pbufs */ |
||
2049 | p = pbuf_alloc(PBUF_LINK, q->tot_len, PBUF_RAM); |
||
2050 | while ((p == NULL) && (neighbor_cache[neighbor_index].q != NULL)) { |
||
2051 | /* Free oldest packet (as per RFC recommendation) */ |
||
2052 | #if LWIP_ND6_QUEUEING |
||
2053 | r = neighbor_cache[neighbor_index].q; |
||
2054 | neighbor_cache[neighbor_index].q = r->next; |
||
2055 | r->next = NULL; |
||
2056 | nd6_free_q(r); |
||
2057 | #else /* LWIP_ND6_QUEUEING */ |
||
2058 | pbuf_free(neighbor_cache[neighbor_index].q); |
||
2059 | neighbor_cache[neighbor_index].q = NULL; |
||
2060 | #endif /* LWIP_ND6_QUEUEING */ |
||
2061 | p = pbuf_alloc(PBUF_LINK, q->tot_len, PBUF_RAM); |
||
2062 | } |
||
2063 | if (p != NULL) { |
||
2064 | if (pbuf_copy(p, q) != ERR_OK) { |
||
2065 | pbuf_free(p); |
||
2066 | p = NULL; |
||
2067 | } |
||
2068 | } |
||
2069 | } else { |
||
2070 | /* referencing the old pbuf is enough */ |
||
2071 | p = q; |
||
2072 | pbuf_ref(p); |
||
2073 | } |
||
2074 | /* packet was copied/ref'd? */ |
||
2075 | if (p != NULL) { |
||
2076 | /* queue packet ... */ |
||
2077 | #if LWIP_ND6_QUEUEING |
||
2078 | /* allocate a new nd6 queue entry */ |
||
2079 | new_entry = (struct nd6_q_entry *)memp_malloc(MEMP_ND6_QUEUE); |
||
2080 | if ((new_entry == NULL) && (neighbor_cache[neighbor_index].q != NULL)) { |
||
2081 | /* Free oldest packet (as per RFC recommendation) */ |
||
2082 | r = neighbor_cache[neighbor_index].q; |
||
2083 | neighbor_cache[neighbor_index].q = r->next; |
||
2084 | r->next = NULL; |
||
2085 | nd6_free_q(r); |
||
2086 | new_entry = (struct nd6_q_entry *)memp_malloc(MEMP_ND6_QUEUE); |
||
2087 | } |
||
2088 | if (new_entry != NULL) { |
||
2089 | new_entry->next = NULL; |
||
2090 | new_entry->p = p; |
||
2091 | if (neighbor_cache[neighbor_index].q != NULL) { |
||
2092 | /* queue was already existent, append the new entry to the end */ |
||
2093 | r = neighbor_cache[neighbor_index].q; |
||
2094 | while (r->next != NULL) { |
||
2095 | r = r->next; |
||
2096 | } |
||
2097 | r->next = new_entry; |
||
2098 | } else { |
||
2099 | /* queue did not exist, first item in queue */ |
||
2100 | neighbor_cache[neighbor_index].q = new_entry; |
||
2101 | } |
||
2102 | LWIP_DEBUGF(LWIP_DBG_TRACE, ("ipv6: queued packet %p on neighbor entry %"S16_F"\n", (void *)p, (s16_t)neighbor_index)); |
||
2103 | result = ERR_OK; |
||
2104 | } else { |
||
2105 | /* the pool MEMP_ND6_QUEUE is empty */ |
||
2106 | pbuf_free(p); |
||
2107 | LWIP_DEBUGF(LWIP_DBG_TRACE, ("ipv6: could not queue a copy of packet %p (out of memory)\n", (void *)p)); |
||
2108 | /* { result == ERR_MEM } through initialization */ |
||
2109 | } |
||
2110 | #else /* LWIP_ND6_QUEUEING */ |
||
2111 | /* Queue a single packet. If an older packet is already queued, free it as per RFC. */ |
||
2112 | if (neighbor_cache[neighbor_index].q != NULL) { |
||
2113 | pbuf_free(neighbor_cache[neighbor_index].q); |
||
2114 | } |
||
2115 | neighbor_cache[neighbor_index].q = p; |
||
2116 | LWIP_DEBUGF(LWIP_DBG_TRACE, ("ipv6: queued packet %p on neighbor entry %"S16_F"\n", (void *)p, (s16_t)neighbor_index)); |
||
2117 | result = ERR_OK; |
||
2118 | #endif /* LWIP_ND6_QUEUEING */ |
||
2119 | } else { |
||
2120 | LWIP_DEBUGF(LWIP_DBG_TRACE, ("ipv6: could not queue a copy of packet %p (out of memory)\n", (void *)q)); |
||
2121 | /* { result == ERR_MEM } through initialization */ |
||
2122 | } |
||
2123 | |||
2124 | return result; |
||
2125 | } |
||
2126 | |||
2127 | #if LWIP_ND6_QUEUEING |
||
2128 | /** |
||
2129 | * Free a complete queue of nd6 q entries |
||
2130 | * |
||
2131 | * @param q a queue of nd6_q_entry to free |
||
2132 | */ |
||
2133 | static void |
||
2134 | nd6_free_q(struct nd6_q_entry *q) |
||
2135 | { |
||
2136 | struct nd6_q_entry *r; |
||
2137 | LWIP_ASSERT("q != NULL", q != NULL); |
||
2138 | LWIP_ASSERT("q->p != NULL", q->p != NULL); |
||
2139 | while (q) { |
||
2140 | r = q; |
||
2141 | q = q->next; |
||
2142 | LWIP_ASSERT("r->p != NULL", (r->p != NULL)); |
||
2143 | pbuf_free(r->p); |
||
2144 | memp_free(MEMP_ND6_QUEUE, r); |
||
2145 | } |
||
2146 | } |
||
2147 | #endif /* LWIP_ND6_QUEUEING */ |
||
2148 | |||
2149 | /** |
||
2150 | * Send queued packets for a neighbor |
||
2151 | * |
||
2152 | * @param i the neighbor to send packets to |
||
2153 | */ |
||
2154 | static void |
||
2155 | nd6_send_q(s8_t i) |
||
2156 | { |
||
2157 | struct ip6_hdr *ip6hdr; |
||
2158 | ip6_addr_t dest; |
||
2159 | #if LWIP_ND6_QUEUEING |
||
2160 | struct nd6_q_entry *q; |
||
2161 | #endif /* LWIP_ND6_QUEUEING */ |
||
2162 | |||
2163 | if ((i < 0) || (i >= LWIP_ND6_NUM_NEIGHBORS)) { |
||
2164 | return; |
||
2165 | } |
||
2166 | |||
2167 | #if LWIP_ND6_QUEUEING |
||
2168 | while (neighbor_cache[i].q != NULL) { |
||
2169 | /* remember first in queue */ |
||
2170 | q = neighbor_cache[i].q; |
||
2171 | /* pop first item off the queue */ |
||
2172 | neighbor_cache[i].q = q->next; |
||
2173 | /* Get ipv6 header. */ |
||
2174 | ip6hdr = (struct ip6_hdr *)(q->p->payload); |
||
2175 | /* Create an aligned copy. */ |
||
2176 | ip6_addr_copy_from_packed(dest, ip6hdr->dest); |
||
2177 | /* Restore the zone, if applicable. */ |
||
2178 | ip6_addr_assign_zone(&dest, IP6_UNKNOWN, neighbor_cache[i].netif); |
||
2179 | /* send the queued IPv6 packet */ |
||
2180 | (neighbor_cache[i].netif)->output_ip6(neighbor_cache[i].netif, q->p, &dest); |
||
2181 | /* free the queued IP packet */ |
||
2182 | pbuf_free(q->p); |
||
2183 | /* now queue entry can be freed */ |
||
2184 | memp_free(MEMP_ND6_QUEUE, q); |
||
2185 | } |
||
2186 | #else /* LWIP_ND6_QUEUEING */ |
||
2187 | if (neighbor_cache[i].q != NULL) { |
||
2188 | /* Get ipv6 header. */ |
||
2189 | ip6hdr = (struct ip6_hdr *)(neighbor_cache[i].q->payload); |
||
2190 | /* Create an aligned copy. */ |
||
2191 | ip6_addr_copy_from_packed(dest, ip6hdr->dest); |
||
2192 | /* Restore the zone, if applicable. */ |
||
2193 | ip6_addr_assign_zone(&dest, IP6_UNKNOWN, neighbor_cache[i].netif); |
||
2194 | /* send the queued IPv6 packet */ |
||
2195 | (neighbor_cache[i].netif)->output_ip6(neighbor_cache[i].netif, neighbor_cache[i].q, &dest); |
||
2196 | /* free the queued IP packet */ |
||
2197 | pbuf_free(neighbor_cache[i].q); |
||
2198 | neighbor_cache[i].q = NULL; |
||
2199 | } |
||
2200 | #endif /* LWIP_ND6_QUEUEING */ |
||
2201 | } |
||
2202 | |||
2203 | /** |
||
2204 | * A packet is to be transmitted to a specific IPv6 destination on a specific |
||
2205 | * interface. Check if we can find the hardware address of the next hop to use |
||
2206 | * for the packet. If so, give the hardware address to the caller, which should |
||
2207 | * use it to send the packet right away. Otherwise, enqueue the packet for |
||
2208 | * later transmission while looking up the hardware address, if possible. |
||
2209 | * |
||
2210 | * As such, this function returns one of three different possible results: |
||
2211 | * |
||
2212 | * - ERR_OK with a non-NULL 'hwaddrp': the caller should send the packet now. |
||
2213 | * - ERR_OK with a NULL 'hwaddrp': the packet has been enqueued for later. |
||
2214 | * - not ERR_OK: something went wrong; forward the error upward in the stack. |
||
2215 | * |
||
2216 | * @param netif The lwIP network interface on which the IP packet will be sent. |
||
2217 | * @param q The pbuf(s) containing the IP packet to be sent. |
||
2218 | * @param ip6addr The destination IPv6 address of the packet. |
||
2219 | * @param hwaddrp On success, filled with a pointer to a HW address or NULL (meaning |
||
2220 | * the packet has been queued). |
||
2221 | * @return |
||
2222 | * - ERR_OK on success, ERR_RTE if no route was found for the packet, |
||
2223 | * or ERR_MEM if low memory conditions prohibit sending the packet at all. |
||
2224 | */ |
||
2225 | err_t |
||
2226 | nd6_get_next_hop_addr_or_queue(struct netif *netif, struct pbuf *q, const ip6_addr_t *ip6addr, const u8_t **hwaddrp) |
||
2227 | { |
||
2228 | s8_t i; |
||
2229 | |||
2230 | /* Get next hop record. */ |
||
2231 | i = nd6_get_next_hop_entry(ip6addr, netif); |
||
2232 | if (i < 0) { |
||
2233 | /* failed to get a next hop neighbor record. */ |
||
2234 | return i; |
||
2235 | } |
||
2236 | |||
2237 | /* Now that we have a destination record, send or queue the packet. */ |
||
2238 | if (neighbor_cache[i].state == ND6_STALE) { |
||
2239 | /* Switch to delay state. */ |
||
2240 | neighbor_cache[i].state = ND6_DELAY; |
||
2241 | neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME / ND6_TMR_INTERVAL; |
||
2242 | } |
||
2243 | /* @todo should we send or queue if PROBE? send for now, to let unicast NS pass. */ |
||
2244 | if ((neighbor_cache[i].state == ND6_REACHABLE) || |
||
2245 | (neighbor_cache[i].state == ND6_DELAY) || |
||
2246 | (neighbor_cache[i].state == ND6_PROBE)) { |
||
2247 | |||
2248 | /* Tell the caller to send out the packet now. */ |
||
2249 | *hwaddrp = neighbor_cache[i].lladdr; |
||
2250 | return ERR_OK; |
||
2251 | } |
||
2252 | |||
2253 | /* We should queue packet on this interface. */ |
||
2254 | *hwaddrp = NULL; |
||
2255 | return nd6_queue_packet(i, q); |
||
2256 | } |
||
2257 | |||
2258 | |||
2259 | /** |
||
2260 | * Get the Path MTU for a destination. |
||
2261 | * |
||
2262 | * @param ip6addr the destination address |
||
2263 | * @param netif the netif on which the packet will be sent |
||
2264 | * @return the Path MTU, if known, or the netif default MTU |
||
2265 | */ |
||
2266 | u16_t |
||
2267 | nd6_get_destination_mtu(const ip6_addr_t *ip6addr, struct netif *netif) |
||
2268 | { |
||
2269 | s8_t i; |
||
2270 | |||
2271 | i = nd6_find_destination_cache_entry(ip6addr); |
||
2272 | if (i >= 0) { |
||
2273 | if (destination_cache[i].pmtu > 0) { |
||
2274 | return destination_cache[i].pmtu; |
||
2275 | } |
||
2276 | } |
||
2277 | |||
2278 | if (netif != NULL) { |
||
2279 | return netif->mtu; |
||
2280 | } |
||
2281 | |||
2282 | return 1280; /* Minimum MTU */ |
||
2283 | } |
||
2284 | |||
2285 | |||
2286 | #if LWIP_ND6_TCP_REACHABILITY_HINTS |
||
2287 | /** |
||
2288 | * Provide the Neighbor discovery process with a hint that a |
||
2289 | * destination is reachable. Called by tcp_receive when ACKs are |
||
2290 | * received or sent (as per RFC). This is useful to avoid sending |
||
2291 | * NS messages every 30 seconds. |
||
2292 | * |
||
2293 | * @param ip6addr the destination address which is know to be reachable |
||
2294 | * by an upper layer protocol (TCP) |
||
2295 | */ |
||
2296 | void |
||
2297 | nd6_reachability_hint(const ip6_addr_t *ip6addr) |
||
2298 | { |
||
2299 | s8_t i; |
||
2300 | |||
2301 | /* Find destination in cache. */ |
||
2302 | if (ip6_addr_cmp(ip6addr, &(destination_cache[nd6_cached_destination_index].destination_addr))) { |
||
2303 | i = nd6_cached_destination_index; |
||
2304 | ND6_STATS_INC(nd6.cachehit); |
||
2305 | } else { |
||
2306 | i = nd6_find_destination_cache_entry(ip6addr); |
||
2307 | } |
||
2308 | if (i < 0) { |
||
2309 | return; |
||
2310 | } |
||
2311 | |||
2312 | /* Find next hop neighbor in cache. */ |
||
2313 | if (ip6_addr_cmp(&(destination_cache[i].next_hop_addr), &(neighbor_cache[nd6_cached_neighbor_index].next_hop_address))) { |
||
2314 | i = nd6_cached_neighbor_index; |
||
2315 | ND6_STATS_INC(nd6.cachehit); |
||
2316 | } else { |
||
2317 | i = nd6_find_neighbor_cache_entry(&(destination_cache[i].next_hop_addr)); |
||
2318 | } |
||
2319 | if (i < 0) { |
||
2320 | return; |
||
2321 | } |
||
2322 | |||
2323 | /* For safety: don't set as reachable if we don't have a LL address yet. Misuse protection. */ |
||
2324 | if (neighbor_cache[i].state == ND6_INCOMPLETE || neighbor_cache[i].state == ND6_NO_ENTRY) { |
||
2325 | return; |
||
2326 | } |
||
2327 | |||
2328 | /* Set reachability state. */ |
||
2329 | neighbor_cache[i].state = ND6_REACHABLE; |
||
2330 | neighbor_cache[i].counter.reachable_time = reachable_time; |
||
2331 | } |
||
2332 | #endif /* LWIP_ND6_TCP_REACHABILITY_HINTS */ |
||
2333 | |||
2334 | /** |
||
2335 | * Remove all prefix, neighbor_cache and router entries of the specified netif. |
||
2336 | * |
||
2337 | * @param netif points to a network interface |
||
2338 | */ |
||
2339 | void |
||
2340 | nd6_cleanup_netif(struct netif *netif) |
||
2341 | { |
||
2342 | u8_t i; |
||
2343 | s8_t router_index; |
||
2344 | for (i = 0; i < LWIP_ND6_NUM_PREFIXES; i++) { |
||
2345 | if (prefix_list[i].netif == netif) { |
||
2346 | prefix_list[i].netif = NULL; |
||
2347 | } |
||
2348 | } |
||
2349 | for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { |
||
2350 | if (neighbor_cache[i].netif == netif) { |
||
2351 | for (router_index = 0; router_index < LWIP_ND6_NUM_ROUTERS; router_index++) { |
||
2352 | if (default_router_list[router_index].neighbor_entry == &neighbor_cache[i]) { |
||
2353 | default_router_list[router_index].neighbor_entry = NULL; |
||
2354 | default_router_list[router_index].flags = 0; |
||
2355 | } |
||
2356 | } |
||
2357 | neighbor_cache[i].isrouter = 0; |
||
2358 | nd6_free_neighbor_cache_entry(i); |
||
2359 | } |
||
2360 | } |
||
2361 | /* Clear the destination cache, since many entries may now have become |
||
2362 | * invalid for one of several reasons. As destination cache entries have no |
||
2363 | * netif association, use a sledgehammer approach (this can be improved). */ |
||
2364 | nd6_clear_destination_cache(); |
||
2365 | } |
||
2366 | |||
2367 | #if LWIP_IPV6_MLD |
||
2368 | /** |
||
2369 | * The state of a local IPv6 address entry is about to change. If needed, join |
||
2370 | * or leave the solicited-node multicast group for the address. |
||
2371 | * |
||
2372 | * @param netif The netif that owns the address. |
||
2373 | * @param addr_idx The index of the address. |
||
2374 | * @param new_state The new (IP6_ADDR_) state for the address. |
||
2375 | */ |
||
2376 | void |
||
2377 | nd6_adjust_mld_membership(struct netif *netif, s8_t addr_idx, u8_t new_state) |
||
2378 | { |
||
2379 | u8_t old_state, old_member, new_member; |
||
2380 | |||
2381 | old_state = netif_ip6_addr_state(netif, addr_idx); |
||
2382 | |||
2383 | /* Determine whether we were, and should be, a member of the solicited-node |
||
2384 | * multicast group for this address. For tentative addresses, the group is |
||
2385 | * not joined until the address enters the TENTATIVE_1 (or VALID) state. */ |
||
2386 | old_member = (old_state != IP6_ADDR_INVALID && old_state != IP6_ADDR_DUPLICATED && old_state != IP6_ADDR_TENTATIVE); |
||
2387 | new_member = (new_state != IP6_ADDR_INVALID && new_state != IP6_ADDR_DUPLICATED && new_state != IP6_ADDR_TENTATIVE); |
||
2388 | |||
2389 | if (old_member != new_member) { |
||
2390 | ip6_addr_set_solicitednode(&multicast_address, netif_ip6_addr(netif, addr_idx)->addr[3]); |
||
2391 | ip6_addr_assign_zone(&multicast_address, IP6_MULTICAST, netif); |
||
2392 | |||
2393 | if (new_member) { |
||
2394 | mld6_joingroup_netif(netif, &multicast_address); |
||
2395 | } else { |
||
2396 | mld6_leavegroup_netif(netif, &multicast_address); |
||
2397 | } |
||
2398 | } |
||
2399 | } |
||
2400 | #endif /* LWIP_IPV6_MLD */ |
||
2401 | |||
2402 | #endif /* LWIP_IPV6 */ |