summaryrefslogtreecommitdiffstats
path: root/src/net/ndp.c
diff options
context:
space:
mode:
authorNikhil Chandru Rao2006-08-19 17:58:22 +0200
committerNikhil Chandru Rao2006-08-19 17:58:22 +0200
commitd1d334b8e1ec42a16f1454f1f172f1256485fc01 (patch)
tree6895d72d6a0881edb2cf2c27dbec5f7fa8fb9bbb /src/net/ndp.c
parentAdded RX tracing as well. (diff)
downloadipxe-d1d334b8e1ec42a16f1454f1f172f1256485fc01.tar.gz
ipxe-d1d334b8e1ec42a16f1454f1f172f1256485fc01.tar.xz
ipxe-d1d334b8e1ec42a16f1454f1f172f1256485fc01.zip
IP6 support
Diffstat (limited to 'src/net/ndp.c')
-rw-r--r--src/net/ndp.c181
1 files changed, 181 insertions, 0 deletions
diff --git a/src/net/ndp.c b/src/net/ndp.c
new file mode 100644
index 00000000..b72cf76f
--- /dev/null
+++ b/src/net/ndp.c
@@ -0,0 +1,181 @@
+#include <stdint.h>
+#include <string.h>
+#include <byteswap.h>
+#include <errno.h>
+#include <gpxe/if_ether.h>
+#include <gpxe/pkbuff.h>
+#include <gpxe/ndp.h>
+#include <gpxe/icmp6.h>
+#include <gpxe/ip6.h>
+#include <gpxe/netdevice.h>
+
+/** @file
+ *
+ * Neighbour Discovery Protocol
+ *
+ * This file implements address resolution as specified by the neighbour
+ * discovery protocol in RFC2461. This protocol is part of the IPv6 protocol
+ * family.
+ */
+
+/* A neighbour entry */
+struct ndp_entry {
+ /** Target IP6 address */
+ struct in6_addr in6;
+ /** Link layer protocol */
+ struct ll_protocol *ll_protocol;
+ /** Link-layer address */
+ uint8_t ll_addr[MAX_LL_ADDR_LEN];
+ /** State of the neighbour entry */
+ int state;
+};
+
+/** Number of entries in the neighbour cache table */
+#define NUM_NDP_ENTRIES 4
+
+/** The neighbour cache table */
+static struct ndp_entry ndp_table[NUM_NDP_ENTRIES];
+#define ndp_table_end &ndp_table[NUM_NDP_ENTRIES]
+
+static unsigned int next_new_ndp_entry = 0;
+
+/**
+ * Find entry in the neighbour cache
+ *
+ * @v in6 IP6 address
+ */
+static struct ndp_entry *
+ndp_find_entry ( struct in6_addr *in6 ) {
+ struct ndp_entry *ndp;
+
+ for ( ndp = ndp_table ; ndp < ndp_table_end ; ndp++ ) {
+ if ( IP6_EQUAL ( ( *in6 ), ndp->in6 ) &&
+ ( ndp->state != NDP_STATE_INVALID ) ) {
+ return ndp;
+ }
+ }
+ return NULL;
+}
+
+/**
+ * Add NDP entry
+ *
+ * @v netdev Network device
+ * @v in6 IP6 address
+ * @v ll_addr Link-layer address
+ * @v state State of the entry - one of the NDP_STATE_XXX values
+ */
+void add_ndp_entry ( struct net_device *netdev, struct in6_addr *in6,
+ void *ll_addr, int state ) {
+ struct ndp_entry *ndp;
+ ndp = &ndp_table[next_new_ndp_entry++ % NUM_NDP_ENTRIES];
+
+ /* Fill up entry */
+ ndp->ll_protocol = netdev->ll_protocol;
+ memcpy ( &ndp->in6, &( *in6 ), sizeof ( *in6 ) );
+ if ( ll_addr ) {
+ memcpy ( ndp->ll_addr, ll_addr, netdev->ll_protocol->ll_addr_len );
+ } else {
+ memset ( ndp->ll_addr, 0, netdev->ll_protocol->ll_addr_len );
+ }
+ ndp->state = state;
+ DBG ( "New neighbour cache entry (%d): IP6 %s => %s %s\n",
+ ( ndp - ndp_table ) / sizeof ( *ndp ),
+ inet6_ntoa ( ndp->in6 ), netdev->ll_protocol->name,
+ netdev->ll_protocol->ntoa ( ndp->ll_addr ) );
+}
+
+/**
+ * Resolve the link-layer address
+ *
+ * @v netdev Network device
+ * @v dest Destination address
+ * @v src Source address
+ * @ret dest_ll_addr Destination link-layer address or NULL
+ * @ret rc Status
+ *
+ * This function looks up the neighbour cache for an entry corresponding to the
+ * destination address. If it finds a valid entry, it fills up dest_ll_addr and
+ * returns 0. Otherwise it sends a neighbour solicitation to the solicited
+ * multicast address.
+ */
+int ndp_resolve ( struct net_device *netdev, struct in6_addr *dest,
+ struct in6_addr *src, void *dest_ll_addr ) {
+ struct ll_protocol *ll_protocol = netdev->ll_protocol;
+ struct ndp_entry *ndp;
+ int rc;
+
+ ndp = ndp_find_entry ( dest );
+ /* Check if the entry is valid */
+ if ( ndp && ndp->state == NDP_STATE_REACHABLE ) {
+ DBG ( "Neighbour cache hit: IP6 %s => %s %s\n",
+ inet6_ntoa ( *dest ), ll_protocol->name,
+ ll_protocol->ntoa ( ndp->ll_addr ) );
+ memcpy ( dest_ll_addr, ndp->ll_addr, ll_protocol->ll_addr_len );
+ return 0;
+ }
+
+ /* Check if the entry was already created */
+ if ( ndp ) {
+ DBG ( "Awaiting neighbour advertisement (cache entry %d)\n",
+ ( ( ndp - ndp_table ) / sizeof ( *ndp ) ) );
+ /* For test */
+// ndp->state = NDP_STATE_REACHABLE;
+// memcpy ( ndp->ll_addr, netdev->ll_addr, 6 );
+// assert ( ndp->ll_protocol->ll_addr_len == 6 );
+// icmp6_test_nadvert ( netdev, dest, ndp->ll_addr );
+// assert ( ndp->state == NDP_STATE_REACHABLE );
+ /* Take it out till here */
+ return -ENOENT;
+ }
+ DBG ( "Neighbour cache miss: IP6 %s\n", inet6_ntoa ( *dest ) );
+
+ /* Add entry in the neighbour cache */
+ add_ndp_entry ( netdev, dest, NULL, NDP_STATE_INCOMPLETE );
+
+ /* Send neighbour solicitation */
+ if ( ( rc = icmp6_send_solicit ( netdev, src, dest ) ) != 0 ) {
+ return rc;
+ }
+ return -ENOENT;
+}
+
+/**
+ * Process neighbour advertisement
+ *
+ * @v pkb Packet buffer
+ * @v st_src Source address
+ * @v st_dest Destination address
+ */
+int ndp_process_advert ( struct pk_buff *pkb, struct sockaddr_tcpip *st_src,
+ struct sockaddr_tcpip *st_dest ) {
+ struct neighbour_advert *nadvert = pkb->data;
+ struct ndp_entry *ndp;
+
+ /* Sanity check */
+ if ( pkb_len ( pkb ) < sizeof ( *nadvert ) ) {
+ DBG ( "Packet too short (%d bytes)\n", pkb_len ( pkb ) );
+ return -EINVAL;
+ }
+
+ assert ( nadvert->code == 0 );
+ assert ( nadvert->flags & ICMP6_FLAGS_SOLICITED );
+ assert ( nadvert->opt_type == 2 );
+
+ /* Update the neighbour cache, if entry is present */
+ ndp = ndp_find_entry ( &nadvert->target );
+ if ( ndp ) {
+
+ assert ( nadvert->opt_len ==
+ ( ( 2 + ndp->ll_protocol->ll_addr_len ) / 8 ) );
+
+ if ( IP6_EQUAL ( ndp->in6, nadvert->target ) ) {
+ memcpy ( ndp->ll_addr, nadvert->opt_ll_addr,
+ ndp->ll_protocol->ll_addr_len );
+ ndp->state = NDP_STATE_REACHABLE;
+ return 0;
+ }
+ }
+ DBG ( "Unsolicited advertisement (dropping packet)\n" );
+ return 0;
+}