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 27KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001
  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
  38. ipv6conf_rx_router_advertisement ( struct net_device *netdev,
  39. struct ndp_router_advertisement_header *radv,
  40. size_t len );
  41. /**
  42. * Transmit NDP packet with link-layer address option
  43. *
  44. * @v netdev Network device
  45. * @v sin6_src Source socket address
  46. * @v sin6_dest Destination socket address
  47. * @v data NDP header
  48. * @v len Size of NDP header
  49. * @v option_type NDP option type
  50. * @ret rc Return status code
  51. */
  52. static int ndp_tx_ll_addr ( struct net_device *netdev,
  53. struct sockaddr_in6 *sin6_src,
  54. struct sockaddr_in6 *sin6_dest,
  55. const void *data, size_t len,
  56. unsigned int option_type ) {
  57. struct sockaddr_tcpip *st_src =
  58. ( ( struct sockaddr_tcpip * ) sin6_src );
  59. struct sockaddr_tcpip *st_dest =
  60. ( ( struct sockaddr_tcpip * ) sin6_dest );
  61. struct ll_protocol *ll_protocol = netdev->ll_protocol;
  62. struct io_buffer *iobuf;
  63. struct ndp_ll_addr_option *ll_addr_opt;
  64. union ndp_header *ndp;
  65. size_t option_len;
  66. int rc;
  67. /* Allocate and populate buffer */
  68. option_len = ( ( sizeof ( *ll_addr_opt ) +
  69. ll_protocol->ll_addr_len + NDP_OPTION_BLKSZ - 1 ) &
  70. ~( NDP_OPTION_BLKSZ - 1 ) );
  71. iobuf = alloc_iob ( MAX_LL_NET_HEADER_LEN + len + option_len );
  72. if ( ! iobuf )
  73. return -ENOMEM;
  74. iob_reserve ( iobuf, MAX_LL_NET_HEADER_LEN );
  75. memcpy ( iob_put ( iobuf, len ), data, len );
  76. ll_addr_opt = iob_put ( iobuf, option_len );
  77. ll_addr_opt->header.type = option_type;
  78. ll_addr_opt->header.blocks = ( option_len / NDP_OPTION_BLKSZ );
  79. memcpy ( ll_addr_opt->ll_addr, netdev->ll_addr,
  80. ll_protocol->ll_addr_len );
  81. ndp = iobuf->data;
  82. ndp->icmp.chksum = tcpip_chksum ( ndp, ( len + option_len ) );
  83. /* Transmit packet */
  84. if ( ( rc = tcpip_tx ( iobuf, &icmpv6_protocol, st_src, st_dest,
  85. netdev, &ndp->icmp.chksum ) ) != 0 ) {
  86. DBGC ( netdev, "NDP %s could not transmit packet: %s\n",
  87. netdev->name, strerror ( rc ) );
  88. return rc;
  89. }
  90. return 0;
  91. }
  92. /**
  93. * Transmit NDP neighbour discovery request
  94. *
  95. * @v netdev Network device
  96. * @v net_protocol Network-layer protocol
  97. * @v net_dest Destination network-layer address
  98. * @v net_source Source network-layer address
  99. * @ret rc Return status code
  100. */
  101. static int ndp_tx_request ( struct net_device *netdev,
  102. struct net_protocol *net_protocol __unused,
  103. const void *net_dest, const void *net_source ) {
  104. struct sockaddr_in6 sin6_src;
  105. struct sockaddr_in6 sin6_dest;
  106. struct ndp_neighbour_header neigh;
  107. int rc;
  108. /* Construct source address */
  109. memset ( &sin6_src, 0, sizeof ( sin6_src ) );
  110. sin6_src.sin6_family = AF_INET6;
  111. memcpy ( &sin6_src.sin6_addr, net_source,
  112. sizeof ( sin6_src.sin6_addr ) );
  113. sin6_src.sin6_scope_id = netdev->index;
  114. /* Construct multicast destination address */
  115. memset ( &sin6_dest, 0, sizeof ( sin6_dest ) );
  116. sin6_dest.sin6_family = AF_INET6;
  117. sin6_dest.sin6_scope_id = netdev->index;
  118. ipv6_solicited_node ( &sin6_dest.sin6_addr, net_dest );
  119. /* Construct neighbour header */
  120. memset ( &neigh, 0, sizeof ( neigh ) );
  121. neigh.icmp.type = ICMPV6_NEIGHBOUR_SOLICITATION;
  122. memcpy ( &neigh.target, net_dest, sizeof ( neigh.target ) );
  123. /* Transmit neighbour discovery packet */
  124. if ( ( rc = ndp_tx_ll_addr ( netdev, &sin6_src, &sin6_dest, &neigh,
  125. sizeof ( neigh ),
  126. NDP_OPT_LL_SOURCE ) ) != 0 )
  127. return rc;
  128. return 0;
  129. }
  130. /** NDP neighbour discovery protocol */
  131. struct neighbour_discovery ndp_discovery = {
  132. .name = "NDP",
  133. .tx_request = ndp_tx_request,
  134. };
  135. /**
  136. * Transmit NDP router solicitation
  137. *
  138. * @v netdev Network device
  139. * @ret rc Return status code
  140. */
  141. static int ndp_tx_router_solicitation ( struct net_device *netdev ) {
  142. struct ndp_router_solicitation_header rsol;
  143. struct sockaddr_in6 sin6_dest;
  144. int rc;
  145. /* Construct multicast destination address */
  146. memset ( &sin6_dest, 0, sizeof ( sin6_dest ) );
  147. sin6_dest.sin6_family = AF_INET6;
  148. sin6_dest.sin6_scope_id = netdev->index;
  149. ipv6_all_routers ( &sin6_dest.sin6_addr );
  150. /* Construct router solicitation */
  151. memset ( &rsol, 0, sizeof ( rsol ) );
  152. rsol.icmp.type = ICMPV6_ROUTER_SOLICITATION;
  153. /* Transmit packet */
  154. if ( ( rc = ndp_tx_ll_addr ( netdev, NULL, &sin6_dest, &rsol,
  155. sizeof ( rsol ), NDP_OPT_LL_SOURCE ) ) !=0)
  156. return rc;
  157. return 0;
  158. }
  159. /**
  160. * Process NDP neighbour solicitation source link-layer address option
  161. *
  162. * @v netdev Network device
  163. * @v sin6_src Source socket address
  164. * @v ndp NDP packet
  165. * @v option NDP option
  166. * @v len NDP option length
  167. * @ret rc Return status code
  168. */
  169. static int
  170. ndp_rx_neighbour_solicitation_ll_source ( struct net_device *netdev,
  171. struct sockaddr_in6 *sin6_src,
  172. union ndp_header *ndp,
  173. union ndp_option *option,
  174. size_t len ) {
  175. struct ndp_neighbour_header *neigh = &ndp->neigh;
  176. struct ndp_ll_addr_option *ll_addr_opt = &option->ll_addr;
  177. struct ll_protocol *ll_protocol = netdev->ll_protocol;
  178. int rc;
  179. /* Silently ignore neighbour solicitations for addresses we do
  180. * not own.
  181. */
  182. if ( ! ipv6_has_addr ( netdev, &neigh->target ) )
  183. return 0;
  184. /* Sanity check */
  185. if ( offsetof ( typeof ( *ll_addr_opt ),
  186. ll_addr[ll_protocol->ll_addr_len] ) > len ) {
  187. DBGC ( netdev, "NDP %s neighbour solicitation link-layer "
  188. "address option too short at %zd bytes\n",
  189. netdev->name, len );
  190. return -EINVAL;
  191. }
  192. /* Create or update neighbour cache entry */
  193. if ( ( rc = neighbour_define ( netdev, &ipv6_protocol,
  194. &sin6_src->sin6_addr,
  195. ll_addr_opt->ll_addr ) ) != 0 ) {
  196. DBGC ( netdev, "NDP %s could not define %s => %s: %s\n",
  197. netdev->name, inet6_ntoa ( &sin6_src->sin6_addr ),
  198. ll_protocol->ntoa ( ll_addr_opt->ll_addr ),
  199. strerror ( rc ) );
  200. return rc;
  201. }
  202. /* Convert neighbour header to advertisement */
  203. memset ( neigh, 0, offsetof ( typeof ( *neigh ), target ) );
  204. neigh->icmp.type = ICMPV6_NEIGHBOUR_ADVERTISEMENT;
  205. neigh->flags = ( NDP_NEIGHBOUR_SOLICITED | NDP_NEIGHBOUR_OVERRIDE );
  206. /* Send neighbour advertisement */
  207. if ( ( rc = ndp_tx_ll_addr ( netdev, NULL, sin6_src, neigh,
  208. sizeof ( *neigh ),
  209. NDP_OPT_LL_TARGET ) ) != 0 )
  210. return rc;
  211. return 0;
  212. }
  213. /**
  214. * Process NDP neighbour advertisement target link-layer address option
  215. *
  216. * @v netdev Network device
  217. * @v sin6_src Source socket address
  218. * @v ndp NDP packet
  219. * @v option NDP option
  220. * @v len NDP option length
  221. * @ret rc Return status code
  222. */
  223. static int
  224. ndp_rx_neighbour_advertisement_ll_target ( struct net_device *netdev,
  225. struct sockaddr_in6 *sin6_src
  226. __unused,
  227. union ndp_header *ndp,
  228. union ndp_option *option,
  229. size_t len ) {
  230. struct ndp_neighbour_header *neigh = &ndp->neigh;
  231. struct ndp_ll_addr_option *ll_addr_opt = &option->ll_addr;
  232. struct ll_protocol *ll_protocol = netdev->ll_protocol;
  233. int rc;
  234. /* Sanity check */
  235. if ( offsetof ( typeof ( *ll_addr_opt ),
  236. ll_addr[ll_protocol->ll_addr_len] ) > len ) {
  237. DBGC ( netdev, "NDP %s neighbour advertisement link-layer "
  238. "address option too short at %zd bytes\n",
  239. netdev->name, len );
  240. return -EINVAL;
  241. }
  242. /* Update neighbour cache entry, if any */
  243. if ( ( rc = neighbour_update ( netdev, &ipv6_protocol, &neigh->target,
  244. ll_addr_opt->ll_addr ) ) != 0 ) {
  245. DBGC ( netdev, "NDP %s could not update %s => %s: %s\n",
  246. netdev->name, inet6_ntoa ( &neigh->target ),
  247. ll_protocol->ntoa ( ll_addr_opt->ll_addr ),
  248. strerror ( rc ) );
  249. return rc;
  250. }
  251. return 0;
  252. }
  253. /**
  254. * Process NDP router advertisement source link-layer address option
  255. *
  256. * @v netdev Network device
  257. * @v sin6_src Source socket address
  258. * @v ndp NDP packet
  259. * @v option NDP option
  260. * @v len NDP option length
  261. * @ret rc Return status code
  262. */
  263. static int
  264. ndp_rx_router_advertisement_ll_source ( struct net_device *netdev,
  265. struct sockaddr_in6 *sin6_src,
  266. union ndp_header *ndp __unused,
  267. union ndp_option *option, size_t len ) {
  268. struct ndp_ll_addr_option *ll_addr_opt = &option->ll_addr;
  269. struct ll_protocol *ll_protocol = netdev->ll_protocol;
  270. int rc;
  271. /* Sanity check */
  272. if ( offsetof ( typeof ( *ll_addr_opt ),
  273. ll_addr[ll_protocol->ll_addr_len] ) > len ) {
  274. DBGC ( netdev, "NDP %s router advertisement link-layer address "
  275. "option too short at %zd bytes\n", netdev->name, len );
  276. return -EINVAL;
  277. }
  278. /* Define neighbour cache entry */
  279. if ( ( rc = neighbour_define ( netdev, &ipv6_protocol,
  280. &sin6_src->sin6_addr,
  281. ll_addr_opt->ll_addr ) ) != 0 ) {
  282. DBGC ( netdev, "NDP %s could not define %s => %s: %s\n",
  283. netdev->name, inet6_ntoa ( &sin6_src->sin6_addr ),
  284. ll_protocol->ntoa ( ll_addr_opt->ll_addr ),
  285. strerror ( rc ) );
  286. return rc;
  287. }
  288. return 0;
  289. }
  290. /**
  291. * Process NDP router advertisement prefix information option
  292. *
  293. * @v netdev Network device
  294. * @v sin6_src Source socket address
  295. * @v ndp NDP packet
  296. * @v option NDP option
  297. * @v len NDP option length
  298. * @ret rc Return status code
  299. */
  300. static int
  301. ndp_rx_router_advertisement_prefix ( struct net_device *netdev,
  302. struct sockaddr_in6 *sin6_src,
  303. union ndp_header *ndp,
  304. union ndp_option *option, size_t len ) {
  305. struct ndp_router_advertisement_header *radv = &ndp->radv;
  306. struct ndp_prefix_information_option *prefix_opt = &option->prefix;
  307. struct in6_addr *router = &sin6_src->sin6_addr;
  308. struct in6_addr address;
  309. int prefix_len;
  310. int rc;
  311. /* Sanity check */
  312. if ( sizeof ( *prefix_opt ) > len ) {
  313. DBGC ( netdev, "NDP %s router advertisement prefix option too "
  314. "short at %zd bytes\n", netdev->name, len );
  315. return -EINVAL;
  316. }
  317. DBGC ( netdev, "NDP %s found %sdefault router %s ",
  318. netdev->name, ( radv->lifetime ? "" : "non-" ),
  319. inet6_ntoa ( &sin6_src->sin6_addr ) );
  320. DBGC ( netdev, "for %s-link %sautonomous prefix %s/%d\n",
  321. ( ( prefix_opt->flags & NDP_PREFIX_ON_LINK ) ? "on" : "off" ),
  322. ( ( prefix_opt->flags & NDP_PREFIX_AUTONOMOUS ) ? "" : "non-" ),
  323. inet6_ntoa ( &prefix_opt->prefix ),
  324. prefix_opt->prefix_len );
  325. /* Ignore off-link prefixes */
  326. if ( ! ( prefix_opt->flags & NDP_PREFIX_ON_LINK ) )
  327. return 0;
  328. /* Define prefix */
  329. if ( ( rc = ipv6_set_prefix ( netdev, &prefix_opt->prefix,
  330. prefix_opt->prefix_len,
  331. ( radv->lifetime ?
  332. router : NULL ) ) ) != 0 ) {
  333. DBGC ( netdev, "NDP %s could not define prefix %s/%d: %s\n",
  334. netdev->name, inet6_ntoa ( &prefix_opt->prefix ),
  335. prefix_opt->prefix_len, strerror ( rc ) );
  336. return rc;
  337. }
  338. /* Perform stateless address autoconfiguration, if applicable */
  339. if ( prefix_opt->flags & NDP_PREFIX_AUTONOMOUS ) {
  340. memcpy ( &address, &prefix_opt->prefix, sizeof ( address ) );
  341. prefix_len = ipv6_eui64 ( &address, netdev );
  342. if ( prefix_len < 0 ) {
  343. rc = prefix_len;
  344. DBGC ( netdev, "NDP %s could not construct SLAAC "
  345. "address: %s\n", netdev->name, strerror ( rc ) );
  346. return rc;
  347. }
  348. if ( prefix_len != prefix_opt->prefix_len ) {
  349. DBGC ( netdev, "NDP %s incorrect SLAAC prefix length "
  350. "%d (expected %d)\n", netdev->name,
  351. prefix_opt->prefix_len, prefix_len );
  352. return -EINVAL;
  353. }
  354. if ( ( rc = ipv6_set_address ( netdev, &address ) ) != 0 ) {
  355. DBGC ( netdev, "NDP %s could not set address %s: %s\n",
  356. netdev->name, inet6_ntoa ( &address ),
  357. strerror ( rc ) );
  358. return rc;
  359. }
  360. }
  361. return 0;
  362. }
  363. /** An NDP option handler */
  364. struct ndp_option_handler {
  365. /** ICMPv6 type */
  366. uint8_t icmp_type;
  367. /** Option type */
  368. uint8_t option_type;
  369. /**
  370. * Handle received option
  371. *
  372. * @v netdev Network device
  373. * @v sin6_src Source socket address
  374. * @v ndp NDP packet
  375. * @v option NDP option
  376. * @ret rc Return status code
  377. */
  378. int ( * rx ) ( struct net_device *netdev, struct sockaddr_in6 *sin6_src,
  379. union ndp_header *ndp, union ndp_option *option,
  380. size_t len );
  381. };
  382. /** NDP option handlers */
  383. static struct ndp_option_handler ndp_option_handlers[] = {
  384. {
  385. .icmp_type = ICMPV6_NEIGHBOUR_SOLICITATION,
  386. .option_type = NDP_OPT_LL_SOURCE,
  387. .rx = ndp_rx_neighbour_solicitation_ll_source,
  388. },
  389. {
  390. .icmp_type = ICMPV6_NEIGHBOUR_ADVERTISEMENT,
  391. .option_type = NDP_OPT_LL_TARGET,
  392. .rx = ndp_rx_neighbour_advertisement_ll_target,
  393. },
  394. {
  395. .icmp_type = ICMPV6_ROUTER_ADVERTISEMENT,
  396. .option_type = NDP_OPT_LL_SOURCE,
  397. .rx = ndp_rx_router_advertisement_ll_source,
  398. },
  399. {
  400. .icmp_type = ICMPV6_ROUTER_ADVERTISEMENT,
  401. .option_type = NDP_OPT_PREFIX,
  402. .rx = ndp_rx_router_advertisement_prefix,
  403. },
  404. };
  405. /**
  406. * Process received NDP option
  407. *
  408. * @v netdev Network device
  409. * @v sin6_src Source socket address
  410. * @v ndp NDP packet
  411. * @v option NDP option
  412. * @v len Option length
  413. * @ret rc Return status code
  414. */
  415. static int ndp_rx_option ( struct net_device *netdev,
  416. struct sockaddr_in6 *sin6_src, union ndp_header *ndp,
  417. union ndp_option *option, size_t len ) {
  418. struct ndp_option_handler *handler;
  419. unsigned int i;
  420. /* Locate a suitable option handler, if any */
  421. for ( i = 0 ; i < ( sizeof ( ndp_option_handlers ) /
  422. sizeof ( ndp_option_handlers[0] ) ) ; i++ ) {
  423. handler = &ndp_option_handlers[i];
  424. if ( ( handler->icmp_type == ndp->icmp.type ) &&
  425. ( handler->option_type == option->header.type ) ) {
  426. return handler->rx ( netdev, sin6_src, ndp,
  427. option, len );
  428. }
  429. }
  430. /* Silently ignore unknown options as per RFC 4861 */
  431. return 0;
  432. }
  433. /**
  434. * Process received NDP packet options
  435. *
  436. * @v netdev Network device
  437. * @v sin6_src Source socket address
  438. * @v ndp NDP header
  439. * @v offset Offset to NDP options
  440. * @v len Length of NDP packet
  441. * @ret rc Return status code
  442. */
  443. static int ndp_rx_options ( struct net_device *netdev,
  444. struct sockaddr_in6 *sin6_src,
  445. union ndp_header *ndp, size_t offset, size_t len ) {
  446. union ndp_option *option;
  447. size_t remaining;
  448. size_t option_len;
  449. int rc;
  450. /* Sanity check */
  451. if ( len < offset ) {
  452. DBGC ( netdev, "NDP %s packet too short at %zd bytes (min %zd "
  453. "bytes)\n", netdev->name, len, offset );
  454. return -EINVAL;
  455. }
  456. /* Search for option */
  457. option = ( ( ( void * ) ndp ) + offset );
  458. remaining = ( len - offset );
  459. while ( remaining ) {
  460. /* Sanity check */
  461. if ( ( remaining < sizeof ( option->header ) ) ||
  462. ( option->header.blocks == 0 ) ||
  463. ( remaining < ( option->header.blocks *
  464. NDP_OPTION_BLKSZ ) ) ) {
  465. DBGC ( netdev, "NDP %s bad option length:\n",
  466. netdev->name );
  467. DBGC_HDA ( netdev, 0, option, remaining );
  468. return -EINVAL;
  469. }
  470. option_len = ( option->header.blocks * NDP_OPTION_BLKSZ );
  471. /* Handle option */
  472. if ( ( rc = ndp_rx_option ( netdev, sin6_src, ndp, option,
  473. option_len ) ) != 0 )
  474. return rc;
  475. /* Move to next option */
  476. option = ( ( ( void * ) option ) + option_len );
  477. remaining -= option_len;
  478. }
  479. return 0;
  480. }
  481. /**
  482. * Process received NDP neighbour solicitation or advertisement
  483. *
  484. * @v iobuf I/O buffer
  485. * @v netdev Network device
  486. * @v sin6_src Source socket address
  487. * @v sin6_dest Destination socket address
  488. * @ret rc Return status code
  489. */
  490. static int ndp_rx_neighbour ( struct io_buffer *iobuf,
  491. struct net_device *netdev,
  492. struct sockaddr_in6 *sin6_src,
  493. struct sockaddr_in6 *sin6_dest __unused ) {
  494. union ndp_header *ndp = iobuf->data;
  495. struct ndp_neighbour_header *neigh = &ndp->neigh;
  496. size_t len = iob_len ( iobuf );
  497. int rc;
  498. /* Process options */
  499. if ( ( rc = ndp_rx_options ( netdev, sin6_src, ndp,
  500. offsetof ( typeof ( *neigh ), option ),
  501. len ) ) != 0 )
  502. goto err_options;
  503. err_options:
  504. free_iob ( iobuf );
  505. return rc;
  506. }
  507. /**
  508. * Process received NDP router advertisement
  509. *
  510. * @v iobuf I/O buffer
  511. * @v netdev Network device
  512. * @v sin6_src Source socket address
  513. * @v sin6_dest Destination socket address
  514. * @ret rc Return status code
  515. */
  516. static int
  517. ndp_rx_router_advertisement ( struct io_buffer *iobuf,
  518. struct net_device *netdev,
  519. struct sockaddr_in6 *sin6_src,
  520. struct sockaddr_in6 *sin6_dest __unused ) {
  521. union ndp_header *ndp = iobuf->data;
  522. struct ndp_router_advertisement_header *radv = &ndp->radv;
  523. size_t len = iob_len ( iobuf );
  524. int rc;
  525. /* Process options */
  526. if ( ( rc = ndp_rx_options ( netdev, sin6_src, ndp,
  527. offsetof ( typeof ( *radv ), option ),
  528. len ) ) != 0 )
  529. goto err_options;
  530. /* Pass to IPv6 autoconfiguration */
  531. if ( ( rc = ipv6conf_rx_router_advertisement ( netdev, radv,
  532. len ) ) != 0 )
  533. goto err_ipv6conf;
  534. err_ipv6conf:
  535. err_options:
  536. free_iob ( iobuf );
  537. return rc;
  538. }
  539. /** NDP ICMPv6 handlers */
  540. struct icmpv6_handler ndp_handlers[] __icmpv6_handler = {
  541. {
  542. .type = ICMPV6_NEIGHBOUR_SOLICITATION,
  543. .rx = ndp_rx_neighbour,
  544. },
  545. {
  546. .type = ICMPV6_NEIGHBOUR_ADVERTISEMENT,
  547. .rx = ndp_rx_neighbour,
  548. },
  549. {
  550. .type = ICMPV6_ROUTER_ADVERTISEMENT,
  551. .rx = ndp_rx_router_advertisement,
  552. },
  553. };
  554. /****************************************************************************
  555. *
  556. * NDP settings
  557. *
  558. */
  559. /** An NDP settings block */
  560. struct ndp_settings {
  561. /** Reference counter */
  562. struct refcnt refcnt;
  563. /** Settings interface */
  564. struct settings settings;
  565. /** Length of NDP options */
  566. size_t len;
  567. /** NDP options */
  568. union ndp_option option[0];
  569. };
  570. /** NDP settings scope */
  571. static const struct settings_scope ndp_settings_scope;
  572. /**
  573. * Construct NDP tag
  574. *
  575. * @v type NDP option type
  576. * @v offset Starting offset of data
  577. * @ret tag NDP tag
  578. */
  579. #define NDP_TAG( type, offset ) ( ( (offset) << 8 ) | (type) )
  580. /**
  581. * Extract NDP tag type
  582. *
  583. * @v tag NDP tag
  584. * @ret type NDP option type
  585. */
  586. #define NDP_TAG_TYPE( tag ) ( (tag) & 0xff )
  587. /**
  588. * Extract NDP tag offset
  589. *
  590. * @v tag NDP tag
  591. * @ret offset Starting offset of data
  592. */
  593. #define NDP_TAG_OFFSET( tag ) ( (tag) >> 8 )
  594. /**
  595. * Check applicability of NDP setting
  596. *
  597. * @v settings Settings block
  598. * @v setting Setting to fetch
  599. * @ret applies Setting applies within this settings block
  600. */
  601. static int ndp_applies ( struct settings *settings __unused,
  602. const struct setting *setting ) {
  603. return ( setting->scope == &ndp_settings_scope );
  604. }
  605. /**
  606. * Fetch value of NDP setting
  607. *
  608. * @v settings Settings block
  609. * @v setting Setting to fetch
  610. * @v data Buffer to fill with setting data
  611. * @v len Length of buffer
  612. * @ret len Length of setting data, or negative error
  613. */
  614. static int ndp_fetch ( struct settings *settings,
  615. struct setting *setting,
  616. void *data, size_t len ) {
  617. struct ndp_settings *ndpset =
  618. container_of ( settings, struct ndp_settings, settings );
  619. struct net_device *netdev =
  620. container_of ( settings->parent, struct net_device,
  621. settings.settings );
  622. union ndp_option *option;
  623. unsigned int type = NDP_TAG_TYPE ( setting->tag );
  624. unsigned int offset = NDP_TAG_OFFSET ( setting->tag );
  625. size_t remaining;
  626. size_t option_len;
  627. size_t payload_len;
  628. /* Scan through NDP options for requested type. We can assume
  629. * that the options are well-formed, otherwise they would have
  630. * been rejected prior to being stored.
  631. */
  632. option = ndpset->option;
  633. remaining = ndpset->len;
  634. while ( remaining ) {
  635. /* Calculate option length */
  636. option_len = ( option->header.blocks * NDP_OPTION_BLKSZ );
  637. /* If this is the requested option, return it */
  638. if ( option->header.type == type ) {
  639. /* Sanity check */
  640. if ( offset > option_len ) {
  641. DBGC ( netdev, "NDP %s option %d too short\n",
  642. netdev->name, type );
  643. return -EINVAL;
  644. }
  645. payload_len = ( option_len - offset );
  646. /* Copy data to output buffer */
  647. if ( len > payload_len )
  648. len = payload_len;
  649. memcpy ( data, ( ( ( void * ) option ) + offset ), len);
  650. return payload_len;
  651. }
  652. /* Move to next option */
  653. option = ( ( ( void * ) option ) + option_len );
  654. remaining -= option_len;
  655. }
  656. return -ENOENT;
  657. }
  658. /** NDP settings operations */
  659. static struct settings_operations ndp_settings_operations = {
  660. .applies = ndp_applies,
  661. .fetch = ndp_fetch,
  662. };
  663. /**
  664. * Register NDP settings
  665. *
  666. * @v netdev Network device
  667. * @v option NDP options
  668. * @v len Length of options
  669. * @ret rc Return status code
  670. */
  671. static int ndp_register_settings ( struct net_device *netdev,
  672. union ndp_option *option, size_t len ) {
  673. struct settings *parent = netdev_settings ( netdev );
  674. struct ndp_settings *ndpset;
  675. int rc;
  676. /* Allocate and initialise structure */
  677. ndpset = zalloc ( sizeof ( *ndpset ) + len );
  678. if ( ! ndpset ) {
  679. rc = -ENOMEM;
  680. goto err_alloc;
  681. }
  682. ref_init ( &ndpset->refcnt, NULL );
  683. settings_init ( &ndpset->settings, &ndp_settings_operations,
  684. &ndpset->refcnt, &ndp_settings_scope );
  685. ndpset->len = len;
  686. memcpy ( ndpset->option, option, len );
  687. /* Register settings */
  688. if ( ( rc = register_settings ( &ndpset->settings, parent,
  689. NDP_SETTINGS_NAME ) ) != 0 )
  690. goto err_register;
  691. err_register:
  692. ref_put ( &ndpset->refcnt );
  693. err_alloc:
  694. return rc;
  695. }
  696. /** DNS server setting */
  697. const struct setting ndp_dns6_setting __setting ( SETTING_IP_EXTRA, dns6 ) = {
  698. .name = "dns6",
  699. .description = "DNS server",
  700. .tag = NDP_TAG ( NDP_OPT_RDNSS,
  701. offsetof ( struct ndp_rdnss_option, addresses ) ),
  702. .type = &setting_type_ipv6,
  703. .scope = &ndp_settings_scope,
  704. };
  705. /****************************************************************************
  706. *
  707. * IPv6 autoconfiguration
  708. *
  709. */
  710. /** An IPv6 configurator */
  711. struct ipv6conf {
  712. /** Reference count */
  713. struct refcnt refcnt;
  714. /** List of configurators */
  715. struct list_head list;
  716. /** Job control interface */
  717. struct interface job;
  718. /** DHCPv6 interface */
  719. struct interface dhcp;
  720. /** Network device being configured */
  721. struct net_device *netdev;
  722. /** Retransmission timer */
  723. struct retry_timer timer;
  724. };
  725. /** List of IPv6 configurators */
  726. static LIST_HEAD ( ipv6confs );
  727. /**
  728. * Free IPv6 configurator
  729. *
  730. * @v refcnt Reference count
  731. */
  732. static void ipv6conf_free ( struct refcnt *refcnt ) {
  733. struct ipv6conf *ipv6conf =
  734. container_of ( refcnt, struct ipv6conf, refcnt );
  735. netdev_put ( ipv6conf->netdev );
  736. free ( ipv6conf );
  737. }
  738. /**
  739. * Identify IPv6 configurator by network device
  740. *
  741. * @v netdev Network device
  742. * @ret ipv6 IPv6 configurator, or NULL
  743. */
  744. static struct ipv6conf * ipv6conf_demux ( struct net_device *netdev ) {
  745. struct ipv6conf *ipv6conf;
  746. list_for_each_entry ( ipv6conf, &ipv6confs, list ) {
  747. if ( ipv6conf->netdev == netdev )
  748. return ipv6conf;
  749. }
  750. return NULL;
  751. }
  752. /**
  753. * Finish IPv6 autoconfiguration
  754. *
  755. * @v ipv6 IPv6 configurator
  756. * @v rc Reason for finishing
  757. */
  758. static void ipv6conf_done ( struct ipv6conf *ipv6conf, int rc ) {
  759. /* Shut down interfaces */
  760. intf_shutdown ( &ipv6conf->job, rc );
  761. intf_shutdown ( &ipv6conf->dhcp, rc );
  762. /* Stop timer */
  763. stop_timer ( &ipv6conf->timer );
  764. /* Remove from list and drop list's reference */
  765. list_del ( &ipv6conf->list );
  766. ref_put ( &ipv6conf->refcnt );
  767. }
  768. /**
  769. * Handle IPv6 configurator timer expiry
  770. *
  771. * @v timer Retry timer
  772. * @v fail Failure indicator
  773. */
  774. static void ipv6conf_expired ( struct retry_timer *timer, int fail ) {
  775. struct ipv6conf *ipv6conf =
  776. container_of ( timer, struct ipv6conf, timer );
  777. /* If we have failed, terminate autoconfiguration */
  778. if ( fail ) {
  779. ipv6conf_done ( ipv6conf, -ETIMEDOUT );
  780. return;
  781. }
  782. /* Otherwise, transmit router solicitation and restart timer */
  783. start_timer ( &ipv6conf->timer );
  784. ndp_tx_router_solicitation ( ipv6conf->netdev );
  785. }
  786. /**
  787. * Handle router advertisement during IPv6 autoconfiguration
  788. *
  789. * @v netdev Network device
  790. * @v radv Router advertisement
  791. * @v len Length of router advertisement
  792. * @ret rc Return status code
  793. *
  794. * This function assumes that the router advertisement is well-formed,
  795. * since it must have already passed through option processing.
  796. */
  797. static int
  798. ipv6conf_rx_router_advertisement ( struct net_device *netdev,
  799. struct ndp_router_advertisement_header *radv,
  800. size_t len ) {
  801. struct ipv6conf *ipv6conf;
  802. size_t option_len;
  803. int stateful;
  804. int rc;
  805. /* Identify IPv6 configurator, if any */
  806. ipv6conf = ipv6conf_demux ( netdev );
  807. if ( ! ipv6conf ) {
  808. /* Not an error; router advertisements are processed
  809. * as a background activity even when no explicit
  810. * autoconfiguration is taking place.
  811. */
  812. return 0;
  813. }
  814. /* If this is not the first solicited router advertisement, ignore it */
  815. if ( ! timer_running ( &ipv6conf->timer ) )
  816. return 0;
  817. /* Stop router solicitation timer */
  818. stop_timer ( &ipv6conf->timer );
  819. /* Register NDP settings */
  820. option_len = ( len - offsetof ( typeof ( *radv ), option ) );
  821. if ( ( rc = ndp_register_settings ( netdev, radv->option,
  822. option_len ) ) != 0 )
  823. return rc;
  824. /* Start DHCPv6 if required */
  825. if ( radv->flags & ( NDP_ROUTER_MANAGED | NDP_ROUTER_OTHER ) ) {
  826. stateful = ( radv->flags & NDP_ROUTER_MANAGED );
  827. if ( ( rc = start_dhcpv6 ( &ipv6conf->dhcp, netdev,
  828. stateful ) ) != 0 ) {
  829. DBGC ( netdev, "NDP %s could not start state%s DHCPv6: "
  830. "%s\n", netdev->name,
  831. ( stateful ? "ful" : "less" ), strerror ( rc ) );
  832. ipv6conf_done ( ipv6conf, rc );
  833. return rc;
  834. }
  835. return 0;
  836. }
  837. /* Otherwise, terminate autoconfiguration */
  838. ipv6conf_done ( ipv6conf, 0 );
  839. return 0;
  840. }
  841. /** IPv6 configurator job interface operations */
  842. static struct interface_operation ipv6conf_job_op[] = {
  843. INTF_OP ( intf_close, struct ipv6conf *, ipv6conf_done ),
  844. };
  845. /** IPv6 configurator job interface descriptor */
  846. static struct interface_descriptor ipv6conf_job_desc =
  847. INTF_DESC ( struct ipv6conf, job, ipv6conf_job_op );
  848. /** IPv6 configurator DHCPv6 interface operations */
  849. static struct interface_operation ipv6conf_dhcp_op[] = {
  850. INTF_OP ( intf_close, struct ipv6conf *, ipv6conf_done ),
  851. };
  852. /** IPv6 configurator DHCPv6 interface descriptor */
  853. static struct interface_descriptor ipv6conf_dhcp_desc =
  854. INTF_DESC ( struct ipv6conf, dhcp, ipv6conf_dhcp_op );
  855. /**
  856. * Start IPv6 autoconfiguration
  857. *
  858. * @v job Job control interface
  859. * @v netdev Network device
  860. * @ret rc Return status code
  861. */
  862. int start_ipv6conf ( struct interface *job, struct net_device *netdev ) {
  863. struct ipv6conf *ipv6conf;
  864. /* Allocate and initialise structure */
  865. ipv6conf = zalloc ( sizeof ( *ipv6conf ) );
  866. if ( ! ipv6conf )
  867. return -ENOMEM;
  868. ref_init ( &ipv6conf->refcnt, ipv6conf_free );
  869. intf_init ( &ipv6conf->job, &ipv6conf_job_desc, &ipv6conf->refcnt );
  870. intf_init ( &ipv6conf->dhcp, &ipv6conf_dhcp_desc, &ipv6conf->refcnt );
  871. timer_init ( &ipv6conf->timer, ipv6conf_expired, &ipv6conf->refcnt );
  872. ipv6conf->netdev = netdev_get ( netdev );
  873. /* Start timer to initiate router solicitation */
  874. start_timer_nodelay ( &ipv6conf->timer );
  875. /* Attach parent interface, transfer reference to list, and return */
  876. intf_plug_plug ( &ipv6conf->job, job );
  877. list_add ( &ipv6conf->list, &ipv6confs );
  878. return 0;
  879. }
  880. /** IPv6 network device configurator */
  881. struct net_device_configurator ipv6_configurator __net_device_configurator = {
  882. .name = "ipv6",
  883. .start = start_ipv6conf,
  884. };