Network API now allows for multiple network devices (although the
[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         struct net_protocol *net_protocol;
43         /** Link-layer protocol */
44         struct ll_protocol *ll_protocol;
45         /** Network-layer address */
46         uint8_t net_addr[MAX_NET_ADDR_LEN];
47         /** Link-layer address */
48         uint8_t ll_addr[MAX_LL_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 struct net_protocol arp_protocol;
65
66 /**
67  * Find entry in the ARP cache
68  *
69  * @v ll_protocol       Link-layer protocol
70  * @v net_protocol      Network-layer protocol
71  * @v net_addr          Network-layer address
72  * @ret arp             ARP cache entry, or NULL if not found
73  *
74  */
75 static struct arp_entry *
76 arp_find_entry ( struct ll_protocol *ll_protocol,
77                  struct net_protocol *net_protocol,
78                  const void *net_addr ) {
79         struct arp_entry *arp;
80
81         for ( arp = arp_table ; arp < arp_table_end ; arp++ ) {
82                 if ( ( arp->ll_protocol == ll_protocol ) &&
83                      ( arp->net_protocol == net_protocol ) &&
84                      ( memcmp ( arp->net_addr, net_addr,
85                                 net_protocol->net_addr_len ) == 0 ) )
86                         return arp;
87         }
88         return NULL;
89 }
90
91 /**
92  * Look up media-specific link-layer address in the ARP cache
93  *
94  * @v nethdr            Generic network-layer header
95  * @ret llhdr           Generic link-layer header
96  * @ret rc              Return status code
97  *
98  * This function will use the ARP cache to look up the link-layer
99  * address for the link-layer protocol specified in @c llhdr and the
100  * network-layer protocol and address as specified in @c nethdr.  If
101  * found, the destination link-layer address will be filled in in @c
102  * llhdr.
103  *
104  * If no address is found in the ARP cache, an ARP request will be
105  * transmitted and -ENOENT will be returned.
106  */
107 int arp_resolve ( const struct net_header *nethdr, struct ll_header *llhdr ) {
108         struct net_protocol *net_protocol = nethdr->net_protocol;
109         struct ll_protocol *ll_protocol = llhdr->ll_protocol;
110         const struct arp_entry *arp;
111         struct pk_buff *pkb;
112         struct arphdr *arphdr;
113         int rc;
114
115         /* Look for existing entry in ARP table */
116         arp = arp_find_entry ( ll_protocol, net_protocol,
117                                nethdr->dest_net_addr );
118         if ( arp ) {
119                 memcpy ( llhdr->dest_ll_addr, arp->ll_addr,
120                          sizeof ( llhdr->dest_ll_addr ) );
121                 return 0;
122         }
123
124         /* Allocate ARP packet */
125         pkb = alloc_pkb ( sizeof ( *arphdr ) +
126                           2 * ( MAX_LL_ADDR_LEN + MAX_NET_ADDR_LEN ) );
127         if ( ! pkb )
128                 return -ENOMEM;
129         pkb->net_protocol = &arp_protocol;
130
131         /* Build up ARP request */
132         arphdr = pkb_put ( pkb, sizeof ( *arphdr ) );
133         arphdr->ar_hrd = ll_protocol->ll_proto;
134         arphdr->ar_hln = ll_protocol->ll_addr_len;
135         arphdr->ar_pro = net_protocol->net_proto;
136         arphdr->ar_pln = net_protocol->net_addr_len;
137         arphdr->ar_op = htons ( ARPOP_REQUEST );
138         memcpy ( pkb_put ( pkb, ll_protocol->ll_addr_len ),
139                  llhdr->source_ll_addr, ll_protocol->ll_addr_len );
140         memcpy ( pkb_put ( pkb, net_protocol->net_addr_len ),
141                  nethdr->source_net_addr, net_protocol->net_addr_len );
142         memset ( pkb_put ( pkb, ll_protocol->ll_addr_len ),
143                  0, ll_protocol->ll_addr_len );
144         memcpy ( pkb_put ( pkb, net_protocol->net_addr_len ),
145                  nethdr->dest_net_addr, net_protocol->net_addr_len );
146
147         /* Transmit ARP request */
148         if ( ( rc = net_transmit ( pkb ) ) != 0 ) {
149                 free_pkb ( pkb );
150                 return rc;
151         }
152
153         return -ENOENT;
154 }
155
156 /**
157  * Process incoming ARP packets
158  *
159  * @v pkb               Packet buffer
160  * @ret rc              Return status code
161  *
162  * This handles ARP requests and responses as detailed in RFC826.  The
163  * method detailed within the RFC is pretty optimised, handling
164  * requests and responses with basically a single code path and
165  * avoiding the need for extraneous ARP requests; read the RFC for
166  * details.
167  */
168 static int arp_rx ( struct pk_buff *pkb ) {
169         struct arphdr *arphdr = pkb->data;
170         struct ll_protocol *ll_protocol;
171         struct net_protocol *net_protocol;
172         struct arp_entry *arp;
173         struct net_device *netdev;
174         int merge = 0;
175
176         /* Identify link-layer and network-layer protocols */
177         ll_protocol = pkb->ll_protocol;
178         net_protocol = net_find_protocol ( arphdr->ar_pro );
179         if ( ! net_protocol )
180                 goto done;
181
182         /* Sanity checks */
183         if ( ( arphdr->ar_hrd != ll_protocol->ll_proto ) ||
184              ( arphdr->ar_hln != ll_protocol->ll_addr_len ) ||
185              ( arphdr->ar_pln != net_protocol->net_addr_len ) )
186                 goto done;
187
188         /* See if we have an entry for this sender, and update it if so */
189         arp = arp_find_entry ( ll_protocol, net_protocol,
190                                arp_sender_pa ( arphdr ) );
191         if ( arp ) {
192                 memcpy ( arp->ll_addr, arp_sender_ha ( arphdr ),
193                          arphdr->ar_hln );
194                 merge = 1;
195         }
196
197         /* See if we own the target protocol address */
198         netdev = net_find_address ( net_protocol, arp_target_pa ( arphdr ) );
199         if ( ! netdev )
200                 goto done;
201         
202         /* Create new ARP table entry if necessary */
203         if ( ! merge ) {
204                 arp = &arp_table[next_new_arp_entry++ % NUM_ARP_ENTRIES];
205                 arp->ll_protocol = ll_protocol;
206                 arp->net_protocol = net_protocol;
207                 memcpy ( arp->ll_addr, arp_sender_ha ( arphdr ),
208                          arphdr->ar_hln );
209                 memcpy ( arp->net_addr, arp_sender_pa ( arphdr ),
210                          arphdr->ar_pln);
211         }
212
213         /* If it's not a request, there's nothing more to do */
214         if ( arphdr->ar_op != htons ( ARPOP_REQUEST ) )
215                 goto done;
216
217         /* Change request to a reply, and send it */
218         arphdr->ar_op = htons ( ARPOP_REPLY );
219         memswap ( arp_sender_ha ( arphdr ), arp_target_ha ( arphdr ),
220                  arphdr->ar_hln + arphdr->ar_pln );
221         memcpy ( arp_target_ha ( arphdr ), netdev->ll_addr, arphdr->ar_hln );
222         if ( net_transmit ( pkb ) == 0 )
223                 pkb = NULL;
224
225  done:
226         free_pkb ( pkb );
227         return 0;
228 }
229
230 /**
231  * Perform ARP network-layer routing
232  *
233  * @v pkb       Packet buffer
234  * @ret source  Network-layer source address
235  * @ret dest    Network-layer destination address
236  * @ret rc      Return status code
237  */
238 static int arp_route ( const struct pk_buff *pkb,
239                        struct net_header *nethdr ) {
240         struct arphdr *arphdr = pkb->data;
241
242         memcpy ( nethdr->source_net_addr, arp_sender_ha ( arphdr ),
243                  arphdr->ar_hln );
244         memcpy ( nethdr->dest_net_addr, arp_target_ha ( arphdr ),
245                  arphdr->ar_hln );
246         nethdr->dest_flags = NETADDR_FL_RAW;
247         if ( arphdr->ar_op == htons ( ARPOP_REQUEST ) )
248                 nethdr->dest_flags |= NETADDR_FL_BROADCAST;
249         
250         return 0;
251 }
252
253 /** ARP protocol */
254 struct net_protocol arp_protocol = {
255         .net_proto = ETH_P_ARP,
256         .rx = arp_rx,
257         .route = arp_route,
258 };
259
260 NET_PROTOCOL ( arp_protocol );