]>
Commit | Line | Data |
---|---|---|
c6610e1d VM |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* | |
3 | * Copyright (C) 2013 Allied Telesis Labs NZ | |
4 | * Chris Packham, <judge.packham@gmail.com> | |
5 | * | |
6 | * Copyright (C) 2022 YADRO | |
7 | * Viacheslav Mitrofanov <v.v.mitrofanov@yadro.com> | |
8 | */ | |
9 | ||
10 | /* Neighbour Discovery for IPv6 */ | |
11 | ||
12 | #include <common.h> | |
13 | #include <net.h> | |
14 | #include <net6.h> | |
15 | #include <ndisc.h> | |
6de98b60 EM |
16 | #include <stdlib.h> |
17 | #include <linux/delay.h> | |
c6610e1d VM |
18 | |
19 | /* IPv6 destination address of packet waiting for ND */ | |
20 | struct in6_addr net_nd_sol_packet_ip6 = ZERO_IPV6_ADDR; | |
21 | /* IPv6 address we are expecting ND advert from */ | |
22 | static struct in6_addr net_nd_rep_packet_ip6 = ZERO_IPV6_ADDR; | |
23 | /* MAC destination address of packet waiting for ND */ | |
24 | uchar *net_nd_packet_mac; | |
25 | /* pointer to packet waiting to be transmitted after ND is resolved */ | |
26 | uchar *net_nd_tx_packet; | |
27 | static uchar net_nd_packet_buf[PKTSIZE_ALIGN + PKTALIGN]; | |
28 | /* size of packet waiting to be transmitted */ | |
29 | int net_nd_tx_packet_size; | |
30 | /* the timer for ND resolution */ | |
31 | ulong net_nd_timer_start; | |
32 | /* the number of requests we have sent so far */ | |
33 | int net_nd_try; | |
6de98b60 EM |
34 | struct in6_addr all_routers = ALL_ROUTERS_MULT_ADDR; |
35 | ||
36 | #define MAX_RTR_SOLICITATIONS 3 | |
37 | /* The maximum time to delay sending the first router solicitation message. */ | |
38 | #define MAX_SOLICITATION_DELAY 1 // 1 second | |
39 | /* The time to wait before sending the next router solicitation message. */ | |
40 | #define RTR_SOLICITATION_INTERVAL 4000 // 4 seconds | |
c6610e1d VM |
41 | |
42 | #define IP6_NDISC_OPT_SPACE(len) (((len) + 2 + 7) & ~7) | |
43 | ||
44 | /** | |
45 | * ndisc_insert_option() - Insert an option into a neighbor discovery packet | |
46 | * | |
6de98b60 | 47 | * @opt: pointer to the option element of the neighbor discovery packet |
c6610e1d VM |
48 | * @type: option type to insert |
49 | * @data: option data to insert | |
50 | * @len: data length | |
51 | * Return: the number of bytes inserted (which may be >= len) | |
52 | */ | |
6de98b60 | 53 | static int ndisc_insert_option(__u8 *opt, int type, u8 *data, int len) |
c6610e1d VM |
54 | { |
55 | int space = IP6_NDISC_OPT_SPACE(len); | |
56 | ||
6de98b60 EM |
57 | opt[0] = type; |
58 | opt[1] = space >> 3; | |
59 | memcpy(&opt[2], data, len); | |
c6610e1d VM |
60 | len += 2; |
61 | ||
62 | /* fill the remainder with 0 */ | |
63 | if (space - len > 0) | |
6de98b60 | 64 | memset(&opt[len], '\0', space - len); |
c6610e1d VM |
65 | |
66 | return space; | |
67 | } | |
68 | ||
69 | /** | |
70 | * ndisc_extract_enetaddr() - Extract the Ethernet address from a ND packet | |
71 | * | |
72 | * Note that the link layer address could be anything but the only networking | |
73 | * media that u-boot supports is Ethernet so we assume we're extracting a 6 | |
74 | * byte Ethernet MAC address. | |
75 | * | |
76 | * @ndisc: pointer to ND packet | |
77 | * @enetaddr: extracted MAC addr | |
78 | */ | |
79 | static void ndisc_extract_enetaddr(struct nd_msg *ndisc, uchar enetaddr[6]) | |
80 | { | |
81 | memcpy(enetaddr, &ndisc->opt[2], 6); | |
82 | } | |
83 | ||
84 | /** | |
85 | * ndisc_has_option() - Check if the ND packet has the specified option set | |
86 | * | |
87 | * @ip6: pointer to IPv6 header | |
88 | * @type: option type to check | |
89 | * Return: 1 if ND has that option, 0 therwise | |
90 | */ | |
91 | static int ndisc_has_option(struct ip6_hdr *ip6, __u8 type) | |
92 | { | |
93 | struct nd_msg *ndisc = (struct nd_msg *)(((uchar *)ip6) + IP6_HDR_SIZE); | |
94 | ||
95 | if (ip6->payload_len <= sizeof(struct icmp6hdr)) | |
96 | return 0; | |
97 | ||
98 | return ndisc->opt[0] == type; | |
99 | } | |
100 | ||
101 | static void ip6_send_ns(struct in6_addr *neigh_addr) | |
102 | { | |
103 | struct in6_addr dst_adr; | |
104 | unsigned char enetaddr[6]; | |
105 | struct nd_msg *msg; | |
106 | __u16 len; | |
107 | uchar *pkt; | |
108 | unsigned short csum; | |
109 | unsigned int pcsum; | |
110 | ||
111 | debug("sending neighbor solicitation for %pI6c our address %pI6c\n", | |
112 | neigh_addr, &net_link_local_ip6); | |
113 | ||
114 | /* calculate src, dest IPv6 addr and dest Eth addr */ | |
115 | ip6_make_snma(&dst_adr, neigh_addr); | |
116 | ip6_make_mult_ethdstaddr(enetaddr, &dst_adr); | |
117 | len = sizeof(struct icmp6hdr) + IN6ADDRSZ + | |
118 | IP6_NDISC_OPT_SPACE(INETHADDRSZ); | |
119 | ||
120 | pkt = (uchar *)net_tx_packet; | |
121 | pkt += net_set_ether(pkt, enetaddr, PROT_IP6); | |
122 | pkt += ip6_add_hdr(pkt, &net_link_local_ip6, &dst_adr, PROT_ICMPV6, | |
123 | IPV6_NDISC_HOPLIMIT, len); | |
124 | ||
125 | /* ICMPv6 - NS */ | |
126 | msg = (struct nd_msg *)pkt; | |
127 | msg->icmph.icmp6_type = IPV6_NDISC_NEIGHBOUR_SOLICITATION; | |
128 | msg->icmph.icmp6_code = 0; | |
129 | memset(&msg->icmph.icmp6_cksum, 0, sizeof(__be16)); | |
130 | memset(&msg->icmph.icmp6_unused, 0, sizeof(__be32)); | |
131 | ||
132 | /* Set the target address and llsaddr option */ | |
133 | net_copy_ip6(&msg->target, neigh_addr); | |
6de98b60 | 134 | ndisc_insert_option(msg->opt, ND_OPT_SOURCE_LL_ADDR, net_ethaddr, |
c6610e1d VM |
135 | INETHADDRSZ); |
136 | ||
137 | /* checksum */ | |
138 | pcsum = csum_partial((__u8 *)msg, len, 0); | |
139 | csum = csum_ipv6_magic(&net_link_local_ip6, &dst_adr, | |
140 | len, PROT_ICMPV6, pcsum); | |
141 | msg->icmph.icmp6_cksum = csum; | |
142 | pkt += len; | |
143 | ||
144 | /* send it! */ | |
145 | net_send_packet(net_tx_packet, (pkt - net_tx_packet)); | |
146 | } | |
147 | ||
6de98b60 EM |
148 | /* |
149 | * ip6_send_rs() - Send IPv6 Router Solicitation Message. | |
150 | * | |
151 | * A router solicitation is sent to discover a router. RS message creation is | |
152 | * based on RFC 4861 section 4.1. Router Solicitation Message Format. | |
153 | */ | |
154 | void ip6_send_rs(void) | |
155 | { | |
156 | unsigned char enetaddr[6]; | |
157 | struct rs_msg *msg; | |
158 | __u16 icmp_len; | |
159 | uchar *pkt; | |
160 | unsigned short csum; | |
161 | unsigned int pcsum; | |
162 | static unsigned int retry_count; | |
163 | ||
164 | if (!ip6_is_unspecified_addr(&net_gateway6) && | |
165 | net_prefix_length != 0) { | |
166 | net_set_state(NETLOOP_SUCCESS); | |
167 | return; | |
168 | } else if (retry_count >= MAX_RTR_SOLICITATIONS) { | |
169 | net_set_state(NETLOOP_FAIL); | |
170 | net_set_timeout_handler(0, NULL); | |
171 | retry_count = 0; | |
172 | return; | |
173 | } | |
174 | ||
175 | printf("ROUTER SOLICITATION %d\n", retry_count + 1); | |
176 | ||
177 | ip6_make_mult_ethdstaddr(enetaddr, &all_routers); | |
178 | /* | |
179 | * ICMP length is the size of ICMP header (8) + one option (8) = 16. | |
180 | * The option is 2 bytes of type and length + 6 bytes for MAC. | |
181 | */ | |
182 | icmp_len = sizeof(struct icmp6hdr) + IP6_NDISC_OPT_SPACE(INETHADDRSZ); | |
183 | ||
184 | pkt = (uchar *)net_tx_packet; | |
185 | pkt += net_set_ether(pkt, enetaddr, PROT_IP6); | |
186 | pkt += ip6_add_hdr(pkt, &net_link_local_ip6, &all_routers, PROT_ICMPV6, | |
187 | IPV6_NDISC_HOPLIMIT, icmp_len); | |
188 | ||
189 | /* ICMPv6 - RS */ | |
190 | msg = (struct rs_msg *)pkt; | |
191 | msg->icmph.icmp6_type = IPV6_NDISC_ROUTER_SOLICITATION; | |
192 | msg->icmph.icmp6_code = 0; | |
193 | memset(&msg->icmph.icmp6_cksum, 0, sizeof(__be16)); | |
194 | memset(&msg->icmph.icmp6_unused, 0, sizeof(__be32)); | |
195 | ||
196 | /* Set the llsaddr option */ | |
197 | ndisc_insert_option(msg->opt, ND_OPT_SOURCE_LL_ADDR, net_ethaddr, | |
198 | INETHADDRSZ); | |
199 | ||
200 | /* checksum */ | |
201 | pcsum = csum_partial((__u8 *)msg, icmp_len, 0); | |
202 | csum = csum_ipv6_magic(&net_link_local_ip6, &all_routers, | |
203 | icmp_len, PROT_ICMPV6, pcsum); | |
204 | msg->icmph.icmp6_cksum = csum; | |
205 | pkt += icmp_len; | |
206 | ||
207 | /* Wait up to 1 second if it is the first try to get the RA */ | |
208 | if (retry_count == 0) | |
209 | udelay(((unsigned int)rand() % 1000000) * MAX_SOLICITATION_DELAY); | |
210 | ||
211 | /* send it! */ | |
212 | net_send_packet(net_tx_packet, (pkt - net_tx_packet)); | |
213 | ||
214 | retry_count++; | |
215 | net_set_timeout_handler(RTR_SOLICITATION_INTERVAL, ip6_send_rs); | |
216 | } | |
217 | ||
c6610e1d VM |
218 | static void |
219 | ip6_send_na(uchar *eth_dst_addr, struct in6_addr *neigh_addr, | |
220 | struct in6_addr *target) | |
221 | { | |
222 | struct nd_msg *msg; | |
223 | __u16 len; | |
224 | uchar *pkt; | |
225 | unsigned short csum; | |
226 | ||
227 | debug("sending neighbor advertisement for %pI6c to %pI6c (%pM)\n", | |
228 | target, neigh_addr, eth_dst_addr); | |
229 | ||
230 | len = sizeof(struct icmp6hdr) + IN6ADDRSZ + | |
231 | IP6_NDISC_OPT_SPACE(INETHADDRSZ); | |
232 | ||
233 | pkt = (uchar *)net_tx_packet; | |
234 | pkt += net_set_ether(pkt, eth_dst_addr, PROT_IP6); | |
235 | pkt += ip6_add_hdr(pkt, &net_link_local_ip6, neigh_addr, | |
236 | PROT_ICMPV6, IPV6_NDISC_HOPLIMIT, len); | |
237 | ||
238 | /* ICMPv6 - NA */ | |
239 | msg = (struct nd_msg *)pkt; | |
240 | msg->icmph.icmp6_type = IPV6_NDISC_NEIGHBOUR_ADVERTISEMENT; | |
241 | msg->icmph.icmp6_code = 0; | |
242 | memset(&msg->icmph.icmp6_cksum, 0, sizeof(__be16)); | |
243 | memset(&msg->icmph.icmp6_unused, 0, sizeof(__be32)); | |
244 | msg->icmph.icmp6_dataun.u_nd_advt.solicited = 1; | |
245 | msg->icmph.icmp6_dataun.u_nd_advt.override = 1; | |
246 | /* Set the target address and lltargetaddr option */ | |
247 | net_copy_ip6(&msg->target, target); | |
6de98b60 | 248 | ndisc_insert_option(msg->opt, ND_OPT_TARGET_LL_ADDR, net_ethaddr, |
c6610e1d VM |
249 | INETHADDRSZ); |
250 | ||
251 | /* checksum */ | |
252 | csum = csum_ipv6_magic(&net_link_local_ip6, | |
253 | neigh_addr, len, PROT_ICMPV6, | |
254 | csum_partial((__u8 *)msg, len, 0)); | |
255 | msg->icmph.icmp6_cksum = csum; | |
256 | pkt += len; | |
257 | ||
258 | /* send it! */ | |
259 | net_send_packet(net_tx_packet, (pkt - net_tx_packet)); | |
260 | } | |
261 | ||
262 | void ndisc_request(void) | |
263 | { | |
264 | if (!ip6_addr_in_subnet(&net_ip6, &net_nd_sol_packet_ip6, | |
265 | net_prefix_length)) { | |
266 | if (ip6_is_unspecified_addr(&net_gateway6)) { | |
267 | puts("## Warning: gatewayip6 is needed but not set\n"); | |
268 | net_nd_rep_packet_ip6 = net_nd_sol_packet_ip6; | |
269 | } else { | |
270 | net_nd_rep_packet_ip6 = net_gateway6; | |
271 | } | |
272 | } else { | |
273 | net_nd_rep_packet_ip6 = net_nd_sol_packet_ip6; | |
274 | } | |
275 | ||
276 | ip6_send_ns(&net_nd_rep_packet_ip6); | |
277 | } | |
278 | ||
279 | int ndisc_timeout_check(void) | |
280 | { | |
281 | ulong t; | |
282 | ||
283 | if (ip6_is_unspecified_addr(&net_nd_sol_packet_ip6)) | |
284 | return 0; | |
285 | ||
286 | t = get_timer(0); | |
287 | ||
288 | /* check for NDISC timeout */ | |
289 | if ((t - net_nd_timer_start) > NDISC_TIMEOUT) { | |
290 | net_nd_try++; | |
291 | if (net_nd_try >= NDISC_TIMEOUT_COUNT) { | |
292 | puts("\nNeighbour discovery retry count exceeded; " | |
293 | "starting again\n"); | |
294 | net_nd_try = 0; | |
295 | net_set_state(NETLOOP_FAIL); | |
296 | } else { | |
297 | net_nd_timer_start = t; | |
298 | ndisc_request(); | |
299 | } | |
300 | } | |
301 | return 1; | |
302 | } | |
303 | ||
6de98b60 EM |
304 | /* |
305 | * ndisc_init() - Make initial steps for ND state machine. | |
306 | * Usually move variables into initial state. | |
307 | */ | |
c6610e1d VM |
308 | void ndisc_init(void) |
309 | { | |
310 | net_nd_packet_mac = NULL; | |
311 | net_nd_tx_packet = NULL; | |
312 | net_nd_sol_packet_ip6 = net_null_addr_ip6; | |
313 | net_nd_rep_packet_ip6 = net_null_addr_ip6; | |
314 | net_nd_tx_packet_size = 0; | |
315 | net_nd_tx_packet = &net_nd_packet_buf[0] + (PKTALIGN - 1); | |
316 | net_nd_tx_packet -= (ulong)net_nd_tx_packet % PKTALIGN; | |
317 | } | |
318 | ||
6de98b60 EM |
319 | /* |
320 | * validate_ra() - Validate the router advertisement message. | |
321 | * | |
322 | * @ip6: Pointer to the router advertisement packet | |
323 | * | |
324 | * Check if the router advertisement message is valid. Conditions are | |
325 | * according to RFC 4861 section 6.1.2. Validation of Router Advertisement | |
326 | * Messages. | |
327 | * | |
328 | * Return: true if the message is valid and false if it is invalid. | |
329 | */ | |
330 | bool validate_ra(struct ip6_hdr *ip6) | |
331 | { | |
332 | struct icmp6hdr *icmp = (struct icmp6hdr *)(ip6 + 1); | |
333 | ||
334 | /* ICMP length (derived from the IP length) should be 16 or more octets. */ | |
335 | if (ip6->payload_len < 16) | |
336 | return false; | |
337 | ||
338 | /* Source IP Address should be a valid link-local address. */ | |
339 | if ((ntohs(ip6->saddr.s6_addr16[0]) & IPV6_LINK_LOCAL_MASK) != | |
340 | IPV6_LINK_LOCAL_PREFIX) | |
341 | return false; | |
342 | ||
343 | /* | |
344 | * The IP Hop Limit field should have a value of 255, i.e., the packet | |
345 | * could not possibly have been forwarded by a router. | |
346 | */ | |
347 | if (ip6->hop_limit != 255) | |
348 | return false; | |
349 | ||
350 | /* ICMP checksum has already been checked in net_ip6_handler. */ | |
351 | ||
352 | if (icmp->icmp6_code != 0) | |
353 | return false; | |
354 | ||
355 | return true; | |
356 | } | |
357 | ||
358 | /* | |
359 | * process_ra() - Process the router advertisement packet. | |
360 | * | |
361 | * @ip6: Pointer to the router advertisement packet | |
362 | * @len: Length of the router advertisement packet | |
363 | * | |
364 | * Process the received router advertisement message. | |
365 | * Although RFC 4861 requires retaining at least two router addresses, we only | |
366 | * keep one because of the U-Boot limitations and its goal of lightweight code. | |
367 | * | |
368 | * Return: 0 - RA is a default router and contains valid prefix information. | |
369 | * Non-zero - RA options are invalid or do not indicate it is a default router | |
370 | * or do not contain valid prefix information. | |
371 | */ | |
372 | int process_ra(struct ip6_hdr *ip6, int len) | |
373 | { | |
374 | /* Pointer to the ICMP section of the packet */ | |
375 | struct icmp6hdr *icmp = (struct icmp6hdr *)(ip6 + 1); | |
376 | struct ra_msg *msg = (struct ra_msg *)icmp; | |
377 | int remaining_option_len = len - IP6_HDR_SIZE - sizeof(struct ra_msg); | |
378 | unsigned short int option_len; /* Length of each option */ | |
379 | /* Pointer to the ICMPv6 message options */ | |
380 | unsigned char *option = NULL; | |
381 | /* 8-bit identifier of the type of ICMPv6 option */ | |
382 | unsigned char type = 0; | |
383 | struct icmp6_ra_prefix_info *prefix = NULL; | |
384 | ||
a29df56e EM |
385 | if (len > ETH_MAX_MTU) |
386 | return -EMSGSIZE; | |
6de98b60 EM |
387 | /* Ignore the packet if router lifetime is 0. */ |
388 | if (!icmp->icmp6_rt_lifetime) | |
389 | return -EOPNOTSUPP; | |
390 | ||
391 | /* Processing the options */ | |
392 | option = msg->opt; | |
393 | while (remaining_option_len > 0) { | |
394 | /* The 2nd byte of the option is its length. */ | |
395 | option_len = option[1]; | |
396 | /* All included options should have a positive length. */ | |
397 | if (option_len == 0) | |
398 | return -EINVAL; | |
399 | ||
400 | type = option[0]; | |
401 | /* All option types except Prefix Information are ignored. */ | |
402 | switch (type) { | |
403 | case ND_OPT_SOURCE_LL_ADDR: | |
404 | case ND_OPT_TARGET_LL_ADDR: | |
405 | case ND_OPT_REDIRECT_HDR: | |
406 | case ND_OPT_MTU: | |
407 | break; | |
408 | case ND_OPT_PREFIX_INFO: | |
409 | prefix = (struct icmp6_ra_prefix_info *)option; | |
410 | /* The link-local prefix 0xfe80::/10 is ignored. */ | |
411 | if ((ntohs(prefix->prefix.s6_addr16[0]) & | |
412 | IPV6_LINK_LOCAL_MASK) == IPV6_LINK_LOCAL_PREFIX) | |
413 | break; | |
414 | if (prefix->on_link && ntohl(prefix->valid_lifetime)) { | |
415 | net_prefix_length = prefix->prefix_len; | |
416 | net_gateway6 = ip6->saddr; | |
417 | return 0; | |
418 | } | |
419 | break; | |
420 | default: | |
421 | debug("Unknown IPv6 Neighbor Discovery Option 0x%x\n", | |
422 | type); | |
423 | } | |
424 | ||
425 | option_len <<= 3; /* Option length is a multiple of 8. */ | |
426 | remaining_option_len -= option_len; | |
427 | option += option_len; | |
428 | } | |
429 | ||
430 | return -EADDRNOTAVAIL; | |
431 | } | |
432 | ||
c6610e1d VM |
433 | int ndisc_receive(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len) |
434 | { | |
435 | struct icmp6hdr *icmp = | |
436 | (struct icmp6hdr *)(((uchar *)ip6) + IP6_HDR_SIZE); | |
437 | struct nd_msg *ndisc = (struct nd_msg *)icmp; | |
438 | uchar neigh_eth_addr[6]; | |
6de98b60 | 439 | int err = 0; // The error code returned calling functions. |
c6610e1d VM |
440 | |
441 | switch (icmp->icmp6_type) { | |
442 | case IPV6_NDISC_NEIGHBOUR_SOLICITATION: | |
443 | debug("received neighbor solicitation for %pI6c from %pI6c\n", | |
444 | &ndisc->target, &ip6->saddr); | |
445 | if (ip6_is_our_addr(&ndisc->target) && | |
446 | ndisc_has_option(ip6, ND_OPT_SOURCE_LL_ADDR)) { | |
447 | ndisc_extract_enetaddr(ndisc, neigh_eth_addr); | |
448 | ip6_send_na(neigh_eth_addr, &ip6->saddr, | |
449 | &ndisc->target); | |
450 | } | |
451 | break; | |
452 | ||
453 | case IPV6_NDISC_NEIGHBOUR_ADVERTISEMENT: | |
454 | /* are we waiting for a reply ? */ | |
455 | if (ip6_is_unspecified_addr(&net_nd_sol_packet_ip6)) | |
456 | break; | |
457 | ||
458 | if ((memcmp(&ndisc->target, &net_nd_rep_packet_ip6, | |
459 | sizeof(struct in6_addr)) == 0) && | |
460 | ndisc_has_option(ip6, ND_OPT_TARGET_LL_ADDR)) { | |
461 | ndisc_extract_enetaddr(ndisc, neigh_eth_addr); | |
462 | ||
463 | /* save address for later use */ | |
464 | if (!net_nd_packet_mac) | |
56e3b147 | 465 | net_nd_packet_mac = neigh_eth_addr; |
c6610e1d VM |
466 | |
467 | /* modify header, and transmit it */ | |
468 | memcpy(((struct ethernet_hdr *)net_nd_tx_packet)->et_dest, | |
469 | neigh_eth_addr, 6); | |
470 | ||
471 | net_send_packet(net_nd_tx_packet, | |
472 | net_nd_tx_packet_size); | |
473 | ||
474 | /* no ND request pending now */ | |
475 | net_nd_sol_packet_ip6 = net_null_addr_ip6; | |
476 | net_nd_tx_packet_size = 0; | |
477 | net_nd_packet_mac = NULL; | |
478 | } | |
479 | break; | |
6de98b60 EM |
480 | case IPV6_NDISC_ROUTER_SOLICITATION: |
481 | break; | |
482 | case IPV6_NDISC_ROUTER_ADVERTISEMENT: | |
483 | debug("Received router advertisement for %pI6c from %pI6c\n", | |
484 | &ip6->daddr, &ip6->saddr); | |
485 | /* | |
486 | * If gateway and prefix are set, the RA packet is ignored. The | |
487 | * reason is that the U-Boot code is supposed to be as compact | |
488 | * as possible and does not need to take care of multiple | |
489 | * routers. In addition to that, U-Boot does not want to handle | |
490 | * scenarios like a router setting its lifetime to zero to | |
491 | * indicate it is not routing anymore. U-Boot program has a | |
492 | * short life when the system boots up and does not need such | |
493 | * sophistication. | |
494 | */ | |
495 | if (!ip6_is_unspecified_addr(&net_gateway6) && | |
496 | net_prefix_length != 0) { | |
497 | break; | |
498 | } | |
499 | if (!validate_ra(ip6)) { | |
500 | debug("Invalid router advertisement message.\n"); | |
501 | break; | |
502 | } | |
503 | err = process_ra(ip6, len); | |
504 | if (err) | |
505 | debug("Ignored router advertisement. Error: %d\n", err); | |
506 | else | |
507 | printf("Set gatewayip6: %pI6c, prefix_length: %d\n", | |
508 | &net_gateway6, net_prefix_length); | |
509 | break; | |
c6610e1d VM |
510 | default: |
511 | debug("Unexpected ICMPv6 type 0x%x\n", icmp->icmp6_type); | |
512 | return -1; | |
513 | } | |
514 | ||
515 | return 0; | |
516 | } |