ndisc.c 15 KB

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