Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

ndp.c 28KB

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