2 * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
3 * Copyright (C) 2006 Nikhil Chandru Rao
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.
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.
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
27 #include <ipxe/list.h>
30 #include <ipxe/if_ether.h>
31 #include <ipxe/iobuf.h>
32 #include <ipxe/netdevice.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>
47 FILE_LICENCE ( GPL2_OR_LATER
);
49 /* Unique IP datagram identification number (high byte) */
50 static uint8_t next_ident_high
= 0;
52 /** List of IPv4 miniroutes */
53 struct list_head ipv4_miniroutes
= LIST_HEAD_INIT ( ipv4_miniroutes
);
55 /** IPv4 statistics */
56 static struct ip_statistics ipv4_stats
;
58 /** IPv4 statistics family */
59 struct ip_statistics_family
60 ipv4_stats_family
__ip_statistics_family ( IP_STATISTICS_IPV4
) = {
65 /** Transmit profiler */
66 static struct profiler ipv4_tx_profiler __profiler
= { .name
= "ipv4.tx" };
68 /** Receive profiler */
69 static struct profiler ipv4_rx_profiler __profiler
= { .name
= "ipv4.rx" };
72 * Add IPv4 minirouting table entry
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
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
;
85 DBGC ( netdev
, "IPv4 add %s", inet_ntoa ( address
) );
86 DBGC ( netdev
, "/%s ", inet_ntoa ( netmask
) );
88 DBGC ( netdev
, "gw %s ", inet_ntoa ( gateway
) );
89 DBGC ( netdev
, "via %s\n", netdev
->name
);
91 /* Allocate and populate miniroute structure */
92 miniroute
= malloc ( sizeof ( *miniroute
) );
94 DBGC ( netdev
, "IPv4 could not add miniroute\n" );
98 /* Record routing information */
99 miniroute
->netdev
= netdev_get ( netdev
);
100 miniroute
->address
= address
;
101 miniroute
->netmask
= netmask
;
102 miniroute
->gateway
= gateway
;
104 /* Add to end of list if we have a gateway, otherwise
107 if ( gateway
.s_addr
) {
108 list_add_tail ( &miniroute
->list
, &ipv4_miniroutes
);
110 list_add ( &miniroute
->list
, &ipv4_miniroutes
);
117 * Delete IPv4 minirouting table entry
119 * @v miniroute Routing table entry
121 static void del_ipv4_miniroute ( struct ipv4_miniroute
*miniroute
) {
122 struct net_device
*netdev
= miniroute
->netdev
;
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
);
130 netdev_put ( miniroute
->netdev
);
131 list_del ( &miniroute
->list
);
136 * Perform IPv4 routing
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
142 * If the route requires use of a gateway, the next hop destination
143 * address will be overwritten with the gateway address.
145 static struct ipv4_miniroute
* ipv4_route ( struct in_addr
*dest
) {
146 struct ipv4_miniroute
*miniroute
;
150 /* Find first usable route in routing table */
151 list_for_each_entry ( miniroute
, &ipv4_miniroutes
, list
) {
152 if ( ! netdev_is_open ( miniroute
->netdev
) )
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
) {
159 *dest
= miniroute
->gateway
;
168 * Determine transmitting network device
170 * @v st_dest Destination network-layer address
171 * @ret netdev Transmitting network device, or NULL
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
;
178 /* Find routing table entry */
179 miniroute
= ipv4_route ( &dest
);
183 return miniroute
->netdev
;
187 * Check if IPv4 fragment matches fragment reassembly buffer
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
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
;
200 return ( ( iphdr
->src
.s_addr
== frag_iphdr
->src
.s_addr
) &&
201 ( iphdr
->ident
== frag_iphdr
->ident
) );
205 * Get IPv4 fragment offset
207 * @v iobuf I/O buffer
208 * @v hdrlen Length of non-fragmentable potion of I/O buffer
211 static size_t ipv4_fragment_offset ( struct io_buffer
*iobuf
,
212 size_t hdrlen __unused
) {
213 struct iphdr
*iphdr
= iobuf
->data
;
215 return ( ( ntohs ( iphdr
->frags
) & IP_MASK_OFFSET
) << 3 );
219 * Check if more fragments exist
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
225 static int ipv4_more_fragments ( struct io_buffer
*iobuf
,
226 size_t hdrlen __unused
) {
227 struct iphdr
*iphdr
= iobuf
->data
;
229 return ( iphdr
->frags
& htons ( IP_MASK_MOREFRAGS
) );
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
,
242 * Add IPv4 pseudo-header checksum to existing checksum
244 * @v iobuf I/O buffer
245 * @v csum Existing checksum
246 * @ret csum Updated checksum
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 );
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
);
260 /* Update the checksum value */
261 return tcpip_continue_chksum ( csum
, &pshdr
, sizeof ( pshdr
) );
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
275 * This function expects a transport-layer segment and prepends the IP header
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
];
293 /* Start profiling */
294 profile_start ( &ipv4_tx_profiler
);
296 /* Update statistics */
297 ipv4_stats
.out_requests
++;
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
) );
305 iphdr
->protocol
= tcpip_protocol
->tcpip_proto
;
306 iphdr
->dest
= sin_dest
->sin_addr
;
308 /* Use routing table to identify next hop and transmitting netdev */
309 next_hop
= iphdr
->dest
;
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
;
320 DBGC ( sin_dest
->sin_addr
, "IPv4 has no route to %s\n",
321 inet_ntoa ( iphdr
->dest
) );
322 ipv4_stats
.out_no_routes
++;
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.
331 iphdr
->ident
= htons ( ( (++next_ident_high
) << 8 ) |
332 ( ( netdev
->rx_stats
.bad
& 0xf ) << 4 ) |
333 ( ( netdev
->rx_stats
.good
& 0xf ) << 0 ) );
335 /* Fix up checksums */
337 *trans_csum
= ipv4_pshdr_chksum ( iobuf
, *trans_csum
);
338 iphdr
->chksum
= tcpip_chksum ( iphdr
, sizeof ( *iphdr
) );
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
) );
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
) );
362 ll_dest
= ll_dest_buf
;
364 /* Unicast address */
368 /* Update statistics */
369 ipv4_stats
.out_transmits
++;
370 ipv4_stats
.out_octets
+= iob_len ( iobuf
);
372 /* Hand off to link layer (via ARP if applicable) */
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
) );
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
) );
391 profile_stop ( &ipv4_tx_profiler
);
400 * Check if network device has any IPv4 address
402 * @v netdev Network device
403 * @ret has_any_addr Network device has any IPv4 address
405 int ipv4_has_any_addr ( struct net_device
*netdev
) {
406 struct ipv4_miniroute
*miniroute
;
408 list_for_each_entry ( miniroute
, &ipv4_miniroutes
, list
) {
409 if ( miniroute
->netdev
== netdev
)
416 * Check if network device has a specific IPv4 address
418 * @v netdev Network device
419 * @v addr IPv4 address
420 * @ret has_addr Network device has this IPv4 address
422 static int ipv4_has_addr ( struct net_device
*netdev
, struct in_addr addr
) {
423 struct ipv4_miniroute
*miniroute
;
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 */
436 * Process incoming packets
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
445 * This function expects an IP4 network datagram. It processes the headers
446 * and sends it to the transport layer.
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
;
457 struct sockaddr_in sin
;
458 struct sockaddr_tcpip st
;
464 /* Start profiling */
465 profile_start ( &ipv4_rx_profiler
);
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
++;
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
) );
482 if ( ( iphdr
->verhdrlen
& IP_MASK_VER
) != IP_VER
) {
483 DBGC ( iphdr
->src
, "IPv4 version %#02x not supported\n",
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
) );
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
) );
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
);
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
);
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
++;
516 /* Truncate packet to correct length */
517 iob_unput ( iobuf
, ( iob_len ( iobuf
) - len
) );
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
) );
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
++;
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.
540 iobuf
= fragment_reassemble ( &ipv4_reassembler
, iobuf
,
547 /* Construct socket addresses, calculate pseudo-header
548 * checksum, and hand off to transport layer
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
) );
565 profile_stop ( &ipv4_rx_profiler
);
569 ipv4_stats
.in_hdr_errors
++;
576 * Check existence of IPv4 address for ARP
578 * @v netdev Network device
579 * @v net_addr Network-layer address
580 * @ret rc Return status code
582 static int ipv4_arp_check ( struct net_device
*netdev
, const void *net_addr
) {
583 const struct in_addr
*address
= net_addr
;
585 if ( ipv4_has_addr ( netdev
, *address
) )
594 * @v string IPv4 address string
595 * @ret in IPv4 address to fill in
596 * @ret ok IPv4 address is valid
598 * Note that this function returns nonzero iff the address is valid,
599 * to match the standard BSD API function of the same name. Unlike
600 * most other iPXE functions, a zero therefore indicates failure.
602 int inet_aton ( const char *string
, struct in_addr
*in
) {
603 const char *separator
= "...";
604 uint8_t *byte
= ( ( uint8_t * ) in
);
609 value
= strtoul ( string
, &endp
, 0 );
610 if ( string
== endp
)
615 if ( *endp
!= *separator
)
617 if ( ! *(separator
++) )
619 string
= ( endp
+ 1 );
624 * Convert IPv4 address to dotted-quad notation
627 * @ret string IPv4 address in dotted-quad notation
629 char * inet_ntoa ( struct in_addr in
) {
630 static char buf
[16]; /* "xxx.xxx.xxx.xxx" */
631 uint8_t *bytes
= ( uint8_t * ) &in
;
633 sprintf ( buf
, "%d.%d.%d.%d", bytes
[0], bytes
[1], bytes
[2], bytes
[3] );
638 * Transcribe IPv4 address
640 * @v net_addr IPv4 address
641 * @ret string IPv4 address in dotted-quad notation
644 static const char * ipv4_ntoa ( const void *net_addr
) {
645 return inet_ntoa ( * ( ( struct in_addr
* ) net_addr
) );
649 * Transcribe IPv4 socket address
651 * @v sa Socket address
652 * @ret string Socket address in standard notation
654 static const char * ipv4_sock_ntoa ( struct sockaddr
*sa
) {
655 struct sockaddr_in
*sin
= ( ( struct sockaddr_in
* ) sa
);
657 return inet_ntoa ( sin
->sin_addr
);
661 * Parse IPv4 socket address
663 * @v string Socket address string
664 * @v sa Socket address to fill in
665 * @ret rc Return status code
667 static int ipv4_sock_aton ( const char *string
, struct sockaddr
*sa
) {
668 struct sockaddr_in
*sin
= ( ( struct sockaddr_in
* ) sa
);
671 if ( inet_aton ( string
, &in
) ) {
679 struct net_protocol ipv4_protocol __net_protocol
= {
681 .net_proto
= htons ( ETH_P_IP
),
682 .net_addr_len
= sizeof ( struct in_addr
),
687 /** IPv4 TCPIP net protocol */
688 struct tcpip_net_protocol ipv4_tcpip_protocol __tcpip_net_protocol
= {
690 .sa_family
= AF_INET
,
691 .header_len
= sizeof ( struct iphdr
),
693 .netdev
= ipv4_netdev
,
696 /** IPv4 ARP protocol */
697 struct arp_net_protocol ipv4_arp_protocol __arp_net_protocol
= {
698 .net_protocol
= &ipv4_protocol
,
699 .check
= ipv4_arp_check
,
702 /** IPv4 socket address converter */
703 struct sockaddr_converter ipv4_sockaddr_converter __sockaddr_converter
= {
705 .ntoa
= ipv4_sock_ntoa
,
706 .aton
= ipv4_sock_aton
,
709 /******************************************************************************
713 ******************************************************************************
717 * Parse IPv4 address setting value
719 * @v type Setting type
720 * @v value Formatted setting value
721 * @v buf Buffer to contain raw value
722 * @v len Length of buffer
723 * @ret len Length of raw value, or negative error
725 int parse_ipv4_setting ( const struct setting_type
*type __unused
,
726 const char *value
, void *buf
, size_t len
) {
729 /* Parse IPv4 address */
730 if ( inet_aton ( value
, &ipv4
) == 0 )
734 if ( len
> sizeof ( ipv4
) )
735 len
= sizeof ( ipv4
);
736 memcpy ( buf
, &ipv4
, len
);
738 return ( sizeof ( ipv4
) );
742 * Format IPv4 address setting value
744 * @v type Setting type
745 * @v raw Raw setting value
746 * @v raw_len Length of raw setting value
747 * @v buf Buffer to contain formatted value
748 * @v len Length of buffer
749 * @ret len Length of formatted value, or negative error
751 int format_ipv4_setting ( const struct setting_type
*type __unused
,
752 const void *raw
, size_t raw_len
, char *buf
,
754 const struct in_addr
*ipv4
= raw
;
756 if ( raw_len
< sizeof ( *ipv4
) )
758 return snprintf ( buf
, len
, "%s", inet_ntoa ( *ipv4
) );
761 /** IPv4 address setting */
762 const struct setting ip_setting
__setting ( SETTING_IP
, ip
) = {
764 .description
= "IP address",
765 .tag
= DHCP_EB_YIADDR
,
766 .type
= &setting_type_ipv4
,
769 /** IPv4 subnet mask setting */
770 const struct setting netmask_setting
__setting ( SETTING_IP
, netmask
) = {
772 .description
= "Subnet mask",
773 .tag
= DHCP_SUBNET_MASK
,
774 .type
= &setting_type_ipv4
,
777 /** Default gateway setting */
778 const struct setting gateway_setting
__setting ( SETTING_IP
, gateway
) = {
780 .description
= "Default gateway",
782 .type
= &setting_type_ipv4
,
786 * Create IPv4 routing table based on configured settings
788 * @ret rc Return status code
790 static int ipv4_create_routes ( void ) {
791 struct ipv4_miniroute
*miniroute
;
792 struct ipv4_miniroute
*tmp
;
793 struct net_device
*netdev
;
794 struct settings
*settings
;
795 struct in_addr address
= { 0 };
796 struct in_addr netmask
= { 0 };
797 struct in_addr gateway
= { 0 };
799 /* Delete all existing routes */
800 list_for_each_entry_safe ( miniroute
, tmp
, &ipv4_miniroutes
, list
)
801 del_ipv4_miniroute ( miniroute
);
803 /* Create a route for each configured network device */
804 for_each_netdev ( netdev
) {
805 settings
= netdev_settings ( netdev
);
806 /* Get IPv4 address */
808 fetch_ipv4_setting ( settings
, &ip_setting
, &address
);
809 if ( ! address
.s_addr
)
811 /* Get subnet mask */
812 fetch_ipv4_setting ( settings
, &netmask_setting
, &netmask
);
813 /* Calculate default netmask, if necessary */
814 if ( ! netmask
.s_addr
) {
815 if ( IN_CLASSA ( ntohl ( address
.s_addr
) ) ) {
816 netmask
.s_addr
= htonl ( IN_CLASSA_NET
);
817 } else if ( IN_CLASSB ( ntohl ( address
.s_addr
) ) ) {
818 netmask
.s_addr
= htonl ( IN_CLASSB_NET
);
819 } else if ( IN_CLASSC ( ntohl ( address
.s_addr
) ) ) {
820 netmask
.s_addr
= htonl ( IN_CLASSC_NET
);
823 /* Get default gateway, if present */
824 fetch_ipv4_setting ( settings
, &gateway_setting
, &gateway
);
825 /* Configure route */
826 miniroute
= add_ipv4_miniroute ( netdev
, address
,
835 /** IPv4 settings applicator */
836 struct settings_applicator ipv4_settings_applicator __settings_applicator
= {
837 .apply
= ipv4_create_routes
,
841 REQUIRE_OBJECT ( icmpv4
);