You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ndp.c 22KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784
  1. /*
  2. * Copyright (C) 2013 Michael Brown <mbrown@fensystems.co.uk>.
  3. *
  4. * This program is free software; you can redistribute it and/or
  5. * modify it under the terms of the GNU General Public License as
  6. * published by the Free Software Foundation; either version 2 of the
  7. * License, or any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful, but
  10. * WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. * General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program; if not, write to the Free Software
  16. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  17. * 02110-1301, USA.
  18. */
  19. FILE_LICENCE ( GPL2_OR_LATER );
  20. #include <stdlib.h>
  21. #include <string.h>
  22. #include <errno.h>
  23. #include <byteswap.h>
  24. #include <ipxe/in.h>
  25. #include <ipxe/iobuf.h>
  26. #include <ipxe/tcpip.h>
  27. #include <ipxe/ipv6.h>
  28. #include <ipxe/icmpv6.h>
  29. #include <ipxe/neighbour.h>
  30. #include <ipxe/dhcpv6.h>
  31. #include <ipxe/ndp.h>
  32. /** @file
  33. *
  34. * IPv6 neighbour discovery protocol
  35. *
  36. */
  37. static int ipv6conf_rx_router_advertisement ( struct net_device *netdev,
  38. unsigned int flags );
  39. /**
  40. * Transmit NDP packet with link-layer address option
  41. *
  42. * @v netdev Network device
  43. * @v sin6_src Source socket address
  44. * @v sin6_dest Destination socket address
  45. * @v data NDP header
  46. * @v len Size of NDP header
  47. * @v option_type NDP option type
  48. * @ret rc Return status code
  49. */
  50. static int ndp_tx_ll_addr ( struct net_device *netdev,
  51. struct sockaddr_in6 *sin6_src,
  52. struct sockaddr_in6 *sin6_dest,
  53. const void *data, size_t len,
  54. unsigned int option_type ) {
  55. struct sockaddr_tcpip *st_src =
  56. ( ( struct sockaddr_tcpip * ) sin6_src );
  57. struct sockaddr_tcpip *st_dest =
  58. ( ( struct sockaddr_tcpip * ) sin6_dest );
  59. struct ll_protocol *ll_protocol = netdev->ll_protocol;
  60. struct io_buffer *iobuf;
  61. struct ndp_ll_addr_option *ll_addr_opt;
  62. union ndp_header *ndp;
  63. size_t option_len;
  64. int rc;
  65. /* Allocate and populate buffer */
  66. option_len = ( ( sizeof ( *ll_addr_opt ) +
  67. ll_protocol->ll_addr_len + NDP_OPTION_BLKSZ - 1 ) &
  68. ~( NDP_OPTION_BLKSZ - 1 ) );
  69. iobuf = alloc_iob ( MAX_LL_NET_HEADER_LEN + len + option_len );
  70. if ( ! iobuf )
  71. return -ENOMEM;
  72. iob_reserve ( iobuf, MAX_LL_NET_HEADER_LEN );
  73. memcpy ( iob_put ( iobuf, len ), data, len );
  74. ll_addr_opt = iob_put ( iobuf, option_len );
  75. ll_addr_opt->header.type = option_type;
  76. ll_addr_opt->header.blocks = ( option_len / NDP_OPTION_BLKSZ );
  77. memcpy ( ll_addr_opt->ll_addr, netdev->ll_addr,
  78. ll_protocol->ll_addr_len );
  79. ndp = iobuf->data;
  80. ndp->icmp.chksum = tcpip_chksum ( ndp, ( len + option_len ) );
  81. /* Transmit packet */
  82. if ( ( rc = tcpip_tx ( iobuf, &icmpv6_protocol, st_src, st_dest,
  83. netdev, &ndp->icmp.chksum ) ) != 0 ) {
  84. DBGC ( netdev, "NDP could not transmit packet: %s\n",
  85. strerror ( rc ) );
  86. return rc;
  87. }
  88. return 0;
  89. }
  90. /**
  91. * Transmit NDP neighbour discovery request
  92. *
  93. * @v netdev Network device
  94. * @v net_protocol Network-layer protocol
  95. * @v net_dest Destination network-layer address
  96. * @v net_source Source network-layer address
  97. * @ret rc Return status code
  98. */
  99. static int ndp_tx_request ( struct net_device *netdev,
  100. struct net_protocol *net_protocol __unused,
  101. const void *net_dest, const void *net_source ) {
  102. struct sockaddr_in6 sin6_src;
  103. struct sockaddr_in6 sin6_dest;
  104. struct ndp_neighbour_header neigh;
  105. int rc;
  106. /* Construct source address */
  107. memset ( &sin6_src, 0, sizeof ( sin6_src ) );
  108. sin6_src.sin6_family = AF_INET6;
  109. memcpy ( &sin6_src.sin6_addr, net_source,
  110. sizeof ( sin6_src.sin6_addr ) );
  111. sin6_src.sin6_scope_id = netdev->index;
  112. /* Construct multicast destination address */
  113. memset ( &sin6_dest, 0, sizeof ( sin6_dest ) );
  114. sin6_dest.sin6_family = AF_INET6;
  115. sin6_dest.sin6_scope_id = netdev->index;
  116. ipv6_solicited_node ( &sin6_dest.sin6_addr, net_dest );
  117. /* Construct neighbour header */
  118. memset ( &neigh, 0, sizeof ( neigh ) );
  119. neigh.icmp.type = ICMPV6_NEIGHBOUR_SOLICITATION;
  120. memcpy ( &neigh.target, net_dest, sizeof ( neigh.target ) );
  121. /* Transmit neighbour discovery packet */
  122. if ( ( rc = ndp_tx_ll_addr ( netdev, &sin6_src, &sin6_dest, &neigh,
  123. sizeof ( neigh ),
  124. NDP_OPT_LL_SOURCE ) ) != 0 )
  125. return rc;
  126. return 0;
  127. }
  128. /** NDP neighbour discovery protocol */
  129. struct neighbour_discovery ndp_discovery = {
  130. .name = "NDP",
  131. .tx_request = ndp_tx_request,
  132. };
  133. /**
  134. * Transmit NDP router solicitation
  135. *
  136. * @v netdev Network device
  137. * @ret rc Return status code
  138. */
  139. static int ndp_tx_router_solicitation ( struct net_device *netdev ) {
  140. struct ndp_router_solicitation_header rsol;
  141. struct sockaddr_in6 sin6_dest;
  142. int rc;
  143. /* Construct multicast destination address */
  144. memset ( &sin6_dest, 0, sizeof ( sin6_dest ) );
  145. sin6_dest.sin6_family = AF_INET6;
  146. sin6_dest.sin6_scope_id = netdev->index;
  147. ipv6_all_routers ( &sin6_dest.sin6_addr );
  148. /* Construct router solicitation */
  149. memset ( &rsol, 0, sizeof ( rsol ) );
  150. rsol.icmp.type = ICMPV6_ROUTER_SOLICITATION;
  151. /* Transmit packet */
  152. if ( ( rc = ndp_tx_ll_addr ( netdev, NULL, &sin6_dest, &rsol,
  153. sizeof ( rsol ), NDP_OPT_LL_SOURCE ) ) !=0)
  154. return rc;
  155. return 0;
  156. }
  157. /**
  158. * Process NDP neighbour solicitation source link-layer address option
  159. *
  160. * @v netdev Network device
  161. * @v sin6_src Source socket address
  162. * @v ndp NDP packet
  163. * @v option NDP option
  164. * @v len NDP option length
  165. * @ret rc Return status code
  166. */
  167. static int
  168. ndp_rx_neighbour_solicitation_ll_source ( struct net_device *netdev,
  169. struct sockaddr_in6 *sin6_src,
  170. union ndp_header *ndp,
  171. union ndp_option *option,
  172. size_t len ) {
  173. struct ndp_neighbour_header *neigh = &ndp->neigh;
  174. struct ndp_ll_addr_option *ll_addr_opt = &option->ll_addr;
  175. struct ll_protocol *ll_protocol = netdev->ll_protocol;
  176. int rc;
  177. /* Silently ignore neighbour solicitations for addresses we do
  178. * not own.
  179. */
  180. if ( ! ipv6_has_addr ( netdev, &neigh->target ) )
  181. return 0;
  182. /* Sanity check */
  183. if ( offsetof ( typeof ( *ll_addr_opt ),
  184. ll_addr[ll_protocol->ll_addr_len] ) > len ) {
  185. DBGC ( netdev, "NDP neighbour solicitation link-layer address "
  186. "option too short at %zd bytes\n", len );
  187. return -EINVAL;
  188. }
  189. /* Create or update neighbour cache entry */
  190. if ( ( rc = neighbour_define ( netdev, &ipv6_protocol,
  191. &sin6_src->sin6_addr,
  192. ll_addr_opt->ll_addr ) ) != 0 ) {
  193. DBGC ( netdev, "NDP could not define %s => %s: %s\n",
  194. inet6_ntoa ( &sin6_src->sin6_addr ),
  195. ll_protocol->ntoa ( ll_addr_opt->ll_addr ),
  196. strerror ( rc ) );
  197. return rc;
  198. }
  199. /* Convert neighbour header to advertisement */
  200. memset ( neigh, 0, offsetof ( typeof ( *neigh ), target ) );
  201. neigh->icmp.type = ICMPV6_NEIGHBOUR_ADVERTISEMENT;
  202. neigh->flags = ( NDP_NEIGHBOUR_SOLICITED | NDP_NEIGHBOUR_OVERRIDE );
  203. /* Send neighbour advertisement */
  204. if ( ( rc = ndp_tx_ll_addr ( netdev, NULL, sin6_src, neigh,
  205. sizeof ( *neigh ),
  206. NDP_OPT_LL_TARGET ) ) != 0 )
  207. return rc;
  208. return 0;
  209. }
  210. /**
  211. * Process NDP neighbour advertisement target link-layer address option
  212. *
  213. * @v netdev Network device
  214. * @v sin6_src Source socket address
  215. * @v ndp NDP packet
  216. * @v option NDP option
  217. * @v len NDP option length
  218. * @ret rc Return status code
  219. */
  220. static int
  221. ndp_rx_neighbour_advertisement_ll_target ( struct net_device *netdev,
  222. struct sockaddr_in6 *sin6_src
  223. __unused,
  224. union ndp_header *ndp,
  225. union ndp_option *option,
  226. size_t len ) {
  227. struct ndp_neighbour_header *neigh = &ndp->neigh;
  228. struct ndp_ll_addr_option *ll_addr_opt = &option->ll_addr;
  229. struct ll_protocol *ll_protocol = netdev->ll_protocol;
  230. int rc;
  231. /* Sanity check */
  232. if ( offsetof ( typeof ( *ll_addr_opt ),
  233. ll_addr[ll_protocol->ll_addr_len] ) > len ) {
  234. DBGC ( netdev, "NDP neighbour advertisement link-layer address "
  235. "option too short at %zd bytes\n", len );
  236. return -EINVAL;
  237. }
  238. /* Update neighbour cache entry, if any */
  239. if ( ( rc = neighbour_update ( netdev, &ipv6_protocol, &neigh->target,
  240. ll_addr_opt->ll_addr ) ) != 0 ) {
  241. DBGC ( netdev, "NDP could not update %s => %s: %s\n",
  242. inet6_ntoa ( &neigh->target ),
  243. ll_protocol->ntoa ( ll_addr_opt->ll_addr ),
  244. strerror ( rc ) );
  245. return rc;
  246. }
  247. return 0;
  248. }
  249. /**
  250. * Process NDP router advertisement source link-layer address option
  251. *
  252. * @v netdev Network device
  253. * @v sin6_src Source socket address
  254. * @v ndp NDP packet
  255. * @v option NDP option
  256. * @v len NDP option length
  257. * @ret rc Return status code
  258. */
  259. static int
  260. ndp_rx_router_advertisement_ll_source ( struct net_device *netdev,
  261. struct sockaddr_in6 *sin6_src,
  262. union ndp_header *ndp __unused,
  263. union ndp_option *option, size_t len ) {
  264. struct ndp_ll_addr_option *ll_addr_opt = &option->ll_addr;
  265. struct ll_protocol *ll_protocol = netdev->ll_protocol;
  266. int rc;
  267. /* Sanity check */
  268. if ( offsetof ( typeof ( *ll_addr_opt ),
  269. ll_addr[ll_protocol->ll_addr_len] ) > len ) {
  270. DBGC ( netdev, "NDP router advertisement link-layer address "
  271. "option too short at %zd bytes\n", len );
  272. return -EINVAL;
  273. }
  274. /* Define neighbour cache entry */
  275. if ( ( rc = neighbour_define ( netdev, &ipv6_protocol,
  276. &sin6_src->sin6_addr,
  277. ll_addr_opt->ll_addr ) ) != 0 ) {
  278. DBGC ( netdev, "NDP could not define %s => %s: %s\n",
  279. inet6_ntoa ( &sin6_src->sin6_addr ),
  280. ll_protocol->ntoa ( ll_addr_opt->ll_addr ),
  281. strerror ( rc ) );
  282. return rc;
  283. }
  284. return 0;
  285. }
  286. /**
  287. * Process NDP router advertisement prefix information option
  288. *
  289. * @v netdev Network device
  290. * @v sin6_src Source socket address
  291. * @v ndp NDP packet
  292. * @v option NDP option
  293. * @v len NDP option length
  294. * @ret rc Return status code
  295. */
  296. static int
  297. ndp_rx_router_advertisement_prefix ( struct net_device *netdev,
  298. struct sockaddr_in6 *sin6_src,
  299. union ndp_header *ndp,
  300. union ndp_option *option, size_t len ) {
  301. struct ndp_router_advertisement_header *radv = &ndp->radv;
  302. struct ndp_prefix_information_option *prefix_opt = &option->prefix;
  303. struct in6_addr *router = &sin6_src->sin6_addr;
  304. int rc;
  305. /* Sanity check */
  306. if ( sizeof ( *prefix_opt ) > len ) {
  307. DBGC ( netdev, "NDP router advertisement prefix option too "
  308. "short at %zd bytes\n", len );
  309. return -EINVAL;
  310. }
  311. DBGC ( netdev, "NDP found %sdefault router %s ",
  312. ( radv->lifetime ? "" : "non-" ),
  313. inet6_ntoa ( &sin6_src->sin6_addr ) );
  314. DBGC ( netdev, "for %s-link %sautonomous prefix %s/%d\n",
  315. ( ( prefix_opt->flags & NDP_PREFIX_ON_LINK ) ? "on" : "off" ),
  316. ( ( prefix_opt->flags & NDP_PREFIX_AUTONOMOUS ) ? "" : "non-" ),
  317. inet6_ntoa ( &prefix_opt->prefix ),
  318. prefix_opt->prefix_len );
  319. /* Perform stateless address autoconfiguration, if applicable */
  320. if ( ( prefix_opt->flags &
  321. ( NDP_PREFIX_ON_LINK | NDP_PREFIX_AUTONOMOUS ) ) ==
  322. ( NDP_PREFIX_ON_LINK | NDP_PREFIX_AUTONOMOUS ) ) {
  323. if ( ( rc = ipv6_slaac ( netdev, &prefix_opt->prefix,
  324. prefix_opt->prefix_len,
  325. ( radv->lifetime ?
  326. router : NULL ) ) ) != 0 ) {
  327. DBGC ( netdev, "NDP could not autoconfigure prefix %s/"
  328. "%d: %s\n", inet6_ntoa ( &prefix_opt->prefix ),
  329. prefix_opt->prefix_len, strerror ( rc ) );
  330. return rc;
  331. }
  332. }
  333. return 0;
  334. }
  335. /** An NDP option handler */
  336. struct ndp_option_handler {
  337. /** ICMPv6 type */
  338. uint8_t icmp_type;
  339. /** Option type */
  340. uint8_t option_type;
  341. /**
  342. * Handle received option
  343. *
  344. * @v netdev Network device
  345. * @v sin6_src Source socket address
  346. * @v ndp NDP packet
  347. * @v option NDP option
  348. * @ret rc Return status code
  349. */
  350. int ( * rx ) ( struct net_device *netdev, struct sockaddr_in6 *sin6_src,
  351. union ndp_header *ndp, union ndp_option *option,
  352. size_t len );
  353. };
  354. /** NDP option handlers */
  355. static struct ndp_option_handler ndp_option_handlers[] = {
  356. {
  357. .icmp_type = ICMPV6_NEIGHBOUR_SOLICITATION,
  358. .option_type = NDP_OPT_LL_SOURCE,
  359. .rx = ndp_rx_neighbour_solicitation_ll_source,
  360. },
  361. {
  362. .icmp_type = ICMPV6_NEIGHBOUR_ADVERTISEMENT,
  363. .option_type = NDP_OPT_LL_TARGET,
  364. .rx = ndp_rx_neighbour_advertisement_ll_target,
  365. },
  366. {
  367. .icmp_type = ICMPV6_ROUTER_ADVERTISEMENT,
  368. .option_type = NDP_OPT_LL_SOURCE,
  369. .rx = ndp_rx_router_advertisement_ll_source,
  370. },
  371. {
  372. .icmp_type = ICMPV6_ROUTER_ADVERTISEMENT,
  373. .option_type = NDP_OPT_PREFIX,
  374. .rx = ndp_rx_router_advertisement_prefix,
  375. },
  376. };
  377. /**
  378. * Process received NDP option
  379. *
  380. * @v netdev Network device
  381. * @v sin6_src Source socket address
  382. * @v ndp NDP packet
  383. * @v option NDP option
  384. * @v len Option length
  385. * @ret rc Return status code
  386. */
  387. static int ndp_rx_option ( struct net_device *netdev,
  388. struct sockaddr_in6 *sin6_src, union ndp_header *ndp,
  389. union ndp_option *option, size_t len ) {
  390. struct ndp_option_handler *handler;
  391. unsigned int i;
  392. /* Locate a suitable option handler, if any */
  393. for ( i = 0 ; i < ( sizeof ( ndp_option_handlers ) /
  394. sizeof ( ndp_option_handlers[0] ) ) ; i++ ) {
  395. handler = &ndp_option_handlers[i];
  396. if ( ( handler->icmp_type == ndp->icmp.type ) &&
  397. ( handler->option_type == option->header.type ) ) {
  398. return handler->rx ( netdev, sin6_src, ndp,
  399. option, len );
  400. }
  401. }
  402. /* Silently ignore unknown options as per RFC 4861 */
  403. return 0;
  404. }
  405. /**
  406. * Process received NDP packet options
  407. *
  408. * @v netdev Network device
  409. * @v sin6_src Source socket address
  410. * @v ndp NDP header
  411. * @v offset Offset to NDP options
  412. * @v len Length of NDP packet
  413. * @ret rc Return status code
  414. */
  415. static int ndp_rx_options ( struct net_device *netdev,
  416. struct sockaddr_in6 *sin6_src,
  417. union ndp_header *ndp, size_t offset, size_t len ) {
  418. union ndp_option *option;
  419. size_t remaining;
  420. size_t option_len;
  421. int rc;
  422. /* Sanity check */
  423. if ( len < offset ) {
  424. DBGC ( netdev, "NDP packet too short at %zd bytes (min %zd "
  425. "bytes)\n", len, offset );
  426. return -EINVAL;
  427. }
  428. /* Search for option */
  429. option = ( ( ( void * ) ndp ) + offset );
  430. remaining = ( len - offset );
  431. while ( remaining ) {
  432. /* Sanity check */
  433. if ( ( remaining < sizeof ( option->header ) ) ||
  434. ( option->header.blocks == 0 ) ||
  435. ( remaining < ( option->header.blocks *
  436. NDP_OPTION_BLKSZ ) ) ) {
  437. DBGC ( netdev, "NDP bad option length:\n" );
  438. DBGC_HDA ( netdev, 0, option, remaining );
  439. return -EINVAL;
  440. }
  441. option_len = ( option->header.blocks * NDP_OPTION_BLKSZ );
  442. /* Handle option */
  443. if ( ( rc = ndp_rx_option ( netdev, sin6_src, ndp, option,
  444. option_len ) ) != 0 )
  445. return rc;
  446. /* Move to next option */
  447. option = ( ( ( void * ) option ) + option_len );
  448. remaining -= option_len;
  449. }
  450. return 0;
  451. }
  452. /**
  453. * Process received NDP neighbour solicitation or advertisement
  454. *
  455. * @v iobuf I/O buffer
  456. * @v netdev Network device
  457. * @v sin6_src Source socket address
  458. * @v sin6_dest Destination socket address
  459. * @ret rc Return status code
  460. */
  461. static int ndp_rx_neighbour ( struct io_buffer *iobuf,
  462. struct net_device *netdev,
  463. struct sockaddr_in6 *sin6_src,
  464. struct sockaddr_in6 *sin6_dest __unused ) {
  465. union ndp_header *ndp = iobuf->data;
  466. struct ndp_neighbour_header *neigh = &ndp->neigh;
  467. size_t len = iob_len ( iobuf );
  468. int rc;
  469. /* Process options */
  470. if ( ( rc = ndp_rx_options ( netdev, sin6_src, ndp,
  471. offsetof ( typeof ( *neigh ), option ),
  472. len ) ) != 0 )
  473. goto err_options;
  474. err_options:
  475. free_iob ( iobuf );
  476. return rc;
  477. }
  478. /**
  479. * Process received NDP router advertisement
  480. *
  481. * @v iobuf I/O buffer
  482. * @v netdev Network device
  483. * @v sin6_src Source socket address
  484. * @v sin6_dest Destination socket address
  485. * @ret rc Return status code
  486. */
  487. static int
  488. ndp_rx_router_advertisement ( struct io_buffer *iobuf,
  489. struct net_device *netdev,
  490. struct sockaddr_in6 *sin6_src,
  491. struct sockaddr_in6 *sin6_dest __unused ) {
  492. union ndp_header *ndp = iobuf->data;
  493. struct ndp_router_advertisement_header *radv = &ndp->radv;
  494. size_t len = iob_len ( iobuf );
  495. int rc;
  496. /* Process options */
  497. if ( ( rc = ndp_rx_options ( netdev, sin6_src, ndp,
  498. offsetof ( typeof ( *radv ), option ),
  499. len ) ) != 0 )
  500. goto err_options;
  501. /* Pass to IPv6 autoconfiguration */
  502. if ( ( rc = ipv6conf_rx_router_advertisement ( netdev,
  503. radv->flags ) ) != 0 )
  504. goto err_ipv6conf;
  505. err_ipv6conf:
  506. err_options:
  507. free_iob ( iobuf );
  508. return rc;
  509. }
  510. /** NDP ICMPv6 handlers */
  511. struct icmpv6_handler ndp_handlers[] __icmpv6_handler = {
  512. {
  513. .type = ICMPV6_NEIGHBOUR_SOLICITATION,
  514. .rx = ndp_rx_neighbour,
  515. },
  516. {
  517. .type = ICMPV6_NEIGHBOUR_ADVERTISEMENT,
  518. .rx = ndp_rx_neighbour,
  519. },
  520. {
  521. .type = ICMPV6_ROUTER_ADVERTISEMENT,
  522. .rx = ndp_rx_router_advertisement,
  523. },
  524. };
  525. /****************************************************************************
  526. *
  527. * IPv6 autoconfiguration
  528. *
  529. */
  530. /** An IPv6 configurator */
  531. struct ipv6conf {
  532. /** Reference count */
  533. struct refcnt refcnt;
  534. /** List of configurators */
  535. struct list_head list;
  536. /** Job control interface */
  537. struct interface job;
  538. /** DHCPv6 interface */
  539. struct interface dhcp;
  540. /** Network device being configured */
  541. struct net_device *netdev;
  542. /** Retransmission timer */
  543. struct retry_timer timer;
  544. };
  545. /** List of IPv6 configurators */
  546. static LIST_HEAD ( ipv6confs );
  547. /**
  548. * Free IPv6 configurator
  549. *
  550. * @v refcnt Reference count
  551. */
  552. static void ipv6conf_free ( struct refcnt *refcnt ) {
  553. struct ipv6conf *ipv6conf =
  554. container_of ( refcnt, struct ipv6conf, refcnt );
  555. netdev_put ( ipv6conf->netdev );
  556. free ( ipv6conf );
  557. }
  558. /**
  559. * Identify IPv6 configurator by network device
  560. *
  561. * @v netdev Network device
  562. * @ret ipv6 IPv6 configurator, or NULL
  563. */
  564. static struct ipv6conf * ipv6conf_demux ( struct net_device *netdev ) {
  565. struct ipv6conf *ipv6conf;
  566. list_for_each_entry ( ipv6conf, &ipv6confs, list ) {
  567. if ( ipv6conf->netdev == netdev )
  568. return ipv6conf;
  569. }
  570. return NULL;
  571. }
  572. /**
  573. * Finish IPv6 autoconfiguration
  574. *
  575. * @v ipv6 IPv6 configurator
  576. * @v rc Reason for finishing
  577. */
  578. static void ipv6conf_done ( struct ipv6conf *ipv6conf, int rc ) {
  579. /* Shut down interfaces */
  580. intf_shutdown ( &ipv6conf->job, rc );
  581. intf_shutdown ( &ipv6conf->dhcp, rc );
  582. /* Stop timer */
  583. stop_timer ( &ipv6conf->timer );
  584. /* Remove from list and drop list's reference */
  585. list_del ( &ipv6conf->list );
  586. ref_put ( &ipv6conf->refcnt );
  587. }
  588. /**
  589. * Handle IPv6 configurator timer expiry
  590. *
  591. * @v timer Retry timer
  592. * @v fail Failure indicator
  593. */
  594. static void ipv6conf_expired ( struct retry_timer *timer, int fail ) {
  595. struct ipv6conf *ipv6conf =
  596. container_of ( timer, struct ipv6conf, timer );
  597. /* If we have failed, terminate autoconfiguration */
  598. if ( fail ) {
  599. ipv6conf_done ( ipv6conf, -ETIMEDOUT );
  600. return;
  601. }
  602. /* Otherwise, transmit router solicitation and restart timer */
  603. start_timer ( &ipv6conf->timer );
  604. ndp_tx_router_solicitation ( ipv6conf->netdev );
  605. }
  606. /**
  607. * Handle router advertisement during IPv6 autoconfiguration
  608. *
  609. * @v netdev Network device
  610. * @v flags Router flags
  611. * @ret rc Return status code
  612. */
  613. static int ipv6conf_rx_router_advertisement ( struct net_device *netdev,
  614. unsigned int flags ) {
  615. struct ipv6conf *ipv6conf;
  616. int stateful;
  617. int rc;
  618. /* Identify IPv6 configurator, if any */
  619. ipv6conf = ipv6conf_demux ( netdev );
  620. if ( ! ipv6conf ) {
  621. /* Not an error; router advertisements are processed
  622. * as a background activity even when no explicit
  623. * autoconfiguration is taking place.
  624. */
  625. return 0;
  626. }
  627. /* If this is not the first solicited router advertisement, ignore it */
  628. if ( ! timer_running ( &ipv6conf->timer ) )
  629. return 0;
  630. /* Stop router solicitation timer */
  631. stop_timer ( &ipv6conf->timer );
  632. /* Start DHCPv6 if required */
  633. if ( flags & ( NDP_ROUTER_MANAGED | NDP_ROUTER_OTHER ) ) {
  634. stateful = ( flags & NDP_ROUTER_MANAGED );
  635. if ( ( rc = start_dhcpv6 ( &ipv6conf->dhcp, netdev,
  636. stateful ) ) != 0 ) {
  637. DBGC ( netdev, "NDP could not start state%s DHCPv6: "
  638. "%s\n", ( stateful ? "ful" : "less" ),
  639. strerror ( rc ) );
  640. ipv6conf_done ( ipv6conf, rc );
  641. return rc;
  642. }
  643. return 0;
  644. }
  645. /* Otherwise, terminate autoconfiguration */
  646. ipv6conf_done ( ipv6conf, 0 );
  647. return 0;
  648. }
  649. /** IPv6 configurator job interface operations */
  650. static struct interface_operation ipv6conf_job_op[] = {
  651. INTF_OP ( intf_close, struct ipv6conf *, ipv6conf_done ),
  652. };
  653. /** IPv6 configurator job interface descriptor */
  654. static struct interface_descriptor ipv6conf_job_desc =
  655. INTF_DESC ( struct ipv6conf, job, ipv6conf_job_op );
  656. /** IPv6 configurator DHCPv6 interface operations */
  657. static struct interface_operation ipv6conf_dhcp_op[] = {
  658. INTF_OP ( intf_close, struct ipv6conf *, ipv6conf_done ),
  659. };
  660. /** IPv6 configurator DHCPv6 interface descriptor */
  661. static struct interface_descriptor ipv6conf_dhcp_desc =
  662. INTF_DESC ( struct ipv6conf, dhcp, ipv6conf_dhcp_op );
  663. /**
  664. * Start IPv6 autoconfiguration
  665. *
  666. * @v job Job control interface
  667. * @v netdev Network device
  668. * @ret rc Return status code
  669. */
  670. int start_ipv6conf ( struct interface *job, struct net_device *netdev ) {
  671. struct ipv6conf *ipv6conf;
  672. /* Allocate and initialise structure */
  673. ipv6conf = zalloc ( sizeof ( *ipv6conf ) );
  674. if ( ! ipv6conf )
  675. return -ENOMEM;
  676. ref_init ( &ipv6conf->refcnt, ipv6conf_free );
  677. intf_init ( &ipv6conf->job, &ipv6conf_job_desc, &ipv6conf->refcnt );
  678. intf_init ( &ipv6conf->dhcp, &ipv6conf_dhcp_desc, &ipv6conf->refcnt );
  679. timer_init ( &ipv6conf->timer, ipv6conf_expired, &ipv6conf->refcnt );
  680. ipv6conf->netdev = netdev_get ( netdev );
  681. /* Start timer to initiate router solicitation */
  682. start_timer_nodelay ( &ipv6conf->timer );
  683. /* Attach parent interface, transfer reference to list, and return */
  684. intf_plug_plug ( &ipv6conf->job, job );
  685. list_add ( &ipv6conf->list, &ipv6confs );
  686. return 0;
  687. }
  688. /** IPv6 network device configurator */
  689. struct net_device_configurator ipv6_configurator __net_device_configurator = {
  690. .name = "ipv6",
  691. .start = start_ipv6conf,
  692. };