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