1/* 2 * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>. 3 * 4 * This program is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU General Public License as 6 * published by the Free Software Foundation; either version 2 of the 7 * License, or any later version. 8 * 9 * This program is distributed in the hope that it will be useful, but 10 * WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, write to the Free Software 16 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 17 */ 18 19FILE_LICENCE ( GPL2_OR_LATER ); 20 21#include <stdint.h> 22#include <string.h> 23#include <byteswap.h> 24#include <errno.h> 25#include <gpxe/if_ether.h> 26#include <gpxe/if_arp.h> 27#include <gpxe/iobuf.h> 28#include <gpxe/netdevice.h> 29#include <gpxe/arp.h> 30 31/** @file 32 * 33 * Address Resolution Protocol 34 * 35 * This file implements the address resolution protocol as defined in 36 * RFC826. The implementation is media-independent and 37 * protocol-independent; it is not limited to Ethernet or to IPv4. 38 * 39 */ 40 41/** An ARP cache entry */ 42struct arp_entry { 43 /** Network-layer protocol */ 44 struct net_protocol *net_protocol; 45 /** Link-layer protocol */ 46 struct ll_protocol *ll_protocol; 47 /** Network-layer address */ 48 uint8_t net_addr[MAX_NET_ADDR_LEN]; 49 /** Link-layer address */ 50 uint8_t ll_addr[MAX_LL_ADDR_LEN]; 51}; 52 53/** Number of entries in the ARP cache 54 * 55 * This is a global cache, covering all network interfaces, 56 * network-layer protocols and link-layer protocols. 57 */ 58#define NUM_ARP_ENTRIES 4 59 60/** The ARP cache */ 61static struct arp_entry arp_table[NUM_ARP_ENTRIES]; 62#define arp_table_end &arp_table[NUM_ARP_ENTRIES] 63 64static unsigned int next_new_arp_entry = 0; 65 66struct net_protocol arp_protocol; 67 68/** 69 * Find entry in the ARP cache 70 * 71 * @v ll_protocol Link-layer protocol 72 * @v net_protocol Network-layer protocol 73 * @v net_addr Network-layer address 74 * @ret arp ARP cache entry, or NULL if not found 75 * 76 */ 77static struct arp_entry * 78arp_find_entry ( struct ll_protocol *ll_protocol, 79 struct net_protocol *net_protocol, 80 const void *net_addr ) { 81 struct arp_entry *arp; 82 83 for ( arp = arp_table ; arp < arp_table_end ; arp++ ) { 84 if ( ( arp->ll_protocol == ll_protocol ) && 85 ( arp->net_protocol == net_protocol ) && 86 ( memcmp ( arp->net_addr, net_addr, 87 net_protocol->net_addr_len ) == 0 ) ) 88 return arp; 89 } 90 return NULL; 91} 92 93/** 94 * Look up media-specific link-layer address in the ARP cache 95 * 96 * @v netdev Network device 97 * @v net_protocol Network-layer protocol 98 * @v dest_net_addr Destination network-layer address 99 * @v source_net_addr Source network-layer address 100 * @ret dest_ll_addr Destination link layer address 101 * @ret rc Return status code 102 * 103 * This function will use the ARP cache to look up the link-layer 104 * address for the link-layer protocol associated with the network 105 * device and the given network-layer protocol and addresses. If 106 * found, the destination link-layer address will be filled in in @c 107 * dest_ll_addr. 108 * 109 * If no address is found in the ARP cache, an ARP request will be 110 * transmitted on the specified network device and -ENOENT will be 111 * returned. 112 */ 113int arp_resolve ( struct net_device *netdev, struct net_protocol *net_protocol, 114 const void *dest_net_addr, const void *source_net_addr, 115 void *dest_ll_addr ) { 116 struct ll_protocol *ll_protocol = netdev->ll_protocol; 117 const struct arp_entry *arp; 118 struct io_buffer *iobuf; 119 struct arphdr *arphdr; 120 int rc; 121 122 /* Look for existing entry in ARP table */ 123 arp = arp_find_entry ( ll_protocol, net_protocol, dest_net_addr ); 124 if ( arp ) { 125 DBG ( "ARP cache hit: %s %s => %s %s\n", 126 net_protocol->name, net_protocol->ntoa ( arp->net_addr ), 127 ll_protocol->name, ll_protocol->ntoa ( arp->ll_addr ) ); 128 memcpy ( dest_ll_addr, arp->ll_addr, ll_protocol->ll_addr_len); 129 return 0; 130 } 131 DBG ( "ARP cache miss: %s %s\n", net_protocol->name, 132 net_protocol->ntoa ( dest_net_addr ) ); 133 134 /* Allocate ARP packet */ 135 iobuf = alloc_iob ( MAX_LL_HEADER_LEN + sizeof ( *arphdr ) + 136 2 * ( MAX_LL_ADDR_LEN + MAX_NET_ADDR_LEN ) ); 137 if ( ! iobuf ) 138 return -ENOMEM; 139 iob_reserve ( iobuf, MAX_LL_HEADER_LEN ); 140 141 /* Build up ARP request */ 142 arphdr = iob_put ( iobuf, sizeof ( *arphdr ) ); 143 arphdr->ar_hrd = ll_protocol->ll_proto; 144 arphdr->ar_hln = ll_protocol->ll_addr_len; 145 arphdr->ar_pro = net_protocol->net_proto; 146 arphdr->ar_pln = net_protocol->net_addr_len; 147 arphdr->ar_op = htons ( ARPOP_REQUEST ); 148 memcpy ( iob_put ( iobuf, ll_protocol->ll_addr_len ), 149 netdev->ll_addr, ll_protocol->ll_addr_len ); 150 memcpy ( iob_put ( iobuf, net_protocol->net_addr_len ), 151 source_net_addr, net_protocol->net_addr_len ); 152 memset ( iob_put ( iobuf, ll_protocol->ll_addr_len ), 153 0, ll_protocol->ll_addr_len ); 154 memcpy ( iob_put ( iobuf, net_protocol->net_addr_len ), 155 dest_net_addr, net_protocol->net_addr_len ); 156 157 /* Transmit ARP request */ 158 if ( ( rc = net_tx ( iobuf, netdev, &arp_protocol, 159 netdev->ll_broadcast ) ) != 0 ) 160 return rc; 161 162 return -ENOENT; 163} 164 165/** 166 * Identify ARP protocol 167 * 168 * @v net_proto Network-layer protocol, in network-endian order 169 * @ret arp_net_protocol ARP protocol, or NULL 170 * 171 */ 172static struct arp_net_protocol * arp_find_protocol ( uint16_t net_proto ) { 173 struct arp_net_protocol *arp_net_protocol; 174 175 for_each_table_entry ( arp_net_protocol, ARP_NET_PROTOCOLS ) { 176 if ( arp_net_protocol->net_protocol->net_proto == net_proto ) { 177 return arp_net_protocol; 178 } 179 } 180 return NULL; 181} 182 183/** 184 * Process incoming ARP packets 185 * 186 * @v iobuf I/O buffer 187 * @v netdev Network device 188 * @v ll_source Link-layer source address 189 * @ret rc Return status code 190 * 191 * This handles ARP requests and responses as detailed in RFC826. The 192 * method detailed within the RFC is pretty optimised, handling 193 * requests and responses with basically a single code path and 194 * avoiding the need for extraneous ARP requests; read the RFC for 195 * details. 196 */ 197static int arp_rx ( struct io_buffer *iobuf, struct net_device *netdev, 198 const void *ll_source __unused ) { 199 struct arphdr *arphdr = iobuf->data; 200 struct arp_net_protocol *arp_net_protocol; 201 struct net_protocol *net_protocol; 202 struct ll_protocol *ll_protocol; 203 struct arp_entry *arp; 204 int merge = 0; 205 206 /* Identify network-layer and link-layer protocols */ 207 arp_net_protocol = arp_find_protocol ( arphdr->ar_pro ); 208 if ( ! arp_net_protocol ) 209 goto done; 210 net_protocol = arp_net_protocol->net_protocol; 211 ll_protocol = netdev->ll_protocol; 212 213 /* Sanity checks */ 214 if ( ( arphdr->ar_hrd != ll_protocol->ll_proto ) || 215 ( arphdr->ar_hln != ll_protocol->ll_addr_len ) || 216 ( arphdr->ar_pln != net_protocol->net_addr_len ) ) 217 goto done; 218 219 /* See if we have an entry for this sender, and update it if so */ 220 arp = arp_find_entry ( ll_protocol, net_protocol, 221 arp_sender_pa ( arphdr ) ); 222 if ( arp ) { 223 memcpy ( arp->ll_addr, arp_sender_ha ( arphdr ), 224 arphdr->ar_hln ); 225 merge = 1; 226 DBG ( "ARP cache update: %s %s => %s %s\n", 227 net_protocol->name, net_protocol->ntoa ( arp->net_addr ), 228 ll_protocol->name, ll_protocol->ntoa ( arp->ll_addr ) ); 229 } 230 231 /* See if we own the target protocol address */ 232 if ( arp_net_protocol->check ( netdev, arp_target_pa ( arphdr ) ) != 0) 233 goto done; 234 235 /* Create new ARP table entry if necessary */ 236 if ( ! merge ) { 237 arp = &arp_table[next_new_arp_entry++ % NUM_ARP_ENTRIES]; 238 arp->ll_protocol = ll_protocol; 239 arp->net_protocol = net_protocol; 240 memcpy ( arp->ll_addr, arp_sender_ha ( arphdr ), 241 arphdr->ar_hln ); 242 memcpy ( arp->net_addr, arp_sender_pa ( arphdr ), 243 arphdr->ar_pln); 244 DBG ( "ARP cache add: %s %s => %s %s\n", 245 net_protocol->name, net_protocol->ntoa ( arp->net_addr ), 246 ll_protocol->name, ll_protocol->ntoa ( arp->ll_addr ) ); 247 } 248 249 /* If it's not a request, there's nothing more to do */ 250 if ( arphdr->ar_op != htons ( ARPOP_REQUEST ) ) 251 goto done; 252 253 /* Change request to a reply */ 254 DBG ( "ARP reply: %s %s => %s %s\n", net_protocol->name, 255 net_protocol->ntoa ( arp_target_pa ( arphdr ) ), 256 ll_protocol->name, ll_protocol->ntoa ( netdev->ll_addr ) ); 257 arphdr->ar_op = htons ( ARPOP_REPLY ); 258 memswap ( arp_sender_ha ( arphdr ), arp_target_ha ( arphdr ), 259 arphdr->ar_hln + arphdr->ar_pln ); 260 memcpy ( arp_sender_ha ( arphdr ), netdev->ll_addr, arphdr->ar_hln ); 261 262 /* Send reply */ 263 net_tx ( iob_disown ( iobuf ), netdev, &arp_protocol, 264 arp_target_ha ( arphdr ) ); 265 266 done: 267 free_iob ( iobuf ); 268 return 0; 269} 270 271/** 272 * Transcribe ARP address 273 * 274 * @v net_addr ARP address 275 * @ret string "<ARP>" 276 * 277 * This operation is meaningless for the ARP protocol. 278 */ 279static const char * arp_ntoa ( const void *net_addr __unused ) { 280 return "<ARP>"; 281} 282 283/** ARP protocol */ 284struct net_protocol arp_protocol __net_protocol = { 285 .name = "ARP", 286 .net_proto = htons ( ETH_P_ARP ), 287 .rx = arp_rx, 288 .ntoa = arp_ntoa, 289}; 290