10 #include <gpxe/if_ether.h>
11 #include <gpxe/pkbuff.h>
12 #include <gpxe/netdevice.h>
15 #include <gpxe/interface.h>
21 * The gPXE IP stack is currently implemented on top of the uIP
22 * protocol stack. This file provides wrappers around uIP so that
23 * higher-level protocol implementations do not need to talk directly
24 * to uIP (which has a somewhat baroque API).
28 /* Unique IP datagram identification number */
29 static uint16_t next_ident = 0;
31 struct net_protocol ipv4_protocol;
33 /** An IPv4 address/routing table entry */
34 struct ipv4_miniroute {
35 /** List of miniroutes */
36 struct list_head list;
38 struct net_device *netdev;
40 struct in_addr address;
42 struct in_addr netmask;
43 /** Gateway address */
44 struct in_addr gateway;
47 /** List of IPv4 miniroutes */
48 static LIST_HEAD ( miniroutes );
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
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;
64 /* Allocate and populate miniroute structure */
65 miniroute = malloc ( sizeof ( *miniroute ) );
68 miniroute->netdev = netdev;
69 miniroute->address = address;
70 miniroute->netmask = netmask;
71 miniroute->gateway = gateway;
73 /* Add to end of list if we have a gateway, otherwise to start
76 if ( gateway.s_addr != INADDR_NONE ) {
77 list_add_tail ( &miniroute->list, &miniroutes );
79 list_add ( &miniroute->list, &miniroutes );
85 * Remove IPv4 interface
87 * @v netdev Network device
89 void del_ipv4_address ( struct net_device *netdev ) {
90 struct ipv4_miniroute *miniroute;
92 list_for_each_entry ( miniroute, &miniroutes, list ) {
93 if ( miniroute->netdev == netdev ) {
94 list_del ( &miniroute->list );
101 * Dump IPv4 packet header
103 * @v iphdr IPv4 header
105 static void ipv4_dump ( struct iphdr *iphdr __unused ) {
106 DBG ( "IP4 header at %p+%zx\n", iphdr, sizeof ( *iphdr ) );
107 DBG ( "\tVersion = %d\n", ( iphdr->verhdrlen & IP_MASK_VER ) / 16 );
108 DBG ( "\tHeader length = %d\n", iphdr->verhdrlen & IP_MASK_HLEN );
109 DBG ( "\tService = %d\n", iphdr->service );
110 DBG ( "\tTotal length = %d\n", iphdr->len );
111 DBG ( "\tIdent = %d\n", iphdr->ident );
112 DBG ( "\tFrags/Offset = %d\n", iphdr->frags );
113 DBG ( "\tIP TTL = %d\n", iphdr->ttl );
114 DBG ( "\tProtocol = %d\n", iphdr->protocol );
115 DBG ( "\tHeader Checksum (at %p) = %x\n", &iphdr->chksum,
117 DBG ( "\tSource = %s\n", inet_ntoa ( iphdr->src ) );
118 DBG ( "\tDestination = %s\n", inet_ntoa ( iphdr->dest ) );
122 * Complete the transport-layer checksum
124 * Refer to the note made in net/interface.c about this function
126 void ipv4_tx_csum ( struct pk_buff *pkb, uint8_t trans_proto ) {
128 struct iphdr *iphdr = pkb->data;
129 void *pshdr = malloc ( IP_PSHLEN );
130 void *csum_offset = iphdr + IP_HLEN + ( trans_proto == IP_UDP ? 6 : 16 );
133 /* Calculate pseudo header */
134 memcpy ( pshdr, &iphdr->src, sizeof ( in_addr ) );
135 offset += sizeof ( in_addr );
136 memcpy ( pshdr + offset, &iphdr->dest, sizeof ( in_addr ) );
137 offset += sizeof ( in_addr );
138 *( ( uint8_t* ) ( pshdr + offset++ ) ) = 0x00;
139 *( ( uint8_t* ) ( pshdr + offset++ ) ) = iphdr->protocol;
140 *( ( uint16_t* ) ( pshdr + offset ) ) = pkb_len ( pkb ) - IP_HLEN;
142 /* Update the checksum value */
143 *( ( uint16_t* ) csum_offset ) = *( ( uint16_t* ) csum_offset ) + calc_chksum ( pshdr, IP_PSHLEN );
147 * Calculate the transport-layer checksum while processing packets
149 uint16_t ipv4_rx_csum ( struct pk_buff *pkb __unused, uint8_t trans_proto __unused ) {
150 /** This function needs to be implemented. Until then, it will return 0xffffffff every time */
155 * Transmit packet constructed by uIP
157 * @v pkb Packet buffer
158 * @ret rc Return status code
161 int ipv4_uip_tx ( struct pk_buff *pkb ) {
162 struct iphdr *iphdr = pkb->data;
163 struct ipv4_miniroute *miniroute;
164 struct net_device *netdev = NULL;
165 struct in_addr next_hop;
166 struct in_addr source;
167 uint8_t ll_dest_buf[MAX_LL_ADDR_LEN];
168 const uint8_t *ll_dest = ll_dest_buf;
171 /* Use routing table to identify next hop and transmitting netdev */
172 next_hop = iphdr->dest;
173 list_for_each_entry ( miniroute, &miniroutes, list ) {
174 if ( ( ( ( iphdr->dest.s_addr ^ miniroute->address.s_addr ) &
175 miniroute->netmask.s_addr ) == 0 ) ||
176 ( miniroute->gateway.s_addr != INADDR_NONE ) ) {
177 netdev = miniroute->netdev;
178 source = miniroute->address;
179 if ( miniroute->gateway.s_addr != INADDR_NONE )
180 next_hop = miniroute->gateway;
185 /* Abort if no network device identified */
187 DBG ( "No route to %s\n", inet_ntoa ( iphdr->dest ) );
192 /* Determine link-layer destination address */
193 if ( next_hop.s_addr == INADDR_BROADCAST ) {
194 /* Broadcast address */
195 ll_dest = netdev->ll_protocol->ll_broadcast;
196 } else if ( IN_MULTICAST ( next_hop.s_addr ) ) {
197 /* Special case: IPv4 multicast over Ethernet. This
198 * code may need to be generalised once we find out
199 * what happens for other link layers.
201 uint8_t *next_hop_bytes = ( uint8_t * ) &next_hop;
202 ll_dest_buf[0] = 0x01;
203 ll_dest_buf[0] = 0x00;
204 ll_dest_buf[0] = 0x5e;
205 ll_dest_buf[3] = next_hop_bytes[1] & 0x7f;
206 ll_dest_buf[4] = next_hop_bytes[2];
207 ll_dest_buf[5] = next_hop_bytes[3];
209 /* Unicast address: resolve via ARP */
210 if ( ( rc = arp_resolve ( netdev, &ipv4_protocol, &next_hop,
211 &source, ll_dest_buf ) ) != 0 ) {
212 DBG ( "No ARP entry for %s\n",
213 inet_ntoa ( iphdr->dest ) );
218 /* Hand off to link layer */
219 return net_tx ( pkb, netdev, &ipv4_protocol, ll_dest );
227 * Transmit IP packet (without uIP)
229 * @v pkb Packet buffer
230 * @v trans_proto Transport-layer protocol number
231 * @v dest Destination network-layer address
234 * This function expects a transport-layer segment and prepends the IP header
236 int ipv4_tx ( struct pk_buff *pkb, uint16_t trans_proto, struct in_addr *dest ) {
237 struct iphdr *iphdr = pkb_push ( pkb, IP_HLEN );
238 struct ipv4_miniroute *miniroute;
239 struct net_device *netdev = NULL;
240 struct in_addr next_hop;
241 struct in_addr source;
242 uint8_t ll_dest_buf[MAX_LL_ADDR_LEN];
243 const uint8_t *ll_dest = ll_dest_buf;
246 /* Fill up the IP header, except source address */
247 iphdr->verhdrlen = ( IP_VER << 4 ) | ( IP_HLEN / 4 ); /* Version = 4, Header length = 5 */
248 iphdr->service = IP_TOS; /* Service = 0, is not implemented */
249 iphdr->len = htons ( pkb_len ( pkb ) ); /* Total packet length, in network byte order */
250 iphdr->ident = next_ident++; /* Identification number */
251 iphdr->frags = 0; /* Fragmentation is not implemented at the host */
252 iphdr->ttl = IP_TTL; /* Time to live */
253 iphdr->protocol = trans_proto; /* Transport-layer protocol - IP_XXX */
255 /* Calculate header checksum, in network byte order */
257 iphdr->chksum = htons ( calc_chksum ( iphdr, IP_HLEN ) );
258 /* Copy destination address */
259 memcpy ( &iphdr->dest, dest, sizeof ( struct in_addr ) );
262 * All fields in the IP header filled in except the source network address (which requires routing). As
263 * the pseudo header requires the source address as well and updating the transport-layer checksum is
264 * done after routing.
266 * Continue processing as in ipv4_uip_tx()
269 /* Use routing table to identify next hop and transmitting netdev */
270 next_hop = iphdr->dest;
271 list_for_each_entry ( miniroute, &miniroutes, list ) {
272 if ( ( ( ( iphdr->dest.s_addr ^ miniroute->address.s_addr ) &
273 miniroute->netmask.s_addr ) == 0 ) ||
274 ( miniroute->gateway.s_addr != INADDR_NONE ) ) {
275 netdev = miniroute->netdev;
276 source = miniroute->address;
277 if ( miniroute->gateway.s_addr != INADDR_NONE )
278 next_hop = miniroute->gateway;
282 /* Abort if no network device identified */
284 DBG ( "No route to %s\n", inet_ntoa ( iphdr->dest ) );
289 /* Copy the source address, after this the IP header is complete */
290 memcpy ( &iphdr->src, &source, sizeof ( struct in_addr ) );
291 /* Calculate the transport layer checksum */
292 ipv4_tx_csum ( pkb, trans_proto );
294 /* Print IP4 header for debugging */
297 /* Determine link-layer destination address */
298 if ( next_hop.s_addr == INADDR_BROADCAST ) {
299 /* Broadcast address */
300 ll_dest = netdev->ll_protocol->ll_broadcast;
301 } else if ( IN_MULTICAST ( next_hop.s_addr ) ) {
302 /* Special case: IPv4 multicast over Ethernet. This
303 * code may need to be generalised once we find out
304 * what happens for other link layers.
306 uint8_t *next_hop_bytes = ( uint8_t * ) &next_hop;
307 ll_dest_buf[0] = 0x01;
308 ll_dest_buf[0] = 0x00;
309 ll_dest_buf[0] = 0x5e;
310 ll_dest_buf[3] = next_hop_bytes[1] & 0x7f;
311 ll_dest_buf[4] = next_hop_bytes[2];
312 ll_dest_buf[5] = next_hop_bytes[3];
314 /* Unicast address: resolve via ARP */
315 if ( ( rc = arp_resolve ( netdev, &ipv4_protocol, &next_hop, &source, ll_dest_buf ) ) != 0 ) {
316 DBG ( "No ARP entry for %s\n", inet_ntoa ( iphdr->dest ) );
321 /* Hand off to link layer */
322 return net_tx ( pkb, netdev, &ipv4_protocol, ll_dest );
325 /* Warning: Allowing this function to execute causes bochs to go into an infinite loop */
331 * Process incoming IP packets
333 * @v pkb Packet buffer
334 * @v netdev Network device
335 * @v ll_source Link-layer source address
336 * @ret rc Return status code
338 * This handles IP packets by handing them off to the uIP protocol
341 static int ipv4_uip_rx ( struct pk_buff *pkb,
342 struct net_device *netdev __unused,
343 const void *ll_source __unused ) {
345 /* Transfer to uIP buffer. Horrendously space-inefficient,
346 * but will do as a proof-of-concept for now.
348 uip_len = pkb_len ( pkb );
349 memcpy ( uip_buf, pkb->data, uip_len );
352 /* Hand to uIP for processing */
355 pkb = alloc_pkb ( MAX_LL_HEADER_LEN + uip_len );
358 pkb_reserve ( pkb, MAX_LL_HEADER_LEN );
359 memcpy ( pkb_put ( pkb, uip_len ), uip_buf, uip_len );
366 * Process incoming packets (without uIP)
368 * @v pkb Packet buffer
369 * @v netdev Network device
370 * @v ll_source Link-layer destination source
372 * This function expects an IP4 network datagram. It will process the headers and send it to the transport layer
374 void ipv4_rx ( struct pk_buff *pkb, struct net_device *netdev __unused,
375 const void *ll_source __unused ) {
376 struct iphdr *iphdr = pkb->data;
377 struct in_addr *src = &iphdr->src;
378 struct in_addr *dest = &iphdr->dest;
381 /* Print IP4 header for debugging */
384 /* Process headers */
385 if ( iphdr->verhdrlen != 0x45 ) {
386 DBG ( "Bad version and header length %x\n", iphdr->verhdrlen );
390 if ( iphdr->len != pkb_len ( pkb ) ) {
391 DBG ( "Bad total length %d\n", iphdr->len );
395 if ( ( chksum = ipv4_rx_csum ( pkb, iphdr->protocol ) ) != 0xffff ) {
396 DBG ( "Bad checksum %x\n", chksum );
399 /* Todo: Compute and verify the header checksum */
401 /* To reduce code size, the following functions are not implemented:
402 * 1. Check the destination address
403 * 2. Check the TTL field
404 * 3. Check the service field
408 pkb_pull ( pkb, IP_HLEN );
410 /* Send it to the transport layer */
411 trans_rx ( pkb, iphdr->protocol, src, dest );
415 * Check existence of IPv4 address for ARP
417 * @v netdev Network device
418 * @v net_addr Network-layer address
419 * @ret rc Return status code
421 static int ipv4_arp_check ( struct net_device *netdev, const void *net_addr ) {
422 const struct in_addr *address = net_addr;
423 struct ipv4_miniroute *miniroute;
425 list_for_each_entry ( miniroute, &miniroutes, list ) {
426 if ( ( miniroute->netdev == netdev ) &&
427 ( miniroute->address.s_addr == address->s_addr ) ) {
428 /* Found matching address */
436 * Convert IPv4 address to dotted-quad notation
439 * @ret string IP address in dotted-quad notation
441 char * inet_ntoa ( struct in_addr in ) {
442 static char buf[16]; /* "xxx.xxx.xxx.xxx" */
443 uint8_t *bytes = ( uint8_t * ) ∈
445 sprintf ( buf, "%d.%d.%d.%d", bytes[0], bytes[1], bytes[2], bytes[3] );
450 * Transcribe IP address
452 * @v net_addr IP address
453 * @ret string IP address in dotted-quad notation
456 static const char * ipv4_ntoa ( const void *net_addr ) {
457 return inet_ntoa ( * ( ( struct in_addr * ) net_addr ) );
461 struct net_protocol ipv4_protocol = {
463 .net_proto = htons ( ETH_P_IP ),
464 .net_addr_len = sizeof ( struct in_addr ),
473 NET_PROTOCOL ( ipv4_protocol );
475 /** IPv4 ARP protocol */
476 struct arp_net_protocol ipv4_arp_protocol = {
477 .net_protocol = &ipv4_protocol,
478 .check = ipv4_arp_check,
481 ARP_NET_PROTOCOL ( ipv4_arp_protocol );