2 * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
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.
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.
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.
23 #include <gpxe/if_ether.h>
24 #include <gpxe/if_arp.h>
25 #include <gpxe/pkbuff.h>
26 #include <gpxe/netdevice.h>
31 * Address Resolution Protocol
33 * This file implements the address resolution protocol as defined in
34 * RFC826. The implementation is media-independent and
35 * protocol-independent; it is not limited to Ethernet or to IPv4.
39 /** An ARP cache entry */
41 /** Network-layer protocol */
43 /** Link-layer protocol */
45 /** Network-layer address */
46 uint8_t net_addr[MAX_NET_ADDR_LEN];
47 /** Link-layer address */
48 uint8_t ll_addr[MAX_LLH_ADDR_LEN];
51 /** Number of entries in the ARP cache
53 * This is a global cache, covering all network interfaces,
54 * network-layer protocols and link-layer protocols.
56 #define NUM_ARP_ENTRIES 4
59 static struct arp_entry arp_table[NUM_ARP_ENTRIES];
60 #define arp_table_end &arp_table[NUM_ARP_ENTRIES]
62 static unsigned int next_new_arp_entry = 0;
65 * Find entry in the ARP cache
67 * @v ll_proto Link-layer protocol
68 * @v net_proto Network-layer protocol
69 * @v net_addr Network-layer address
70 * @v net_addr_len Network-layer address length
71 * @ret arp ARP cache entry, or NULL if not found
74 static struct arp_entry *
75 arp_find_entry ( uint16_t ll_proto, uint16_t net_proto, const void *net_addr,
76 size_t net_addr_len ) {
77 struct arp_entry *arp;
79 for ( arp = arp_table ; arp < arp_table_end ; arp++ ) {
80 if ( ( arp->ll_proto == ll_proto ) &&
81 ( arp->net_proto == net_proto ) &&
82 ( memcmp ( arp->net_addr, net_addr, net_addr_len ) == 0 ))
89 * Look up media-specific link-layer address in the ARP cache
91 * @v netdev Network device
92 * @v pkb Packet buffer
93 * @ret ll_addr Pointer to link-layer address
94 * @ret rc Return status code
96 * This function will use the ARP cache to look up the link-layer
97 * address for the media corresponding to @c netdev and the
98 * network-layer address as specified in the @c pkb metadata.
100 * If no address is found in the ARP cache, an ARP request will be
101 * transmitted, -ENOENT will be returned, and the packet buffer
102 * contents will be undefined.
104 int arp_resolve ( struct net_device *netdev, struct pk_buff *pkb,
105 const void **ll_addr ) {
106 const struct arp_entry *arp;
107 struct net_interface *netif;
108 struct arphdr *arphdr;
110 /* Look for existing entry in ARP table */
111 arp = arp_find_entry ( netdev->ll_proto, pkb->net_proto,
112 pkb->net_addr, pkb->net_addr_len );
114 *ll_addr = arp->ll_addr;
118 /* Find interface for this protocol */
119 netif = netdev_find_netif ( netdev, pkb->net_proto );
121 return -EAFNOSUPPORT;
123 /* Build up ARP request */
125 arphdr = pkb_put ( pkb, sizeof ( *arphdr ) );
126 arphdr->ar_hrd = netdev->ll_proto;
127 arphdr->ar_hln = netdev->ll_addr_len;
128 arphdr->ar_pro = pkb->net_proto;
129 arphdr->ar_pln = pkb->net_addr_len;
130 arphdr->ar_op = htons ( ARPOP_REQUEST );
131 memcpy ( pkb_put ( pkb, netdev->ll_addr_len ),
132 netdev->ll_addr, netdev->ll_addr_len );
133 memcpy ( pkb_put ( pkb, netif->net_addr_len ),
134 netif->net_addr, netif->net_addr_len );
135 memset ( pkb_put ( pkb, netdev->ll_addr_len ),
136 0xff, netdev->ll_addr_len );
137 memcpy ( pkb_put ( pkb, netif->net_addr_len ),
138 pkb->net_addr, netif->net_addr_len );
140 /* Locate ARP interface and send ARP request */
141 netif = netdev_find_netif ( netdev, htons ( ETH_P_ARP ) );
142 assert ( netif != NULL );
143 netif_send ( netif, pkb );
149 * Process incoming ARP packets
151 * @v arp_netif Network interface for ARP packets
152 * @v pkb Packet buffer
153 * @ret rc Return status code
155 * This handles ARP requests and responses as detailed in RFC826. The
156 * method detailed within the RFC is pretty optimised, handling
157 * requests and responses with basically a single code path and
158 * avoiding the need for extraneous ARP requests; read the RFC for
161 int arp_process ( struct net_interface *arp_netif, struct pk_buff *pkb ) {
162 struct arphdr *arphdr = pkb->data;
163 struct net_device *netdev = arp_netif->netdev;
164 struct net_interface *netif;
165 struct arp_entry *arp;
168 /* Check for correct link-layer protocol and length */
169 if ( ( arphdr->ar_hrd != netdev->ll_proto ) ||
170 ( arphdr->ar_hln != netdev->ll_addr_len ) )
173 /* See if we have an interface for this network-layer protocol */
174 netif = netdev_find_netif ( netdev, arphdr->ar_pro );
177 if ( arphdr->ar_pln != netif->net_addr_len )
180 /* See if we have an entry for this sender, and update it if so */
181 arp = arp_find_entry ( arphdr->ar_hrd, arphdr->ar_pro,
182 arp_sender_pa ( arphdr ), arphdr->ar_pln );
184 memcpy ( arp->ll_addr, arp_sender_ha ( arphdr ),
189 /* See if we are the target protocol address */
190 if ( memcmp ( arp_target_pa ( arphdr ), netif->net_addr,
191 arphdr->ar_pln ) != 0 )
194 /* Create new ARP table entry if necessary */
196 arp = &arp_table[next_new_arp_entry++ % NUM_ARP_ENTRIES];
197 arp->ll_proto = arphdr->ar_hrd;
198 arp->net_proto = arphdr->ar_pro;
199 memcpy ( arp->ll_addr, arp_sender_ha ( arphdr ),
201 memcpy ( arp->net_addr, arp_sender_pa ( arphdr ),
205 /* If it's not a request, there's nothing more to do */
206 if ( arphdr->ar_op != htons ( ARPOP_REQUEST ) )
209 /* Change request to a reply, and send it */
210 arphdr->ar_op = htons ( ARPOP_REPLY );
211 memcpy ( arp_sender_ha ( arphdr ), arp_target_ha ( arphdr ),
212 arphdr->ar_hln + arphdr->ar_pln );
213 memcpy ( arp_target_ha ( arphdr ), netdev->ll_addr, arphdr->ar_hln );
214 memcpy ( arp_target_pa ( arphdr ), netif->net_addr, arphdr->ar_pln );
215 netif_send ( arp_netif, pkb );
221 * Add media-independent link-layer header
223 * @v arp_netif Network interface for ARP packets
224 * @v pkb Packet buffer
225 * @ret rc Return status code
227 int arp_add_llh_metadata ( struct net_interface *arp_netif __unused,
228 struct pk_buff *pkb ) {
229 struct arphdr *arphdr = pkb->data;
231 pkb->net_proto = htons ( ETH_P_ARP );
232 pkb->flags = PKB_FL_RAW_NET_ADDR;
233 pkb->net_addr_len = arphdr->ar_hln;
234 pkb->net_addr = arp_target_ha ( arphdr );