• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 *
ndp_find_entry(struct in6_addr * in6)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
add_ndp_entry(struct net_device * netdev,struct in6_addr * in6,void * ll_addr,int state)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  */
ndp_resolve(struct net_device * netdev,struct in6_addr * dest,struct in6_addr * src,void * dest_ll_addr)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  */
ndp_process_advert(struct io_buffer * iobuf,struct sockaddr_tcpip * st_src __unused,struct sockaddr_tcpip * st_dest __unused)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