bee7f57732815faa2354d56d8c9633156d8e2abb
[people/oremanj/gpxe.git] / src / net / arp.c
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
19 #include <stdint.h>
20 #include <string.h>
21 #include <byteswap.h>
22 #include <errno.h>
23 #include <gpxe/if_ether.h>
24 #include <gpxe/if_arp.h>
25 #include <gpxe/pkbuff.h>
26 #include <gpxe/netdevice.h>
27 #include <gpxe/arp.h>
28
29 /** @file
30  *
31  * Address Resolution Protocol
32  *
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.
36  *
37  */
38
39 /** An ARP cache entry */
40 struct arp_entry {
41         /** Network-layer protocol */
42         uint16_t net_proto;
43         /** Link-layer protocol */
44         uint16_t ll_proto;
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];
49 };
50
51 /** Number of entries in the ARP cache
52  *
53  * This is a global cache, covering all network interfaces,
54  * network-layer protocols and link-layer protocols.
55  */
56 #define NUM_ARP_ENTRIES 4
57
58 /** The ARP cache */
59 static struct arp_entry arp_table[NUM_ARP_ENTRIES];
60 #define arp_table_end &arp_table[NUM_ARP_ENTRIES]
61
62 static unsigned int next_new_arp_entry = 0;
63
64 /**
65  * Find entry in the ARP cache
66  *
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
72  *
73  */
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;
78
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 ))
83                         return arp;
84         }
85         return NULL;
86 }
87
88 /**
89  * Look up media-specific link-layer address in the ARP cache
90  *
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
95  *
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.
99  *
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.
103  */
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;
109
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 );
113         if ( arp ) {
114                 *ll_addr = arp->ll_addr;
115                 return 0;
116         }
117
118         /* Find interface for this protocol */
119         netif = netdev_find_netif ( netdev, pkb->net_proto );
120         if ( ! netif )
121                 return -EAFNOSUPPORT;
122
123         /* Build up ARP request */
124         pkb_empty ( pkb );
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 );
139
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 );
144
145         return -ENOENT;
146 }
147
148 /**
149  * Process incoming ARP packets
150  *
151  * @v arp_netif         Network interface for ARP packets
152  * @v pkb               Packet buffer
153  * @ret rc              Return status code
154  *
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
159  * details.
160  */
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;
166         int merge = 0;
167
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 ) )
171                 return 0;
172
173         /* See if we have an interface for this network-layer protocol */
174         netif = netdev_find_netif ( netdev, arphdr->ar_pro );
175         if ( ! netif )
176                 return 0;
177         if ( arphdr->ar_pln != netif->net_addr_len )
178                 return 0;
179
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 );
183         if ( arp ) {
184                 memcpy ( arp->ll_addr, arp_sender_ha ( arphdr ),
185                          arphdr->ar_hln );
186                 merge = 1;
187         }
188
189         /* See if we are the target protocol address */
190         if ( memcmp ( arp_target_pa ( arphdr ), netif->net_addr,
191                       arphdr->ar_pln ) != 0 )
192                 return 0;
193
194         /* Create new ARP table entry if necessary */
195         if ( ! merge ) {
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 ),
200                          arphdr->ar_hln );
201                 memcpy ( arp->net_addr, arp_sender_pa ( arphdr ),
202                          arphdr->ar_pln );
203         }
204
205         /* If it's not a request, there's nothing more to do */
206         if ( arphdr->ar_op != htons ( ARPOP_REQUEST ) )
207                 return 0;
208
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 );
216
217         return 0;
218 }
219
220 /**
221  * Add media-independent link-layer header
222  *
223  * @v arp_netif         Network interface for ARP packets
224  * @v pkb               Packet buffer
225  * @ret rc              Return status code
226  */
227 int arp_add_llh_metadata ( struct net_interface *arp_netif __unused,
228                            struct pk_buff *pkb ) {
229         struct arphdr *arphdr = pkb->data;
230
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 );
235         
236         return 0;
237 }