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