[legal] Add missing copyright header to net/ipv4.c
[ipxe.git] / src / net / ipv4.c
1 /*
2 * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
3 * Copyright (C) 2006 Nikhil Chandru Rao
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of the
8 * License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18 * 02110-1301, USA.
19 */
20
21 #include <string.h>
22 #include <stdint.h>
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <errno.h>
26 #include <byteswap.h>
27 #include <ipxe/list.h>
28 #include <ipxe/in.h>
29 #include <ipxe/arp.h>
30 #include <ipxe/if_ether.h>
31 #include <ipxe/iobuf.h>
32 #include <ipxe/netdevice.h>
33 #include <ipxe/ip.h>
34 #include <ipxe/tcpip.h>
35 #include <ipxe/dhcp.h>
36 #include <ipxe/settings.h>
37 #include <ipxe/fragment.h>
38 #include <ipxe/ipstat.h>
39 #include <ipxe/profile.h>
40
41 /** @file
42 *
43 * IPv4 protocol
44 *
45 */
46
47 FILE_LICENCE ( GPL2_OR_LATER );
48
49 /* Unique IP datagram identification number (high byte) */
50 static uint8_t next_ident_high = 0;
51
52 /** List of IPv4 miniroutes */
53 struct list_head ipv4_miniroutes = LIST_HEAD_INIT ( ipv4_miniroutes );
54
55 /** IPv4 statistics */
56 static struct ip_statistics ipv4_stats;
57
58 /** IPv4 statistics family */
59 struct ip_statistics_family
60 ipv4_stats_family __ip_statistics_family ( IP_STATISTICS_IPV4 ) = {
61 .version = 4,
62 .stats = &ipv4_stats,
63 };
64
65 /** Transmit profiler */
66 static struct profiler ipv4_tx_profiler __profiler = { .name = "ipv4.tx" };
67
68 /** Receive profiler */
69 static struct profiler ipv4_rx_profiler __profiler = { .name = "ipv4.rx" };
70
71 /**
72 * Add IPv4 minirouting table entry
73 *
74 * @v netdev Network device
75 * @v address IPv4 address
76 * @v netmask Subnet mask
77 * @v gateway Gateway address (if any)
78 * @ret miniroute Routing table entry, or NULL
79 */
80 static struct ipv4_miniroute * __malloc
81 add_ipv4_miniroute ( struct net_device *netdev, struct in_addr address,
82 struct in_addr netmask, struct in_addr gateway ) {
83 struct ipv4_miniroute *miniroute;
84
85 DBGC ( netdev, "IPv4 add %s", inet_ntoa ( address ) );
86 DBGC ( netdev, "/%s ", inet_ntoa ( netmask ) );
87 if ( gateway.s_addr )
88 DBGC ( netdev, "gw %s ", inet_ntoa ( gateway ) );
89 DBGC ( netdev, "via %s\n", netdev->name );
90
91 /* Allocate and populate miniroute structure */
92 miniroute = malloc ( sizeof ( *miniroute ) );
93 if ( ! miniroute ) {
94 DBGC ( netdev, "IPv4 could not add miniroute\n" );
95 return NULL;
96 }
97
98 /* Record routing information */
99 miniroute->netdev = netdev_get ( netdev );
100 miniroute->address = address;
101 miniroute->netmask = netmask;
102 miniroute->gateway = gateway;
103
104 /* Add to end of list if we have a gateway, otherwise
105 * to start of list.
106 */
107 if ( gateway.s_addr ) {
108 list_add_tail ( &miniroute->list, &ipv4_miniroutes );
109 } else {
110 list_add ( &miniroute->list, &ipv4_miniroutes );
111 }
112
113 return miniroute;
114 }
115
116 /**
117 * Delete IPv4 minirouting table entry
118 *
119 * @v miniroute Routing table entry
120 */
121 static void del_ipv4_miniroute ( struct ipv4_miniroute *miniroute ) {
122 struct net_device *netdev = miniroute->netdev;
123
124 DBGC ( netdev, "IPv4 del %s", inet_ntoa ( miniroute->address ) );
125 DBGC ( netdev, "/%s ", inet_ntoa ( miniroute->netmask ) );
126 if ( miniroute->gateway.s_addr )
127 DBGC ( netdev, "gw %s ", inet_ntoa ( miniroute->gateway ) );
128 DBGC ( netdev, "via %s\n", miniroute->netdev->name );
129
130 netdev_put ( miniroute->netdev );
131 list_del ( &miniroute->list );
132 free ( miniroute );
133 }
134
135 /**
136 * Perform IPv4 routing
137 *
138 * @v dest Final destination address
139 * @ret dest Next hop destination address
140 * @ret miniroute Routing table entry to use, or NULL if no route
141 *
142 * If the route requires use of a gateway, the next hop destination
143 * address will be overwritten with the gateway address.
144 */
145 static struct ipv4_miniroute * ipv4_route ( struct in_addr *dest ) {
146 struct ipv4_miniroute *miniroute;
147 int local;
148 int has_gw;
149
150 /* Find first usable route in routing table */
151 list_for_each_entry ( miniroute, &ipv4_miniroutes, list ) {
152 if ( ! netdev_is_open ( miniroute->netdev ) )
153 continue;
154 local = ( ( ( dest->s_addr ^ miniroute->address.s_addr )
155 & miniroute->netmask.s_addr ) == 0 );
156 has_gw = ( miniroute->gateway.s_addr );
157 if ( local || has_gw ) {
158 if ( ! local )
159 *dest = miniroute->gateway;
160 return miniroute;
161 }
162 }
163
164 return NULL;
165 }
166
167 /**
168 * Determine transmitting network device
169 *
170 * @v st_dest Destination network-layer address
171 * @ret netdev Transmitting network device, or NULL
172 */
173 static struct net_device * ipv4_netdev ( struct sockaddr_tcpip *st_dest ) {
174 struct sockaddr_in *sin_dest = ( ( struct sockaddr_in * ) st_dest );
175 struct in_addr dest = sin_dest->sin_addr;
176 struct ipv4_miniroute *miniroute;
177
178 /* Find routing table entry */
179 miniroute = ipv4_route ( &dest );
180 if ( ! miniroute )
181 return NULL;
182
183 return miniroute->netdev;
184 }
185
186 /**
187 * Check if IPv4 fragment matches fragment reassembly buffer
188 *
189 * @v fragment Fragment reassembly buffer
190 * @v iobuf I/O buffer
191 * @v hdrlen Length of non-fragmentable potion of I/O buffer
192 * @ret is_fragment Fragment matches this reassembly buffer
193 */
194 static int ipv4_is_fragment ( struct fragment *fragment,
195 struct io_buffer *iobuf,
196 size_t hdrlen __unused ) {
197 struct iphdr *frag_iphdr = fragment->iobuf->data;
198 struct iphdr *iphdr = iobuf->data;
199
200 return ( ( iphdr->src.s_addr == frag_iphdr->src.s_addr ) &&
201 ( iphdr->ident == frag_iphdr->ident ) );
202 }
203
204 /**
205 * Get IPv4 fragment offset
206 *
207 * @v iobuf I/O buffer
208 * @v hdrlen Length of non-fragmentable potion of I/O buffer
209 * @ret offset Offset
210 */
211 static size_t ipv4_fragment_offset ( struct io_buffer *iobuf,
212 size_t hdrlen __unused ) {
213 struct iphdr *iphdr = iobuf->data;
214
215 return ( ( ntohs ( iphdr->frags ) & IP_MASK_OFFSET ) << 3 );
216 }
217
218 /**
219 * Check if more fragments exist
220 *
221 * @v iobuf I/O buffer
222 * @v hdrlen Length of non-fragmentable potion of I/O buffer
223 * @ret more_frags More fragments exist
224 */
225 static int ipv4_more_fragments ( struct io_buffer *iobuf,
226 size_t hdrlen __unused ) {
227 struct iphdr *iphdr = iobuf->data;
228
229 return ( iphdr->frags & htons ( IP_MASK_MOREFRAGS ) );
230 }
231
232 /** IPv4 fragment reassembler */
233 static struct fragment_reassembler ipv4_reassembler = {
234 .list = LIST_HEAD_INIT ( ipv4_reassembler.list ),
235 .is_fragment = ipv4_is_fragment,
236 .fragment_offset = ipv4_fragment_offset,
237 .more_fragments = ipv4_more_fragments,
238 .stats = &ipv4_stats,
239 };
240
241 /**
242 * Add IPv4 pseudo-header checksum to existing checksum
243 *
244 * @v iobuf I/O buffer
245 * @v csum Existing checksum
246 * @ret csum Updated checksum
247 */
248 static uint16_t ipv4_pshdr_chksum ( struct io_buffer *iobuf, uint16_t csum ) {
249 struct ipv4_pseudo_header pshdr;
250 struct iphdr *iphdr = iobuf->data;
251 size_t hdrlen = ( ( iphdr->verhdrlen & IP_MASK_HLEN ) * 4 );
252
253 /* Build pseudo-header */
254 pshdr.src = iphdr->src;
255 pshdr.dest = iphdr->dest;
256 pshdr.zero_padding = 0x00;
257 pshdr.protocol = iphdr->protocol;
258 pshdr.len = htons ( iob_len ( iobuf ) - hdrlen );
259
260 /* Update the checksum value */
261 return tcpip_continue_chksum ( csum, &pshdr, sizeof ( pshdr ) );
262 }
263
264 /**
265 * Transmit IP packet
266 *
267 * @v iobuf I/O buffer
268 * @v tcpip Transport-layer protocol
269 * @v st_src Source network-layer address
270 * @v st_dest Destination network-layer address
271 * @v netdev Network device to use if no route found, or NULL
272 * @v trans_csum Transport-layer checksum to complete, or NULL
273 * @ret rc Status
274 *
275 * This function expects a transport-layer segment and prepends the IP header
276 */
277 static int ipv4_tx ( struct io_buffer *iobuf,
278 struct tcpip_protocol *tcpip_protocol,
279 struct sockaddr_tcpip *st_src,
280 struct sockaddr_tcpip *st_dest,
281 struct net_device *netdev,
282 uint16_t *trans_csum ) {
283 struct iphdr *iphdr = iob_push ( iobuf, sizeof ( *iphdr ) );
284 struct sockaddr_in *sin_src = ( ( struct sockaddr_in * ) st_src );
285 struct sockaddr_in *sin_dest = ( ( struct sockaddr_in * ) st_dest );
286 struct ipv4_miniroute *miniroute;
287 struct in_addr next_hop;
288 struct in_addr netmask = { .s_addr = 0 };
289 uint8_t ll_dest_buf[MAX_LL_ADDR_LEN];
290 const void *ll_dest;
291 int rc;
292
293 /* Start profiling */
294 profile_start ( &ipv4_tx_profiler );
295
296 /* Update statistics */
297 ipv4_stats.out_requests++;
298
299 /* Fill up the IP header, except source address */
300 memset ( iphdr, 0, sizeof ( *iphdr ) );
301 iphdr->verhdrlen = ( IP_VER | ( sizeof ( *iphdr ) / 4 ) );
302 iphdr->service = IP_TOS;
303 iphdr->len = htons ( iob_len ( iobuf ) );
304 iphdr->ttl = IP_TTL;
305 iphdr->protocol = tcpip_protocol->tcpip_proto;
306 iphdr->dest = sin_dest->sin_addr;
307
308 /* Use routing table to identify next hop and transmitting netdev */
309 next_hop = iphdr->dest;
310 if ( sin_src )
311 iphdr->src = sin_src->sin_addr;
312 if ( ( next_hop.s_addr != INADDR_BROADCAST ) &&
313 ( ! IN_MULTICAST ( ntohl ( next_hop.s_addr ) ) ) &&
314 ( ( miniroute = ipv4_route ( &next_hop ) ) != NULL ) ) {
315 iphdr->src = miniroute->address;
316 netmask = miniroute->netmask;
317 netdev = miniroute->netdev;
318 }
319 if ( ! netdev ) {
320 DBGC ( sin_dest->sin_addr, "IPv4 has no route to %s\n",
321 inet_ntoa ( iphdr->dest ) );
322 ipv4_stats.out_no_routes++;
323 rc = -ENETUNREACH;
324 goto err;
325 }
326
327 /* (Ab)use the "ident" field to convey metadata about the
328 * network device statistics into packet traces. Useful for
329 * extracting debug information from non-debug builds.
330 */
331 iphdr->ident = htons ( ( (++next_ident_high) << 8 ) |
332 ( ( netdev->rx_stats.bad & 0xf ) << 4 ) |
333 ( ( netdev->rx_stats.good & 0xf ) << 0 ) );
334
335 /* Fix up checksums */
336 if ( trans_csum )
337 *trans_csum = ipv4_pshdr_chksum ( iobuf, *trans_csum );
338 iphdr->chksum = tcpip_chksum ( iphdr, sizeof ( *iphdr ) );
339
340 /* Print IP4 header for debugging */
341 DBGC2 ( sin_dest->sin_addr, "IPv4 TX %s->", inet_ntoa ( iphdr->src ) );
342 DBGC2 ( sin_dest->sin_addr, "%s len %d proto %d id %04x csum %04x\n",
343 inet_ntoa ( iphdr->dest ), ntohs ( iphdr->len ),
344 iphdr->protocol, ntohs ( iphdr->ident ),
345 ntohs ( iphdr->chksum ) );
346
347 /* Calculate link-layer destination address, if possible */
348 if ( ( ( next_hop.s_addr ^ INADDR_BROADCAST ) & ~netmask.s_addr ) == 0){
349 /* Broadcast address */
350 ipv4_stats.out_bcast_pkts++;
351 ll_dest = netdev->ll_broadcast;
352 } else if ( IN_MULTICAST ( ntohl ( next_hop.s_addr ) ) ) {
353 /* Multicast address */
354 ipv4_stats.out_mcast_pkts++;
355 if ( ( rc = netdev->ll_protocol->mc_hash ( AF_INET, &next_hop,
356 ll_dest_buf ) ) !=0){
357 DBGC ( sin_dest->sin_addr, "IPv4 could not hash "
358 "multicast %s: %s\n",
359 inet_ntoa ( next_hop ), strerror ( rc ) );
360 goto err;
361 }
362 ll_dest = ll_dest_buf;
363 } else {
364 /* Unicast address */
365 ll_dest = NULL;
366 }
367
368 /* Update statistics */
369 ipv4_stats.out_transmits++;
370 ipv4_stats.out_octets += iob_len ( iobuf );
371
372 /* Hand off to link layer (via ARP if applicable) */
373 if ( ll_dest ) {
374 if ( ( rc = net_tx ( iobuf, netdev, &ipv4_protocol, ll_dest,
375 netdev->ll_addr ) ) != 0 ) {
376 DBGC ( sin_dest->sin_addr, "IPv4 could not transmit "
377 "packet via %s: %s\n",
378 netdev->name, strerror ( rc ) );
379 return rc;
380 }
381 } else {
382 if ( ( rc = arp_tx ( iobuf, netdev, &ipv4_protocol, &next_hop,
383 &iphdr->src, netdev->ll_addr ) ) != 0 ) {
384 DBGC ( sin_dest->sin_addr, "IPv4 could not transmit "
385 "packet via %s: %s\n",
386 netdev->name, strerror ( rc ) );
387 return rc;
388 }
389 }
390
391 profile_stop ( &ipv4_tx_profiler );
392 return 0;
393
394 err:
395 free_iob ( iobuf );
396 return rc;
397 }
398
399 /**
400 * Check if network device has any IPv4 address
401 *
402 * @v netdev Network device
403 * @ret has_any_addr Network device has any IPv4 address
404 */
405 int ipv4_has_any_addr ( struct net_device *netdev ) {
406 struct ipv4_miniroute *miniroute;
407
408 list_for_each_entry ( miniroute, &ipv4_miniroutes, list ) {
409 if ( miniroute->netdev == netdev )
410 return 1;
411 }
412 return 0;
413 }
414
415 /**
416 * Check if network device has a specific IPv4 address
417 *
418 * @v netdev Network device
419 * @v addr IPv4 address
420 * @ret has_addr Network device has this IPv4 address
421 */
422 static int ipv4_has_addr ( struct net_device *netdev, struct in_addr addr ) {
423 struct ipv4_miniroute *miniroute;
424
425 list_for_each_entry ( miniroute, &ipv4_miniroutes, list ) {
426 if ( ( miniroute->netdev == netdev ) &&
427 ( miniroute->address.s_addr == addr.s_addr ) ) {
428 /* Found matching address */
429 return 1;
430 }
431 }
432 return 0;
433 }
434
435 /**
436 * Process incoming packets
437 *
438 * @v iobuf I/O buffer
439 * @v netdev Network device
440 * @v ll_dest Link-layer destination address
441 * @v ll_source Link-layer destination source
442 * @v flags Packet flags
443 * @ret rc Return status code
444 *
445 * This function expects an IP4 network datagram. It processes the headers
446 * and sends it to the transport layer.
447 */
448 static int ipv4_rx ( struct io_buffer *iobuf,
449 struct net_device *netdev,
450 const void *ll_dest __unused,
451 const void *ll_source __unused,
452 unsigned int flags ) {
453 struct iphdr *iphdr = iobuf->data;
454 size_t hdrlen;
455 size_t len;
456 union {
457 struct sockaddr_in sin;
458 struct sockaddr_tcpip st;
459 } src, dest;
460 uint16_t csum;
461 uint16_t pshdr_csum;
462 int rc;
463
464 /* Start profiling */
465 profile_start ( &ipv4_rx_profiler );
466
467 /* Update statistics */
468 ipv4_stats.in_receives++;
469 ipv4_stats.in_octets += iob_len ( iobuf );
470 if ( flags & LL_BROADCAST ) {
471 ipv4_stats.in_bcast_pkts++;
472 } else if ( flags & LL_MULTICAST ) {
473 ipv4_stats.in_mcast_pkts++;
474 }
475
476 /* Sanity check the IPv4 header */
477 if ( iob_len ( iobuf ) < sizeof ( *iphdr ) ) {
478 DBGC ( iphdr->src, "IPv4 packet too short at %zd bytes (min "
479 "%zd bytes)\n", iob_len ( iobuf ), sizeof ( *iphdr ) );
480 goto err_header;
481 }
482 if ( ( iphdr->verhdrlen & IP_MASK_VER ) != IP_VER ) {
483 DBGC ( iphdr->src, "IPv4 version %#02x not supported\n",
484 iphdr->verhdrlen );
485 goto err_header;
486 }
487 hdrlen = ( ( iphdr->verhdrlen & IP_MASK_HLEN ) * 4 );
488 if ( hdrlen < sizeof ( *iphdr ) ) {
489 DBGC ( iphdr->src, "IPv4 header too short at %zd bytes (min "
490 "%zd bytes)\n", hdrlen, sizeof ( *iphdr ) );
491 goto err_header;
492 }
493 if ( hdrlen > iob_len ( iobuf ) ) {
494 DBGC ( iphdr->src, "IPv4 header too long at %zd bytes "
495 "(packet is %zd bytes)\n", hdrlen, iob_len ( iobuf ) );
496 goto err_header;
497 }
498 if ( ( csum = tcpip_chksum ( iphdr, hdrlen ) ) != 0 ) {
499 DBGC ( iphdr->src, "IPv4 checksum incorrect (is %04x "
500 "including checksum field, should be 0000)\n", csum );
501 goto err_header;
502 }
503 len = ntohs ( iphdr->len );
504 if ( len < hdrlen ) {
505 DBGC ( iphdr->src, "IPv4 length too short at %zd bytes "
506 "(header is %zd bytes)\n", len, hdrlen );
507 goto err_header;
508 }
509 if ( len > iob_len ( iobuf ) ) {
510 DBGC ( iphdr->src, "IPv4 length too long at %zd bytes "
511 "(packet is %zd bytes)\n", len, iob_len ( iobuf ) );
512 ipv4_stats.in_truncated_pkts++;
513 goto err_other;
514 }
515
516 /* Truncate packet to correct length */
517 iob_unput ( iobuf, ( iob_len ( iobuf ) - len ) );
518
519 /* Print IPv4 header for debugging */
520 DBGC2 ( iphdr->src, "IPv4 RX %s<-", inet_ntoa ( iphdr->dest ) );
521 DBGC2 ( iphdr->src, "%s len %d proto %d id %04x csum %04x\n",
522 inet_ntoa ( iphdr->src ), ntohs ( iphdr->len ), iphdr->protocol,
523 ntohs ( iphdr->ident ), ntohs ( iphdr->chksum ) );
524
525 /* Discard unicast packets not destined for us */
526 if ( ( ! ( flags & LL_MULTICAST ) ) &&
527 ipv4_has_any_addr ( netdev ) &&
528 ( ! ipv4_has_addr ( netdev, iphdr->dest ) ) ) {
529 DBGC ( iphdr->src, "IPv4 discarding non-local unicast packet "
530 "for %s\n", inet_ntoa ( iphdr->dest ) );
531 ipv4_stats.in_addr_errors++;
532 goto err_other;
533 }
534
535 /* Perform fragment reassembly if applicable */
536 if ( iphdr->frags & htons ( IP_MASK_OFFSET | IP_MASK_MOREFRAGS ) ) {
537 /* Pass the fragment to fragment_reassemble() which returns
538 * either a fully reassembled I/O buffer or NULL.
539 */
540 iobuf = fragment_reassemble ( &ipv4_reassembler, iobuf,
541 &hdrlen );
542 if ( ! iobuf )
543 return 0;
544 iphdr = iobuf->data;
545 }
546
547 /* Construct socket addresses, calculate pseudo-header
548 * checksum, and hand off to transport layer
549 */
550 memset ( &src, 0, sizeof ( src ) );
551 src.sin.sin_family = AF_INET;
552 src.sin.sin_addr = iphdr->src;
553 memset ( &dest, 0, sizeof ( dest ) );
554 dest.sin.sin_family = AF_INET;
555 dest.sin.sin_addr = iphdr->dest;
556 pshdr_csum = ipv4_pshdr_chksum ( iobuf, TCPIP_EMPTY_CSUM );
557 iob_pull ( iobuf, hdrlen );
558 if ( ( rc = tcpip_rx ( iobuf, netdev, iphdr->protocol, &src.st,
559 &dest.st, pshdr_csum, &ipv4_stats ) ) != 0 ) {
560 DBGC ( src.sin.sin_addr, "IPv4 received packet rejected by "
561 "stack: %s\n", strerror ( rc ) );
562 return rc;
563 }
564
565 profile_stop ( &ipv4_rx_profiler );
566 return 0;
567
568 err_header:
569 ipv4_stats.in_hdr_errors++;
570 err_other:
571 free_iob ( iobuf );
572 return -EINVAL;
573 }
574
575 /**
576 * Check existence of IPv4 address for ARP
577 *
578 * @v netdev Network device
579 * @v net_addr Network-layer address
580 * @ret rc Return status code
581 */
582 static int ipv4_arp_check ( struct net_device *netdev, const void *net_addr ) {
583 const struct in_addr *address = net_addr;
584
585 if ( ipv4_has_addr ( netdev, *address ) )
586 return 0;
587
588 return -ENOENT;
589 }
590
591 /**
592 * Convert IPv4 address to dotted-quad notation
593 *
594 * @v in IP address
595 * @ret string IP address in dotted-quad notation
596 */
597 char * inet_ntoa ( struct in_addr in ) {
598 static char buf[16]; /* "xxx.xxx.xxx.xxx" */
599 uint8_t *bytes = ( uint8_t * ) &in;
600
601 sprintf ( buf, "%d.%d.%d.%d", bytes[0], bytes[1], bytes[2], bytes[3] );
602 return buf;
603 }
604
605 /**
606 * Transcribe IP address
607 *
608 * @v net_addr IP address
609 * @ret string IP address in dotted-quad notation
610 *
611 */
612 static const char * ipv4_ntoa ( const void *net_addr ) {
613 return inet_ntoa ( * ( ( struct in_addr * ) net_addr ) );
614 }
615
616 /**
617 * Transcribe IPv4 socket address
618 *
619 * @v sa Socket address
620 * @ret string Socket address in standard notation
621 */
622 static const char * ipv4_sock_ntoa ( struct sockaddr *sa ) {
623 struct sockaddr_in *sin = ( ( struct sockaddr_in * ) sa );
624
625 return inet_ntoa ( sin->sin_addr );
626 }
627
628 /**
629 * Parse IPv4 socket address
630 *
631 * @v string Socket address string
632 * @v sa Socket address to fill in
633 * @ret rc Return status code
634 */
635 static int ipv4_sock_aton ( const char *string, struct sockaddr *sa ) {
636 struct sockaddr_in *sin = ( ( struct sockaddr_in * ) sa );
637 struct in_addr in;
638
639 if ( inet_aton ( string, &in ) ) {
640 sin->sin_addr = in;
641 return 0;
642 }
643 return -EINVAL;
644 }
645
646 /** IPv4 protocol */
647 struct net_protocol ipv4_protocol __net_protocol = {
648 .name = "IP",
649 .net_proto = htons ( ETH_P_IP ),
650 .net_addr_len = sizeof ( struct in_addr ),
651 .rx = ipv4_rx,
652 .ntoa = ipv4_ntoa,
653 };
654
655 /** IPv4 TCPIP net protocol */
656 struct tcpip_net_protocol ipv4_tcpip_protocol __tcpip_net_protocol = {
657 .name = "IPv4",
658 .sa_family = AF_INET,
659 .header_len = sizeof ( struct iphdr ),
660 .tx = ipv4_tx,
661 .netdev = ipv4_netdev,
662 };
663
664 /** IPv4 ARP protocol */
665 struct arp_net_protocol ipv4_arp_protocol __arp_net_protocol = {
666 .net_protocol = &ipv4_protocol,
667 .check = ipv4_arp_check,
668 };
669
670 /** IPv4 socket address converter */
671 struct sockaddr_converter ipv4_sockaddr_converter __sockaddr_converter = {
672 .family = AF_INET,
673 .ntoa = ipv4_sock_ntoa,
674 .aton = ipv4_sock_aton,
675 };
676
677 /******************************************************************************
678 *
679 * Settings
680 *
681 ******************************************************************************
682 */
683
684 /**
685 * Parse IPv4 address setting value
686 *
687 * @v type Setting type
688 * @v value Formatted setting value
689 * @v buf Buffer to contain raw value
690 * @v len Length of buffer
691 * @ret len Length of raw value, or negative error
692 */
693 int parse_ipv4_setting ( const struct setting_type *type __unused,
694 const char *value, void *buf, size_t len ) {
695 struct in_addr ipv4;
696
697 /* Parse IPv4 address */
698 if ( inet_aton ( value, &ipv4 ) == 0 )
699 return -EINVAL;
700
701 /* Copy to buffer */
702 if ( len > sizeof ( ipv4 ) )
703 len = sizeof ( ipv4 );
704 memcpy ( buf, &ipv4, len );
705
706 return ( sizeof ( ipv4 ) );
707 }
708
709 /**
710 * Format IPv4 address setting value
711 *
712 * @v type Setting type
713 * @v raw Raw setting value
714 * @v raw_len Length of raw setting value
715 * @v buf Buffer to contain formatted value
716 * @v len Length of buffer
717 * @ret len Length of formatted value, or negative error
718 */
719 int format_ipv4_setting ( const struct setting_type *type __unused,
720 const void *raw, size_t raw_len, char *buf,
721 size_t len ) {
722 const struct in_addr *ipv4 = raw;
723
724 if ( raw_len < sizeof ( *ipv4 ) )
725 return -EINVAL;
726 return snprintf ( buf, len, "%s", inet_ntoa ( *ipv4 ) );
727 }
728
729 /** IPv4 address setting */
730 const struct setting ip_setting __setting ( SETTING_IP, ip ) = {
731 .name = "ip",
732 .description = "IP address",
733 .tag = DHCP_EB_YIADDR,
734 .type = &setting_type_ipv4,
735 };
736
737 /** IPv4 subnet mask setting */
738 const struct setting netmask_setting __setting ( SETTING_IP, netmask ) = {
739 .name = "netmask",
740 .description = "Subnet mask",
741 .tag = DHCP_SUBNET_MASK,
742 .type = &setting_type_ipv4,
743 };
744
745 /** Default gateway setting */
746 const struct setting gateway_setting __setting ( SETTING_IP, gateway ) = {
747 .name = "gateway",
748 .description = "Default gateway",
749 .tag = DHCP_ROUTERS,
750 .type = &setting_type_ipv4,
751 };
752
753 /**
754 * Create IPv4 routing table based on configured settings
755 *
756 * @ret rc Return status code
757 */
758 static int ipv4_create_routes ( void ) {
759 struct ipv4_miniroute *miniroute;
760 struct ipv4_miniroute *tmp;
761 struct net_device *netdev;
762 struct settings *settings;
763 struct in_addr address = { 0 };
764 struct in_addr netmask = { 0 };
765 struct in_addr gateway = { 0 };
766
767 /* Delete all existing routes */
768 list_for_each_entry_safe ( miniroute, tmp, &ipv4_miniroutes, list )
769 del_ipv4_miniroute ( miniroute );
770
771 /* Create a route for each configured network device */
772 for_each_netdev ( netdev ) {
773 settings = netdev_settings ( netdev );
774 /* Get IPv4 address */
775 address.s_addr = 0;
776 fetch_ipv4_setting ( settings, &ip_setting, &address );
777 if ( ! address.s_addr )
778 continue;
779 /* Get subnet mask */
780 fetch_ipv4_setting ( settings, &netmask_setting, &netmask );
781 /* Calculate default netmask, if necessary */
782 if ( ! netmask.s_addr ) {
783 if ( IN_CLASSA ( ntohl ( address.s_addr ) ) ) {
784 netmask.s_addr = htonl ( IN_CLASSA_NET );
785 } else if ( IN_CLASSB ( ntohl ( address.s_addr ) ) ) {
786 netmask.s_addr = htonl ( IN_CLASSB_NET );
787 } else if ( IN_CLASSC ( ntohl ( address.s_addr ) ) ) {
788 netmask.s_addr = htonl ( IN_CLASSC_NET );
789 }
790 }
791 /* Get default gateway, if present */
792 fetch_ipv4_setting ( settings, &gateway_setting, &gateway );
793 /* Configure route */
794 miniroute = add_ipv4_miniroute ( netdev, address,
795 netmask, gateway );
796 if ( ! miniroute )
797 return -ENOMEM;
798 }
799
800 return 0;
801 }
802
803 /** IPv4 settings applicator */
804 struct settings_applicator ipv4_settings_applicator __settings_applicator = {
805 .apply = ipv4_create_routes,
806 };
807
808 /* Drag in ICMPv4 */
809 REQUIRE_OBJECT ( icmpv4 );