ndp.c

Go to the documentation of this file.
00001 #include <stdint.h>
00002 #include <string.h>
00003 #include <byteswap.h>
00004 #include <errno.h>
00005 #include <gpxe/if_ether.h>
00006 #include <gpxe/iobuf.h>
00007 #include <gpxe/ndp.h>
00008 #include <gpxe/icmp6.h>
00009 #include <gpxe/ip6.h>
00010 #include <gpxe/netdevice.h>
00011 
00012 /** @file
00013  *
00014  * Neighbour Discovery Protocol
00015  *
00016  * This file implements address resolution as specified by the neighbour
00017  * discovery protocol in RFC2461. This protocol is part of the IPv6 protocol
00018  * family.
00019  */
00020 
00021 /* A neighbour entry */
00022 struct ndp_entry {
00023         /** Target IP6 address */
00024         struct in6_addr in6;
00025         /** Link layer protocol */
00026         struct ll_protocol *ll_protocol;
00027         /** Link-layer address */
00028         uint8_t ll_addr[MAX_LL_ADDR_LEN];
00029         /** State of the neighbour entry */
00030         int state;
00031 };
00032 
00033 /** Number of entries in the neighbour cache table */
00034 #define NUM_NDP_ENTRIES 4
00035 
00036 /** The neighbour cache table */
00037 static struct ndp_entry ndp_table[NUM_NDP_ENTRIES];
00038 #define ndp_table_end &ndp_table[NUM_NDP_ENTRIES]
00039 
00040 static unsigned int next_new_ndp_entry = 0;
00041 
00042 /**
00043  * Find entry in the neighbour cache
00044  *
00045  * @v in6       IP6 address
00046  */
00047 static struct ndp_entry *
00048 ndp_find_entry ( struct in6_addr *in6 ) {
00049         struct ndp_entry *ndp;
00050 
00051         for ( ndp = ndp_table ; ndp < ndp_table_end ; ndp++ ) {
00052                 if ( IP6_EQUAL ( ( *in6 ), ndp->in6 ) && 
00053                      ( ndp->state != NDP_STATE_INVALID ) ) {
00054                         return ndp;
00055                 }
00056         }
00057         return NULL;
00058 }
00059 
00060 /**
00061  * Add NDP entry
00062  * 
00063  * @v netdev    Network device
00064  * @v in6       IP6 address
00065  * @v ll_addr   Link-layer address
00066  * @v state     State of the entry - one of the NDP_STATE_XXX values
00067  */
00068 static void 
00069 add_ndp_entry ( struct net_device *netdev, struct in6_addr *in6,
00070                 void *ll_addr, int state ) {
00071         struct ndp_entry *ndp;
00072         ndp = &ndp_table[next_new_ndp_entry++ % NUM_NDP_ENTRIES];
00073 
00074         /* Fill up entry */
00075         ndp->ll_protocol = netdev->ll_protocol;
00076         memcpy ( &ndp->in6, &( *in6 ), sizeof ( *in6 ) );
00077         if ( ll_addr ) {
00078                 memcpy ( ndp->ll_addr, ll_addr, netdev->ll_protocol->ll_addr_len );
00079         } else {
00080                 memset ( ndp->ll_addr, 0, netdev->ll_protocol->ll_addr_len );
00081         }
00082         ndp->state = state;
00083         DBG ( "New neighbour cache entry: IP6 %s => %s %s\n",
00084               inet6_ntoa ( ndp->in6 ), netdev->ll_protocol->name,
00085               netdev->ll_protocol->ntoa ( ndp->ll_addr ) );
00086 }
00087 
00088 /**
00089  * Resolve the link-layer address
00090  *
00091  * @v netdev            Network device
00092  * @v dest              Destination address
00093  * @v src               Source address
00094  * @ret dest_ll_addr    Destination link-layer address or NULL
00095  * @ret rc              Status
00096  *
00097  * This function looks up the neighbour cache for an entry corresponding to the
00098  * destination address. If it finds a valid entry, it fills up dest_ll_addr and
00099  * returns 0. Otherwise it sends a neighbour solicitation to the solicited
00100  * multicast address.
00101  */
00102 int ndp_resolve ( struct net_device *netdev, struct in6_addr *dest,
00103                   struct in6_addr *src, void *dest_ll_addr ) {
00104         struct ll_protocol *ll_protocol = netdev->ll_protocol;
00105         struct ndp_entry *ndp;
00106         int rc;
00107 
00108         ndp = ndp_find_entry ( dest );
00109         /* Check if the entry is valid */
00110         if ( ndp && ndp->state == NDP_STATE_REACHABLE ) {
00111                 DBG ( "Neighbour cache hit: IP6 %s => %s %s\n",
00112                       inet6_ntoa ( *dest ), ll_protocol->name,
00113                       ll_protocol->ntoa ( ndp->ll_addr ) );
00114                 memcpy ( dest_ll_addr, ndp->ll_addr, ll_protocol->ll_addr_len );
00115                 return 0;
00116         }
00117 
00118         /* Check if the entry was already created */
00119         if ( ndp ) {
00120                 DBG ( "Awaiting neighbour advertisement\n" );
00121                 /* For test */
00122 //              ndp->state = NDP_STATE_REACHABLE;
00123 //              memcpy ( ndp->ll_addr, netdev->ll_addr, 6 );
00124 //              assert ( ndp->ll_protocol->ll_addr_len == 6 );
00125 //              icmp6_test_nadvert ( netdev, dest, ndp->ll_addr );
00126 //              assert ( ndp->state == NDP_STATE_REACHABLE );
00127                 /* Take it out till here */
00128                 return -ENOENT;
00129         }
00130         DBG ( "Neighbour cache miss: IP6 %s\n", inet6_ntoa ( *dest ) );
00131 
00132         /* Add entry in the neighbour cache */
00133         add_ndp_entry ( netdev, dest, NULL, NDP_STATE_INCOMPLETE );
00134 
00135         /* Send neighbour solicitation */
00136         if ( ( rc = icmp6_send_solicit ( netdev, src, dest ) ) != 0 ) {
00137                 return rc;
00138         }
00139         return -ENOENT;
00140 }
00141 
00142 /**
00143  * Process neighbour advertisement
00144  *
00145  * @v iobuf     I/O buffer
00146  * @v st_src    Source address
00147  * @v st_dest   Destination address 
00148  */
00149 int ndp_process_advert ( struct io_buffer *iobuf, struct sockaddr_tcpip *st_src __unused,
00150                            struct sockaddr_tcpip *st_dest __unused ) {
00151         struct neighbour_advert *nadvert = iobuf->data;
00152         struct ndp_entry *ndp;
00153 
00154         /* Sanity check */
00155         if ( iob_len ( iobuf ) < sizeof ( *nadvert ) ) {
00156                 DBG ( "Packet too short (%zd bytes)\n", iob_len ( iobuf ) );
00157                 return -EINVAL;
00158         }
00159 
00160         assert ( nadvert->code == 0 );
00161         assert ( nadvert->flags & ICMP6_FLAGS_SOLICITED );
00162         assert ( nadvert->opt_type == 2 );
00163 
00164         /* Update the neighbour cache, if entry is present */
00165         ndp = ndp_find_entry ( &nadvert->target );
00166         if ( ndp ) {
00167 
00168         assert ( nadvert->opt_len ==
00169                         ( ( 2 + ndp->ll_protocol->ll_addr_len ) / 8 ) );
00170 
00171                 if ( IP6_EQUAL ( ndp->in6, nadvert->target ) ) {
00172                         memcpy ( ndp->ll_addr, nadvert->opt_ll_addr,
00173                                  ndp->ll_protocol->ll_addr_len );
00174                         ndp->state = NDP_STATE_REACHABLE;
00175                         return 0;
00176                 }
00177         }
00178         DBG ( "Unsolicited advertisement (dropping packet)\n" );
00179         return 0;
00180 }

Generated on Tue Apr 6 20:01:10 2010 for gPXE by  doxygen 1.5.7.1