|
@@ -12,6 +12,7 @@
|
12
|
12
|
#include <gpxe/netdevice.h>
|
13
|
13
|
#include "uip/uip.h"
|
14
|
14
|
#include <gpxe/ip.h>
|
|
15
|
+#include <gpxe/interface.h>
|
15
|
16
|
|
16
|
17
|
/** @file
|
17
|
18
|
*
|
|
@@ -24,6 +25,9 @@
|
24
|
25
|
*
|
25
|
26
|
*/
|
26
|
27
|
|
|
28
|
+/* Unique IP datagram identification number */
|
|
29
|
+static uint16_t next_ident = 0;
|
|
30
|
+
|
27
|
31
|
struct net_protocol ipv4_protocol;
|
28
|
32
|
|
29
|
33
|
/** An IPv4 address/routing table entry */
|
|
@@ -93,6 +97,39 @@ void del_ipv4_address ( struct net_device *netdev ) {
|
93
|
97
|
}
|
94
|
98
|
}
|
95
|
99
|
|
|
100
|
+/**
|
|
101
|
+ * Complete the transport-layer checksum
|
|
102
|
+ *
|
|
103
|
+ * Refer to the note made in net/interface.c about this function
|
|
104
|
+ */
|
|
105
|
+void ipv4_tx_csum ( struct pk_buff *pkb, uint8_t trans_proto ) {
|
|
106
|
+
|
|
107
|
+ struct iphdr *iphdr = pkb->data;
|
|
108
|
+ void *pshdr = malloc ( IP_PSHLEN );
|
|
109
|
+ void *csum_offset = iphdr + IP_HLEN + ( trans_proto == IP_UDP ? 6 : 16 );
|
|
110
|
+ int offset = 0;
|
|
111
|
+
|
|
112
|
+ /* Calculate pseudo header */
|
|
113
|
+ memcpy ( pshdr, &iphdr->src, sizeof ( in_addr ) );
|
|
114
|
+ offset += sizeof ( in_addr );
|
|
115
|
+ memcpy ( pshdr + offset, &iphdr->dest, sizeof ( in_addr ) );
|
|
116
|
+ offset += sizeof ( in_addr );
|
|
117
|
+ *( ( uint8_t* ) ( pshdr + offset++ ) ) = 0x00;
|
|
118
|
+ *( ( uint8_t* ) ( pshdr + offset++ ) ) = iphdr->protocol;
|
|
119
|
+ *( ( uint16_t* ) ( pshdr + offset ) ) = pkb_len ( pkb ) - IP_HLEN;
|
|
120
|
+
|
|
121
|
+ /* Update the checksum value */
|
|
122
|
+ *( ( uint16_t* ) csum_offset ) = *( ( uint16_t* ) csum_offset ) + calc_chksum ( pshdr, IP_PSHLEN );
|
|
123
|
+}
|
|
124
|
+
|
|
125
|
+/**
|
|
126
|
+ * Calculate the transport-layer checksum while processing packets
|
|
127
|
+ */
|
|
128
|
+uint16_t ipv4_rx_csum ( struct pk_buff *pkb __unused, uint8_t trans_proto __unused ) {
|
|
129
|
+ /** This function needs to be implemented. Until then, it will return 0xffffffff every time */
|
|
130
|
+ return 0xffff;
|
|
131
|
+}
|
|
132
|
+
|
96
|
133
|
/**
|
97
|
134
|
* Transmit packet constructed by uIP
|
98
|
135
|
*
|
|
@@ -165,6 +202,132 @@ int ipv4_uip_tx ( struct pk_buff *pkb ) {
|
165
|
202
|
return rc;
|
166
|
203
|
}
|
167
|
204
|
|
|
205
|
+/**
|
|
206
|
+ * Transmit IP packet (without uIP)
|
|
207
|
+ *
|
|
208
|
+ * @v pkb Packet buffer
|
|
209
|
+ * @v trans_proto Transport-layer protocol number
|
|
210
|
+ * @v dest Destination network-layer address
|
|
211
|
+ * @ret rc Status
|
|
212
|
+ *
|
|
213
|
+ * This function expects a transport-layer segment and prepends the IP header
|
|
214
|
+ */
|
|
215
|
+int ipv4_tx ( struct pk_buff *pkb, uint16_t trans_proto, struct in_addr *dest ) {
|
|
216
|
+ struct iphdr *iphdr = pkb_push ( pkb, IP_HLEN );
|
|
217
|
+ struct ipv4_miniroute *miniroute;
|
|
218
|
+ struct net_device *netdev = NULL;
|
|
219
|
+ struct in_addr next_hop;
|
|
220
|
+ struct in_addr source;
|
|
221
|
+ uint8_t ll_dest_buf[MAX_LL_ADDR_LEN];
|
|
222
|
+ const uint8_t *ll_dest = ll_dest_buf;
|
|
223
|
+ int rc;
|
|
224
|
+
|
|
225
|
+ /* Fill up the IP header, except source address */
|
|
226
|
+ iphdr->verhdrlen = ( IP_VER << 4 ) | ( IP_HLEN / 4 ); /* Version = 4, Header length = 5 */
|
|
227
|
+ iphdr->service = IP_TOS; /* Service = 0, is not implemented */
|
|
228
|
+ iphdr->len = htons ( pkb_len ( pkb ) ); /* Total packet length, in network byte order */
|
|
229
|
+ iphdr->ident = next_ident++; /* Identification number */
|
|
230
|
+ iphdr->frags = 0; /* Fragmentation is not implemented at the host */
|
|
231
|
+ iphdr->ttl = IP_TTL; /* Time to live */
|
|
232
|
+ iphdr->protocol = trans_proto; /* Transport-layer protocol - IP_XXX */
|
|
233
|
+
|
|
234
|
+ /* Calculate header checksum, in network byte order */
|
|
235
|
+ iphdr->chksum = 0;
|
|
236
|
+ iphdr->chksum = htons ( calc_chksum ( iphdr, IP_HLEN ) );
|
|
237
|
+ /* Copy destination address */
|
|
238
|
+ memcpy ( &iphdr->dest, dest, sizeof ( struct in_addr ) );
|
|
239
|
+
|
|
240
|
+ /**
|
|
241
|
+ * All fields in the IP header filled in except the source network address (which requires routing). As
|
|
242
|
+ * the pseudo header requires the source address as well and updating the transport-layer checksum is
|
|
243
|
+ * done after routing.
|
|
244
|
+ *
|
|
245
|
+ * Continue processing as in ipv4_uip_tx()
|
|
246
|
+ */
|
|
247
|
+
|
|
248
|
+ /* Use routing table to identify next hop and transmitting netdev */
|
|
249
|
+ next_hop = iphdr->dest;
|
|
250
|
+ list_for_each_entry ( miniroute, &miniroutes, list ) {
|
|
251
|
+ if ( ( ( ( iphdr->dest.s_addr ^ miniroute->address.s_addr ) &
|
|
252
|
+ miniroute->netmask.s_addr ) == 0 ) ||
|
|
253
|
+ ( miniroute->gateway.s_addr != INADDR_NONE ) ) {
|
|
254
|
+ netdev = miniroute->netdev;
|
|
255
|
+ source = miniroute->address;
|
|
256
|
+ if ( miniroute->gateway.s_addr != INADDR_NONE )
|
|
257
|
+ next_hop = miniroute->gateway;
|
|
258
|
+ break;
|
|
259
|
+ }
|
|
260
|
+ }
|
|
261
|
+ /* Abort if no network device identified */
|
|
262
|
+ if ( ! netdev ) {
|
|
263
|
+ DBG ( "No route to %s\n", inet_ntoa ( iphdr->dest ) );
|
|
264
|
+ rc = -EHOSTUNREACH;
|
|
265
|
+ goto err;
|
|
266
|
+ }
|
|
267
|
+
|
|
268
|
+ /* Copy the source address, after this the IP header is complete */
|
|
269
|
+ memcpy ( &iphdr->src, &source, sizeof ( struct in_addr ) );
|
|
270
|
+ /* Calculate the transport layer checksum */
|
|
271
|
+ ipv4_tx_csum ( pkb, trans_proto );
|
|
272
|
+
|
|
273
|
+ /* Print IP4 header for debugging */
|
|
274
|
+ DBG ( "IP4 header at %#x + %d\n", iphdr, IP_HLEN );
|
|
275
|
+ DBG ( "\tVersion = %d\n", ( iphdr->verhdrlen & IP_MASK_VER ) / 16 );
|
|
276
|
+ DBG ( "\tHeader length = %d\n", iphdr->verhdrlen & IP_MASK_HLEN );
|
|
277
|
+ DBG ( "\tService = %d\n", iphdr->service );
|
|
278
|
+ DBG ( "\tTotal length = %d\n", iphdr->len );
|
|
279
|
+ DBG ( "\tIdent = %d\n", iphdr->ident );
|
|
280
|
+ DBG ( "\tFrags/Offset = %d\n", iphdr->frags );
|
|
281
|
+ DBG ( "\tIP TTL = %d\n", iphdr->ttl );
|
|
282
|
+ DBG ( "\tProtocol = %d\n", iphdr->protocol );
|
|
283
|
+ DBG ( "\tHeader Checksum (at %#x) = %x\n", &iphdr->chksum, iphdr->chksum );
|
|
284
|
+ DBG ( "\tSource = %s\n", inet_ntoa ( iphdr->src) );
|
|
285
|
+ DBG ( "\tDestination = %s\n", inet_ntoa ( iphdr->dest ) );
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+ /* Determine link-layer destination address */
|
|
289
|
+ if ( next_hop.s_addr == INADDR_BROADCAST ) {
|
|
290
|
+ /* Broadcast address */
|
|
291
|
+ ll_dest = netdev->ll_protocol->ll_broadcast;
|
|
292
|
+ } else if ( IN_MULTICAST ( next_hop.s_addr ) ) {
|
|
293
|
+ /* Special case: IPv4 multicast over Ethernet. This
|
|
294
|
+ * code may need to be generalised once we find out
|
|
295
|
+ * what happens for other link layers.
|
|
296
|
+ */
|
|
297
|
+ uint8_t *next_hop_bytes = ( uint8_t * ) &next_hop;
|
|
298
|
+ ll_dest_buf[0] = 0x01;
|
|
299
|
+ ll_dest_buf[0] = 0x00;
|
|
300
|
+ ll_dest_buf[0] = 0x5e;
|
|
301
|
+ ll_dest_buf[3] = next_hop_bytes[1] & 0x7f;
|
|
302
|
+ ll_dest_buf[4] = next_hop_bytes[2];
|
|
303
|
+ ll_dest_buf[5] = next_hop_bytes[3];
|
|
304
|
+ } else {
|
|
305
|
+ /* Unicast address: resolve via ARP */
|
|
306
|
+ if ( ( rc = arp_resolve ( netdev, &ipv4_protocol, &next_hop, &source, ll_dest_buf ) ) != 0 ) {
|
|
307
|
+ DBG ( "No ARP entry for %s\n", inet_ntoa ( iphdr->dest ) );
|
|
308
|
+ goto err;
|
|
309
|
+ }
|
|
310
|
+ }
|
|
311
|
+
|
|
312
|
+ /* Hand off to link layer */
|
|
313
|
+ return net_tx ( pkb, netdev, &ipv4_protocol, ll_dest );
|
|
314
|
+
|
|
315
|
+ err:
|
|
316
|
+ /* Warning: Allowing this function to execute causes bochs to go into an infinite loop */
|
|
317
|
+ free_pkb ( pkb );
|
|
318
|
+ return rc;
|
|
319
|
+}
|
|
320
|
+
|
|
321
|
+/**
|
|
322
|
+ * Transmit IP6 packets
|
|
323
|
+ *
|
|
324
|
+ * Placeholder to allow linking. The function should be placed in net/ipv6.c
|
|
325
|
+ */
|
|
326
|
+int ipv6_tx ( struct pk_buff *pkb __unused, uint16_t trans_proto __unused, struct in6_addr *dest __unused) {
|
|
327
|
+ return -ENOSYS;
|
|
328
|
+}
|
|
329
|
+
|
|
330
|
+
|
168
|
331
|
/**
|
169
|
332
|
* Process incoming IP packets
|
170
|
333
|
*
|
|
@@ -200,6 +363,74 @@ static int ipv4_uip_rx ( struct pk_buff *pkb,
|
200
|
363
|
return 0;
|
201
|
364
|
}
|
202
|
365
|
|
|
366
|
+/**
|
|
367
|
+ * Process incoming IP6 packets
|
|
368
|
+ *
|
|
369
|
+ * Placeholder function. Should rewrite in net/ipv6.c
|
|
370
|
+ */
|
|
371
|
+void ipv6_rx ( struct pk_buff *pkb __unused, struct net_device *netdev __unused, const void *ll_source __unused ) {
|
|
372
|
+}
|
|
373
|
+
|
|
374
|
+/**
|
|
375
|
+ * Process incoming packets (without uIP)
|
|
376
|
+ *
|
|
377
|
+ * @v pkb Packet buffer
|
|
378
|
+ * @v netdev Network device
|
|
379
|
+ * @v ll_source Link-layer destination source
|
|
380
|
+ *
|
|
381
|
+ * This function expects an IP4 network datagram. It will process the headers and send it to the transport layer
|
|
382
|
+ */
|
|
383
|
+void ipv4_rx ( struct pk_buff *pkb, struct net_device *netdev __unused,
|
|
384
|
+ const void *ll_source __unused ) {
|
|
385
|
+ struct iphdr *iphdr = pkb->data;
|
|
386
|
+ struct in_addr *src = &iphdr->src;
|
|
387
|
+ struct in_addr *dest = &iphdr->dest;
|
|
388
|
+ uint16_t chksum;
|
|
389
|
+
|
|
390
|
+ /* Print IP4 header for debugging */
|
|
391
|
+ DBG ( "IP4 header at %#x + %d\n", iphdr, IP_HLEN );
|
|
392
|
+ DBG ( "\tVersion = %d\n", ( iphdr->verhdrlen & IP_MASK_VER ) / 16 );
|
|
393
|
+ DBG ( "\tHeader length = %d\n", iphdr->verhdrlen & IP_MASK_HLEN );
|
|
394
|
+ DBG ( "\tService = %d\n", iphdr->service );
|
|
395
|
+ DBG ( "\tTotal length = %d\n", iphdr->len );
|
|
396
|
+ DBG ( "\tIdent = %d\n", iphdr->ident );
|
|
397
|
+ DBG ( "\tFrags/Offset = %d\n", iphdr->frags );
|
|
398
|
+ DBG ( "\tIP TTL = %d\n", iphdr->ttl );
|
|
399
|
+ DBG ( "\tProtocol = %d\n", iphdr->protocol );
|
|
400
|
+ DBG ( "\tHeader Checksum (at %#x) = %x\n", &iphdr->chksum, iphdr->chksum );
|
|
401
|
+ DBG ( "\tSource = %s\n", inet_ntoa ( iphdr->src) );
|
|
402
|
+ DBG ( "\tDestination = %s\n", inet_ntoa ( iphdr->dest ) );
|
|
403
|
+
|
|
404
|
+ /* Process headers */
|
|
405
|
+ if ( iphdr->verhdrlen != 0x45 ) {
|
|
406
|
+ DBG ( "Bad version and header length %x\n", iphdr->verhdrlen );
|
|
407
|
+ return;
|
|
408
|
+ }
|
|
409
|
+
|
|
410
|
+ if ( iphdr->len != pkb_len ( pkb ) ) {
|
|
411
|
+ DBG ( "Bad total length %d\n", iphdr->len );
|
|
412
|
+ return;
|
|
413
|
+ }
|
|
414
|
+
|
|
415
|
+ if ( ( chksum = ipv4_rx_csum ( pkb, iphdr->protocol ) ) != 0xffff ) {
|
|
416
|
+ DBG ( "Bad checksum %x\n", chksum );
|
|
417
|
+ }
|
|
418
|
+
|
|
419
|
+ /* Todo: Compute and verify the header checksum */
|
|
420
|
+
|
|
421
|
+ /* To reduce code size, the following functions are not implemented:
|
|
422
|
+ * 1. Check the destination address
|
|
423
|
+ * 2. Check the TTL field
|
|
424
|
+ * 3. Check the service field
|
|
425
|
+ */
|
|
426
|
+
|
|
427
|
+ /* Strip header */
|
|
428
|
+ pkb_pull ( pkb, IP_HLEN );
|
|
429
|
+
|
|
430
|
+ /* Send it to the transport layer */
|
|
431
|
+ trans_rx ( pkb, iphdr->protocol, src, dest );
|
|
432
|
+}
|
|
433
|
+
|
203
|
434
|
/**
|
204
|
435
|
* Check existence of IPv4 address for ARP
|
205
|
436
|
*
|