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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. #include <stdint.h>
  2. #include <string.h>
  3. #include <byteswap.h>
  4. #include <errno.h>
  5. #include <gpxe/if_ether.h>
  6. #include <gpxe/pkbuff.h>
  7. #include <gpxe/ndp.h>
  8. #include <gpxe/icmp6.h>
  9. #include <gpxe/ip6.h>
  10. #include <gpxe/netdevice.h>
  11. /** @file
  12. *
  13. * Neighbour Discovery Protocol
  14. *
  15. * This file implements address resolution as specified by the neighbour
  16. * discovery protocol in RFC2461. This protocol is part of the IPv6 protocol
  17. * family.
  18. */
  19. /* A neighbour entry */
  20. struct ndp_entry {
  21. /** Target IP6 address */
  22. struct in6_addr in6;
  23. /** Link layer protocol */
  24. struct ll_protocol *ll_protocol;
  25. /** Link-layer address */
  26. uint8_t ll_addr[MAX_LL_ADDR_LEN];
  27. /** State of the neighbour entry */
  28. int state;
  29. };
  30. /** Number of entries in the neighbour cache table */
  31. #define NUM_NDP_ENTRIES 4
  32. /** The neighbour cache table */
  33. static struct ndp_entry ndp_table[NUM_NDP_ENTRIES];
  34. #define ndp_table_end &ndp_table[NUM_NDP_ENTRIES]
  35. static unsigned int next_new_ndp_entry = 0;
  36. /**
  37. * Find entry in the neighbour cache
  38. *
  39. * @v in6 IP6 address
  40. */
  41. static struct ndp_entry *
  42. ndp_find_entry ( struct in6_addr *in6 ) {
  43. struct ndp_entry *ndp;
  44. for ( ndp = ndp_table ; ndp < ndp_table_end ; ndp++ ) {
  45. if ( IP6_EQUAL ( ( *in6 ), ndp->in6 ) &&
  46. ( ndp->state != NDP_STATE_INVALID ) ) {
  47. return ndp;
  48. }
  49. }
  50. return NULL;
  51. }
  52. /**
  53. * Add NDP entry
  54. *
  55. * @v netdev Network device
  56. * @v in6 IP6 address
  57. * @v ll_addr Link-layer address
  58. * @v state State of the entry - one of the NDP_STATE_XXX values
  59. */
  60. void add_ndp_entry ( struct net_device *netdev, struct in6_addr *in6,
  61. void *ll_addr, int state ) {
  62. struct ndp_entry *ndp;
  63. ndp = &ndp_table[next_new_ndp_entry++ % NUM_NDP_ENTRIES];
  64. /* Fill up entry */
  65. ndp->ll_protocol = netdev->ll_protocol;
  66. memcpy ( &ndp->in6, &( *in6 ), sizeof ( *in6 ) );
  67. if ( ll_addr ) {
  68. memcpy ( ndp->ll_addr, ll_addr, netdev->ll_protocol->ll_addr_len );
  69. } else {
  70. memset ( ndp->ll_addr, 0, netdev->ll_protocol->ll_addr_len );
  71. }
  72. ndp->state = state;
  73. DBG ( "New neighbour cache entry (%d): IP6 %s => %s %s\n",
  74. ( ndp - ndp_table ) / sizeof ( *ndp ),
  75. inet6_ntoa ( ndp->in6 ), netdev->ll_protocol->name,
  76. netdev->ll_protocol->ntoa ( ndp->ll_addr ) );
  77. }
  78. /**
  79. * Resolve the link-layer address
  80. *
  81. * @v netdev Network device
  82. * @v dest Destination address
  83. * @v src Source address
  84. * @ret dest_ll_addr Destination link-layer address or NULL
  85. * @ret rc Status
  86. *
  87. * This function looks up the neighbour cache for an entry corresponding to the
  88. * destination address. If it finds a valid entry, it fills up dest_ll_addr and
  89. * returns 0. Otherwise it sends a neighbour solicitation to the solicited
  90. * multicast address.
  91. */
  92. int ndp_resolve ( struct net_device *netdev, struct in6_addr *dest,
  93. struct in6_addr *src, void *dest_ll_addr ) {
  94. struct ll_protocol *ll_protocol = netdev->ll_protocol;
  95. struct ndp_entry *ndp;
  96. int rc;
  97. ndp = ndp_find_entry ( dest );
  98. /* Check if the entry is valid */
  99. if ( ndp && ndp->state == NDP_STATE_REACHABLE ) {
  100. DBG ( "Neighbour cache hit: IP6 %s => %s %s\n",
  101. inet6_ntoa ( *dest ), ll_protocol->name,
  102. ll_protocol->ntoa ( ndp->ll_addr ) );
  103. memcpy ( dest_ll_addr, ndp->ll_addr, ll_protocol->ll_addr_len );
  104. return 0;
  105. }
  106. /* Check if the entry was already created */
  107. if ( ndp ) {
  108. DBG ( "Awaiting neighbour advertisement (cache entry %d)\n",
  109. ( ( ndp - ndp_table ) / sizeof ( *ndp ) ) );
  110. /* For test */
  111. // ndp->state = NDP_STATE_REACHABLE;
  112. // memcpy ( ndp->ll_addr, netdev->ll_addr, 6 );
  113. // assert ( ndp->ll_protocol->ll_addr_len == 6 );
  114. // icmp6_test_nadvert ( netdev, dest, ndp->ll_addr );
  115. // assert ( ndp->state == NDP_STATE_REACHABLE );
  116. /* Take it out till here */
  117. return -ENOENT;
  118. }
  119. DBG ( "Neighbour cache miss: IP6 %s\n", inet6_ntoa ( *dest ) );
  120. /* Add entry in the neighbour cache */
  121. add_ndp_entry ( netdev, dest, NULL, NDP_STATE_INCOMPLETE );
  122. /* Send neighbour solicitation */
  123. if ( ( rc = icmp6_send_solicit ( netdev, src, dest ) ) != 0 ) {
  124. return rc;
  125. }
  126. return -ENOENT;
  127. }
  128. /**
  129. * Process neighbour advertisement
  130. *
  131. * @v pkb Packet buffer
  132. * @v st_src Source address
  133. * @v st_dest Destination address
  134. */
  135. int ndp_process_advert ( struct pk_buff *pkb, struct sockaddr_tcpip *st_src __unused,
  136. struct sockaddr_tcpip *st_dest __unused ) {
  137. struct neighbour_advert *nadvert = pkb->data;
  138. struct ndp_entry *ndp;
  139. /* Sanity check */
  140. if ( pkb_len ( pkb ) < sizeof ( *nadvert ) ) {
  141. DBG ( "Packet too short (%d bytes)\n", pkb_len ( pkb ) );
  142. return -EINVAL;
  143. }
  144. assert ( nadvert->code == 0 );
  145. assert ( nadvert->flags & ICMP6_FLAGS_SOLICITED );
  146. assert ( nadvert->opt_type == 2 );
  147. /* Update the neighbour cache, if entry is present */
  148. ndp = ndp_find_entry ( &nadvert->target );
  149. if ( ndp ) {
  150. assert ( nadvert->opt_len ==
  151. ( ( 2 + ndp->ll_protocol->ll_addr_len ) / 8 ) );
  152. if ( IP6_EQUAL ( ndp->in6, nadvert->target ) ) {
  153. memcpy ( ndp->ll_addr, nadvert->opt_ll_addr,
  154. ndp->ll_protocol->ll_addr_len );
  155. ndp->state = NDP_STATE_REACHABLE;
  156. return 0;
  157. }
  158. }
  159. DBG ( "Unsolicited advertisement (dropping packet)\n" );
  160. return 0;
  161. }