sd_fusing.py: Ensure selected flashing target actually matches what is on device
[platform/kernel/u-boot.git] / net / ndisc.c
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>
16 #include <stdlib.h>
17 #include <linux/delay.h>
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;
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
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  *
47  * @opt:        pointer to the option element of the neighbor discovery packet
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  */
53 static int ndisc_insert_option(__u8 *opt, int type, u8 *data, int len)
54 {
55         int space = IP6_NDISC_OPT_SPACE(len);
56
57         opt[0] = type;
58         opt[1] = space >> 3;
59         memcpy(&opt[2], data, len);
60         len += 2;
61
62         /* fill the remainder with 0 */
63         if (space - len > 0)
64                 memset(&opt[len], '\0', space - len);
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);
134         ndisc_insert_option(msg->opt, ND_OPT_SOURCE_LL_ADDR, net_ethaddr,
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
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
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);
248         ndisc_insert_option(msg->opt, ND_OPT_TARGET_LL_ADDR, net_ethaddr,
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
304 /*
305  * ndisc_init() - Make initial steps for ND state machine.
306  * Usually move variables into initial state.
307  */
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
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
385         if (len > ETH_MAX_MTU)
386                 return -EMSGSIZE;
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
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];
439         int err = 0;    // The error code returned calling functions.
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)
465                                 net_nd_packet_mac = neigh_eth_addr;
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;
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;
510         default:
511                 debug("Unexpected ICMPv6 type 0x%x\n", icmp->icmp6_type);
512                 return -1;
513         }
514
515         return 0;
516 }