Simplify RX data path.
[people/xl0/gpxe.git] / src / net / ipv4.c
1 #include <string.h>
2 #include <stdint.h>
3 #include <errno.h>
4 #include <byteswap.h>
5 #include <malloc.h>
6 #include <vsprintf.h>
7 #include <gpxe/list.h>
8 #include <gpxe/in.h>
9 #include <gpxe/arp.h>
10 #include <gpxe/if_ether.h>
11 #include <gpxe/pkbuff.h>
12 #include <gpxe/netdevice.h>
13 #include "uip/uip.h"
14 #include <gpxe/ip.h>
15
16 /** @file
17  *
18  * IPv4 protocol
19  *
20  * The gPXE IP stack is currently implemented on top of the uIP
21  * protocol stack.  This file provides wrappers around uIP so that
22  * higher-level protocol implementations do not need to talk directly
23  * to uIP (which has a somewhat baroque API).
24  *
25  */
26
27 struct net_protocol ipv4_protocol;
28
29 /** An IPv4 address/routing table entry */
30 struct ipv4_miniroute {
31         /** List of miniroutes */
32         struct list_head list;
33         /** Network device */
34         struct net_device *netdev;
35         /** IPv4 address */
36         struct in_addr address;
37         /** Subnet mask */
38         struct in_addr netmask;
39         /** Gateway address */
40         struct in_addr gateway;
41 };
42
43 /** List of IPv4 miniroutes */
44 static LIST_HEAD ( miniroutes );
45
46 /**
47  * Add IPv4 interface
48  *
49  * @v netdev    Network device
50  * @v address   IPv4 address
51  * @v netmask   Subnet mask
52  * @v gateway   Gateway address (or @c INADDR_NONE for no gateway)
53  * @ret rc      Return status code
54  *
55  */
56 int add_ipv4_address ( struct net_device *netdev, struct in_addr address,
57                        struct in_addr netmask, struct in_addr gateway ) {
58         struct ipv4_miniroute *miniroute;
59
60         /* Allocate and populate miniroute structure */
61         miniroute = malloc ( sizeof ( *miniroute ) );
62         if ( ! miniroute )
63                 return -ENOMEM;
64         miniroute->netdev = netdev;
65         miniroute->address = address;
66         miniroute->netmask = netmask;
67         miniroute->gateway = gateway;
68         
69         /* Add to end of list if we have a gateway, otherwise to start
70          * of list.
71          */
72         if ( gateway.s_addr != INADDR_NONE ) {
73                 list_add_tail ( &miniroute->list, &miniroutes );
74         } else {
75                 list_add ( &miniroute->list, &miniroutes );
76         }
77         return 0;
78 }
79
80 /**
81  * Remove IPv4 interface
82  *
83  * @v netdev    Network device
84  */
85 void del_ipv4_address ( struct net_device *netdev ) {
86         struct ipv4_miniroute *miniroute;
87
88         list_for_each_entry ( miniroute, &miniroutes, list ) {
89                 if ( miniroute->netdev == netdev ) {
90                         list_del ( &miniroute->list );
91                         break;
92                 }
93         }
94 }
95
96 /**
97  * Transmit packet constructed by uIP
98  *
99  * @v pkb               Packet buffer
100  * @ret rc              Return status code
101  *
102  */
103 int ipv4_uip_tx ( struct pk_buff *pkb ) {
104         struct iphdr *iphdr = pkb->data;
105         struct ipv4_miniroute *miniroute;
106         struct net_device *netdev = NULL;
107         struct in_addr next_hop;
108         struct in_addr source;
109         uint8_t ll_dest_buf[MAX_LL_ADDR_LEN];
110         const uint8_t *ll_dest = ll_dest_buf;
111         int rc;
112
113         /* Use routing table to identify next hop and transmitting netdev */
114         next_hop = iphdr->dest;
115         list_for_each_entry ( miniroute, &miniroutes, list ) {
116                 if ( ( ( ( iphdr->dest.s_addr ^ miniroute->address.s_addr ) &
117                          miniroute->netmask.s_addr ) == 0 ) ||
118                      ( miniroute->gateway.s_addr != INADDR_NONE ) ) {
119                         netdev = miniroute->netdev;
120                         source = miniroute->address;
121                         if ( miniroute->gateway.s_addr != INADDR_NONE )
122                                 next_hop = miniroute->gateway;
123                         break;
124                 }
125         }
126
127         /* Abort if no network device identified */
128         if ( ! netdev ) {
129                 DBG ( "No route to %s\n", inet_ntoa ( iphdr->dest ) );
130                 rc = -EHOSTUNREACH;
131                 goto err;
132         }
133
134         /* Determine link-layer destination address */
135         if ( next_hop.s_addr == INADDR_BROADCAST ) {
136                 /* Broadcast address */
137                 ll_dest = netdev->ll_protocol->ll_broadcast;
138         } else if ( IN_MULTICAST ( next_hop.s_addr ) ) {
139                 /* Special case: IPv4 multicast over Ethernet.  This
140                  * code may need to be generalised once we find out
141                  * what happens for other link layers.
142                  */
143                 uint8_t *next_hop_bytes = ( uint8_t * ) &next_hop;
144                 ll_dest_buf[0] = 0x01;
145                 ll_dest_buf[0] = 0x00;
146                 ll_dest_buf[0] = 0x5e;
147                 ll_dest_buf[3] = next_hop_bytes[1] & 0x7f;
148                 ll_dest_buf[4] = next_hop_bytes[2];
149                 ll_dest_buf[5] = next_hop_bytes[3];
150         } else {
151                 /* Unicast address: resolve via ARP */
152                 if ( ( rc = arp_resolve ( netdev, &ipv4_protocol, &next_hop,
153                                           &source, ll_dest_buf ) ) != 0 ) {
154                         DBG ( "No ARP entry for %s\n",
155                               inet_ntoa ( iphdr->dest ) );
156                         goto err;
157                 }
158         }
159         
160         /* Hand off to link layer */
161         return net_tx ( pkb, netdev, &ipv4_protocol, ll_dest );
162
163  err:
164         free_pkb ( pkb );
165         return rc;
166 }
167
168 /**
169  * Process incoming IP packets
170  *
171  * @v pkb               Packet buffer
172  * @v netdev            Network device
173  * @v ll_source         Link-layer source address
174  * @ret rc              Return status code
175  *
176  * This handles IP packets by handing them off to the uIP protocol
177  * stack.
178  */
179 static int ipv4_rx ( struct pk_buff *pkb, struct net_device *netdev __unused,
180                      const void *ll_source __unused ) {
181
182         /* Transfer to uIP buffer.  Horrendously space-inefficient,
183          * but will do as a proof-of-concept for now.
184          */
185         uip_len = pkb_len ( pkb );
186         memcpy ( uip_buf, pkb->data, uip_len );
187         free_pkb ( pkb );
188
189         /* Hand to uIP for processing */
190         uip_input ();
191         if ( uip_len > 0 ) {
192                 pkb = alloc_pkb ( MAX_LL_HEADER_LEN + uip_len );
193                 if ( ! pkb )
194                         return -ENOMEM;
195                 pkb_reserve ( pkb, MAX_LL_HEADER_LEN );
196                 memcpy ( pkb_put ( pkb, uip_len ), uip_buf, uip_len );
197                 ipv4_uip_tx ( pkb );
198         }
199         return 0;
200 }
201
202 /** 
203  * Check existence of IPv4 address for ARP
204  *
205  * @v netdev            Network device
206  * @v net_addr          Network-layer address
207  * @ret rc              Return status code
208  */
209 static int ipv4_arp_check ( struct net_device *netdev, const void *net_addr ) {
210         const struct in_addr *address = net_addr;
211         struct ipv4_miniroute *miniroute;
212
213         list_for_each_entry ( miniroute, &miniroutes, list ) {
214                 if ( ( miniroute->netdev == netdev ) &&
215                      ( miniroute->address.s_addr == address->s_addr ) ) {
216                         /* Found matching address */
217                         return 0;
218                 }
219         }
220         return -ENOENT;
221 }
222
223 /**
224  * Convert IPv4 address to dotted-quad notation
225  *
226  * @v in        IP address
227  * @ret string  IP address in dotted-quad notation
228  */
229 char * inet_ntoa ( struct in_addr in ) {
230         static char buf[16]; /* "xxx.xxx.xxx.xxx" */
231         uint8_t *bytes = ( uint8_t * ) &in;
232         
233         sprintf ( buf, "%d.%d.%d.%d", bytes[0], bytes[1], bytes[2], bytes[3] );
234         return buf;
235 }
236
237 /**
238  * Transcribe IP address
239  *
240  * @v net_addr  IP address
241  * @ret string  IP address in dotted-quad notation
242  *
243  */
244 static const char * ipv4_ntoa ( const void *net_addr ) {
245         return inet_ntoa ( * ( ( struct in_addr * ) net_addr ) );
246 }
247
248 /** IPv4 protocol */
249 struct net_protocol ipv4_protocol = {
250         .name = "IP",
251         .net_proto = htons ( ETH_P_IP ),
252         .net_addr_len = sizeof ( struct in_addr ),
253         .rx = ipv4_rx,
254         .ntoa = ipv4_ntoa,
255 };
256
257 NET_PROTOCOL ( ipv4_protocol );
258
259 /** IPv4 ARP protocol */
260 struct arp_net_protocol ipv4_arp_protocol = {
261         .net_protocol = &ipv4_protocol,
262         .check = ipv4_arp_check,
263 };
264
265 ARP_NET_PROTOCOL ( ipv4_arp_protocol );