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