Home | History | Annotate | Download | only in net
      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