176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <string.h> 276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <stdint.h> 376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <stdlib.h> 476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <stdio.h> 576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <errno.h> 676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <byteswap.h> 776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <gpxe/list.h> 876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <gpxe/in.h> 976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <gpxe/arp.h> 1076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <gpxe/if_ether.h> 1176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <gpxe/iobuf.h> 1276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <gpxe/netdevice.h> 1376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <gpxe/ip.h> 1476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <gpxe/tcpip.h> 1576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <gpxe/dhcp.h> 1676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <gpxe/settings.h> 1776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 1876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** @file 1976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 2076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * IPv4 protocol 2176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 2276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 2376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 2476d05dc695b06c4e987bb8078f78032441e1430cGreg HartmanFILE_LICENCE ( GPL2_OR_LATER ); 2576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 2676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/* Unique IP datagram identification number */ 2776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic uint16_t next_ident = 0; 2876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 2976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstruct net_protocol ipv4_protocol; 3076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 3176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** List of IPv4 miniroutes */ 3276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstruct list_head ipv4_miniroutes = LIST_HEAD_INIT ( ipv4_miniroutes ); 3376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 3476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** List of fragment reassembly buffers */ 3576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic LIST_HEAD ( frag_buffers ); 3676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 3776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** 3876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Add IPv4 minirouting table entry 3976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 4076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v netdev Network device 4176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v address IPv4 address 4276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v netmask Subnet mask 4376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v gateway Gateway address (if any) 4476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret miniroute Routing table entry, or NULL 4576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 4676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic struct ipv4_miniroute * __malloc 4776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanadd_ipv4_miniroute ( struct net_device *netdev, struct in_addr address, 4876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct in_addr netmask, struct in_addr gateway ) { 4976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct ipv4_miniroute *miniroute; 5076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 5176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBG ( "IPv4 add %s", inet_ntoa ( address ) ); 5276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBG ( "/%s ", inet_ntoa ( netmask ) ); 5376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( gateway.s_addr ) 5476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBG ( "gw %s ", inet_ntoa ( gateway ) ); 5576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBG ( "via %s\n", netdev->name ); 5676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 5776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Allocate and populate miniroute structure */ 5876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman miniroute = malloc ( sizeof ( *miniroute ) ); 5976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( ! miniroute ) { 6076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBG ( "IPv4 could not add miniroute\n" ); 6176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return NULL; 6276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 6376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 6476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Record routing information */ 6576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman miniroute->netdev = netdev_get ( netdev ); 6676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman miniroute->address = address; 6776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman miniroute->netmask = netmask; 6876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman miniroute->gateway = gateway; 6976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 7076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Add to end of list if we have a gateway, otherwise 7176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * to start of list. 7276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 7376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( gateway.s_addr ) { 7476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman list_add_tail ( &miniroute->list, &ipv4_miniroutes ); 7576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } else { 7676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman list_add ( &miniroute->list, &ipv4_miniroutes ); 7776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 7876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 7976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return miniroute; 8076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman} 8176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 8276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** 8376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Delete IPv4 minirouting table entry 8476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 8576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v miniroute Routing table entry 8676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 8776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic void del_ipv4_miniroute ( struct ipv4_miniroute *miniroute ) { 8876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 8976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBG ( "IPv4 del %s", inet_ntoa ( miniroute->address ) ); 9076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBG ( "/%s ", inet_ntoa ( miniroute->netmask ) ); 9176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( miniroute->gateway.s_addr ) 9276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBG ( "gw %s ", inet_ntoa ( miniroute->gateway ) ); 9376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBG ( "via %s\n", miniroute->netdev->name ); 9476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 9576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman netdev_put ( miniroute->netdev ); 9676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman list_del ( &miniroute->list ); 9776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman free ( miniroute ); 9876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman} 9976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 10076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** 10176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Perform IPv4 routing 10276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 10376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v dest Final destination address 10476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret dest Next hop destination address 10576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret miniroute Routing table entry to use, or NULL if no route 10676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 10776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * If the route requires use of a gateway, the next hop destination 10876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * address will be overwritten with the gateway address. 10976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 11076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic struct ipv4_miniroute * ipv4_route ( struct in_addr *dest ) { 11176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct ipv4_miniroute *miniroute; 11276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman int local; 11376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman int has_gw; 11476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 11576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Never attempt to route the broadcast address */ 11676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( dest->s_addr == INADDR_BROADCAST ) 11776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return NULL; 11876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 11976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Find first usable route in routing table */ 12076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman list_for_each_entry ( miniroute, &ipv4_miniroutes, list ) { 12176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( ! ( miniroute->netdev->state & NETDEV_OPEN ) ) 12276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman continue; 12376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman local = ( ( ( dest->s_addr ^ miniroute->address.s_addr ) 12476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman & miniroute->netmask.s_addr ) == 0 ); 12576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman has_gw = ( miniroute->gateway.s_addr ); 12676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( local || has_gw ) { 12776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( ! local ) 12876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *dest = miniroute->gateway; 12976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return miniroute; 13076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 13176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 13276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 13376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return NULL; 13476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman} 13576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 13676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** 13776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Fragment reassembly counter timeout 13876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 13976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v timer Retry timer 14076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v over If asserted, the timer is greater than @c MAX_TIMEOUT 14176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 14276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic void ipv4_frag_expired ( struct retry_timer *timer __unused, 14376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman int over ) { 14476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( over ) { 14576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBG ( "Fragment reassembly timeout" ); 14676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Free the fragment buffer */ 14776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 14876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman} 14976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 15076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** 15176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Free fragment buffer 15276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 15376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v fragbug Fragment buffer 15476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 15576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic void free_fragbuf ( struct frag_buffer *fragbuf ) { 15676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman free ( fragbuf ); 15776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman} 15876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 15976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** 16076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Fragment reassembler 16176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 16276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v iobuf I/O buffer, fragment of the datagram 16376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret frag_iob Reassembled packet, or NULL 16476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 16576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic struct io_buffer * ipv4_reassemble ( struct io_buffer * iobuf ) { 16676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct iphdr *iphdr = iobuf->data; 16776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct frag_buffer *fragbuf; 16876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 16976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /** 17076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Check if the fragment belongs to any fragment series 17176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 17276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman list_for_each_entry ( fragbuf, &frag_buffers, list ) { 17376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( fragbuf->ident == iphdr->ident && 17476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman fragbuf->src.s_addr == iphdr->src.s_addr ) { 17576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /** 17676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Check if the packet is the expected fragment 17776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 17876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * The offset of the new packet must be equal to the 17976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * length of the data accumulated so far (the length of 18076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * the reassembled I/O buffer 18176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 18276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( iob_len ( fragbuf->frag_iob ) == 18376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman ( iphdr->frags & IP_MASK_OFFSET ) ) { 18476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /** 18576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Append the contents of the fragment to the 18676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * reassembled I/O buffer 18776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 18876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman iob_pull ( iobuf, sizeof ( *iphdr ) ); 18976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman memcpy ( iob_put ( fragbuf->frag_iob, 19076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman iob_len ( iobuf ) ), 19176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman iobuf->data, iob_len ( iobuf ) ); 19276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman free_iob ( iobuf ); 19376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 19476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /** Check if the fragment series is over */ 19576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( ! ( iphdr->frags & IP_MASK_MOREFRAGS ) ) { 19676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman iobuf = fragbuf->frag_iob; 19776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman free_fragbuf ( fragbuf ); 19876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return iobuf; 19976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 20076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 20176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } else { 20276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Discard the fragment series */ 20376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman free_fragbuf ( fragbuf ); 20476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman free_iob ( iobuf ); 20576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 20676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return NULL; 20776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 20876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 20976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 21076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /** Check if the fragment is the first in the fragment series */ 21176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( iphdr->frags & IP_MASK_MOREFRAGS && 21276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman ( ( iphdr->frags & IP_MASK_OFFSET ) == 0 ) ) { 21376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 21476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /** Create a new fragment buffer */ 21576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman fragbuf = ( struct frag_buffer* ) malloc ( sizeof( *fragbuf ) ); 21676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman fragbuf->ident = iphdr->ident; 21776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman fragbuf->src = iphdr->src; 21876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 21976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Set up the reassembly I/O buffer */ 22076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman fragbuf->frag_iob = alloc_iob ( IP_FRAG_IOB_SIZE ); 22176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman iob_pull ( iobuf, sizeof ( *iphdr ) ); 22276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman memcpy ( iob_put ( fragbuf->frag_iob, iob_len ( iobuf ) ), 22376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman iobuf->data, iob_len ( iobuf ) ); 22476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman free_iob ( iobuf ); 22576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 22676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Set the reassembly timer */ 22776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman fragbuf->frag_timer.timeout = IP_FRAG_TIMEOUT; 22876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman fragbuf->frag_timer.expired = ipv4_frag_expired; 22976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman start_timer ( &fragbuf->frag_timer ); 23076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 23176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Add the fragment buffer to the list of fragment buffers */ 23276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman list_add ( &fragbuf->list, &frag_buffers ); 23376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 23476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 23576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return NULL; 23676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman} 23776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 23876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** 23976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Add IPv4 pseudo-header checksum to existing checksum 24076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 24176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v iobuf I/O buffer 24276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v csum Existing checksum 24376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret csum Updated checksum 24476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 24576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic uint16_t ipv4_pshdr_chksum ( struct io_buffer *iobuf, uint16_t csum ) { 24676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct ipv4_pseudo_header pshdr; 24776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct iphdr *iphdr = iobuf->data; 24876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman size_t hdrlen = ( ( iphdr->verhdrlen & IP_MASK_HLEN ) * 4 ); 24976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 25076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Build pseudo-header */ 25176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman pshdr.src = iphdr->src; 25276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman pshdr.dest = iphdr->dest; 25376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman pshdr.zero_padding = 0x00; 25476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman pshdr.protocol = iphdr->protocol; 25576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman pshdr.len = htons ( iob_len ( iobuf ) - hdrlen ); 25676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 25776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Update the checksum value */ 25876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return tcpip_continue_chksum ( csum, &pshdr, sizeof ( pshdr ) ); 25976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman} 26076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 26176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** 26276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Determine link-layer address 26376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 26476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v dest IPv4 destination address 26576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v src IPv4 source address 26676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v netdev Network device 26776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v ll_dest Link-layer destination address buffer 26876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret rc Return status code 26976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 27076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic int ipv4_ll_addr ( struct in_addr dest, struct in_addr src, 27176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct net_device *netdev, uint8_t *ll_dest ) { 27276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct ll_protocol *ll_protocol = netdev->ll_protocol; 27376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 27476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( dest.s_addr == INADDR_BROADCAST ) { 27576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Broadcast address */ 27676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman memcpy ( ll_dest, netdev->ll_broadcast, 27776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman ll_protocol->ll_addr_len ); 27876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return 0; 27976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } else if ( IN_MULTICAST ( ntohl ( dest.s_addr ) ) ) { 28076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return ll_protocol->mc_hash ( AF_INET, &dest, ll_dest ); 28176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } else { 28276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Unicast address: resolve via ARP */ 28376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return arp_resolve ( netdev, &ipv4_protocol, &dest, 28476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman &src, ll_dest ); 28576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 28676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman} 28776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 28876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** 28976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Transmit IP packet 29076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 29176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v iobuf I/O buffer 29276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v tcpip Transport-layer protocol 29376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v st_src Source network-layer address 29476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v st_dest Destination network-layer address 29576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v netdev Network device to use if no route found, or NULL 29676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v trans_csum Transport-layer checksum to complete, or NULL 29776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret rc Status 29876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 29976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * This function expects a transport-layer segment and prepends the IP header 30076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 30176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic int ipv4_tx ( struct io_buffer *iobuf, 30276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct tcpip_protocol *tcpip_protocol, 30376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct sockaddr_tcpip *st_src, 30476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct sockaddr_tcpip *st_dest, 30576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct net_device *netdev, 30676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman uint16_t *trans_csum ) { 30776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct iphdr *iphdr = iob_push ( iobuf, sizeof ( *iphdr ) ); 30876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct sockaddr_in *sin_src = ( ( struct sockaddr_in * ) st_src ); 30976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct sockaddr_in *sin_dest = ( ( struct sockaddr_in * ) st_dest ); 31076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct ipv4_miniroute *miniroute; 31176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct in_addr next_hop; 31276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman uint8_t ll_dest[MAX_LL_ADDR_LEN]; 31376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman int rc; 31476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 31576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Fill up the IP header, except source address */ 31676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman memset ( iphdr, 0, sizeof ( *iphdr ) ); 31776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman iphdr->verhdrlen = ( IP_VER | ( sizeof ( *iphdr ) / 4 ) ); 31876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman iphdr->service = IP_TOS; 31976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman iphdr->len = htons ( iob_len ( iobuf ) ); 32076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman iphdr->ident = htons ( ++next_ident ); 32176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman iphdr->ttl = IP_TTL; 32276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman iphdr->protocol = tcpip_protocol->tcpip_proto; 32376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman iphdr->dest = sin_dest->sin_addr; 32476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 32576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Use routing table to identify next hop and transmitting netdev */ 32676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman next_hop = iphdr->dest; 32776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( sin_src ) 32876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman iphdr->src = sin_src->sin_addr; 32976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( ( next_hop.s_addr != INADDR_BROADCAST ) && 33076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman ( ! IN_MULTICAST ( ntohl ( next_hop.s_addr ) ) ) && 33176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman ( ( miniroute = ipv4_route ( &next_hop ) ) != NULL ) ) { 33276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman iphdr->src = miniroute->address; 33376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman netdev = miniroute->netdev; 33476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 33576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( ! netdev ) { 33676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBG ( "IPv4 has no route to %s\n", inet_ntoa ( iphdr->dest ) ); 33776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman rc = -ENETUNREACH; 33876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman goto err; 33976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 34076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 34176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Determine link-layer destination address */ 34276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( ( rc = ipv4_ll_addr ( next_hop, iphdr->src, netdev, 34376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman ll_dest ) ) != 0 ) { 34476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBG ( "IPv4 has no link-layer address for %s: %s\n", 34576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman inet_ntoa ( next_hop ), strerror ( rc ) ); 34676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman goto err; 34776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 34876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 34976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Fix up checksums */ 35076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( trans_csum ) 35176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *trans_csum = ipv4_pshdr_chksum ( iobuf, *trans_csum ); 35276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman iphdr->chksum = tcpip_chksum ( iphdr, sizeof ( *iphdr ) ); 35376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 35476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Print IP4 header for debugging */ 35576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBG ( "IPv4 TX %s->", inet_ntoa ( iphdr->src ) ); 35676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBG ( "%s len %d proto %d id %04x csum %04x\n", 35776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman inet_ntoa ( iphdr->dest ), ntohs ( iphdr->len ), iphdr->protocol, 35876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman ntohs ( iphdr->ident ), ntohs ( iphdr->chksum ) ); 35976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 36076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Hand off to link layer */ 36176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( ( rc = net_tx ( iobuf, netdev, &ipv4_protocol, ll_dest ) ) != 0 ) { 36276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBG ( "IPv4 could not transmit packet via %s: %s\n", 36376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman netdev->name, strerror ( rc ) ); 36476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return rc; 36576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 36676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 36776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return 0; 36876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 36976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman err: 37076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman free_iob ( iobuf ); 37176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return rc; 37276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman} 37376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 37476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** 37576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Process incoming packets 37676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 37776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v iobuf I/O buffer 37876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v netdev Network device 37976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v ll_source Link-layer destination source 38076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 38176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * This function expects an IP4 network datagram. It processes the headers 38276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * and sends it to the transport layer. 38376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 38476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic int ipv4_rx ( struct io_buffer *iobuf, struct net_device *netdev __unused, 38576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman const void *ll_source __unused ) { 38676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct iphdr *iphdr = iobuf->data; 38776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman size_t hdrlen; 38876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman size_t len; 38976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman union { 39076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct sockaddr_in sin; 39176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct sockaddr_tcpip st; 39276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } src, dest; 39376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman uint16_t csum; 39476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman uint16_t pshdr_csum; 39576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman int rc; 39676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 39776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Sanity check the IPv4 header */ 39876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( iob_len ( iobuf ) < sizeof ( *iphdr ) ) { 39976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBG ( "IPv4 packet too short at %zd bytes (min %zd bytes)\n", 40076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman iob_len ( iobuf ), sizeof ( *iphdr ) ); 40176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman goto err; 40276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 40376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( ( iphdr->verhdrlen & IP_MASK_VER ) != IP_VER ) { 40476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBG ( "IPv4 version %#02x not supported\n", iphdr->verhdrlen ); 40576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman goto err; 40676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 40776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman hdrlen = ( ( iphdr->verhdrlen & IP_MASK_HLEN ) * 4 ); 40876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( hdrlen < sizeof ( *iphdr ) ) { 40976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBG ( "IPv4 header too short at %zd bytes (min %zd bytes)\n", 41076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman hdrlen, sizeof ( *iphdr ) ); 41176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman goto err; 41276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 41376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( hdrlen > iob_len ( iobuf ) ) { 41476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBG ( "IPv4 header too long at %zd bytes " 41576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman "(packet is %zd bytes)\n", hdrlen, iob_len ( iobuf ) ); 41676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman goto err; 41776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 41876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( ( csum = tcpip_chksum ( iphdr, hdrlen ) ) != 0 ) { 41976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBG ( "IPv4 checksum incorrect (is %04x including checksum " 42076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman "field, should be 0000)\n", csum ); 42176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman goto err; 42276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 42376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman len = ntohs ( iphdr->len ); 42476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( len < hdrlen ) { 42576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBG ( "IPv4 length too short at %zd bytes " 42676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman "(header is %zd bytes)\n", len, hdrlen ); 42776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman goto err; 42876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 42976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( len > iob_len ( iobuf ) ) { 43076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBG ( "IPv4 length too long at %zd bytes " 43176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman "(packet is %zd bytes)\n", len, iob_len ( iobuf ) ); 43276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman goto err; 43376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 43476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 43576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Print IPv4 header for debugging */ 43676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBG ( "IPv4 RX %s<-", inet_ntoa ( iphdr->dest ) ); 43776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBG ( "%s len %d proto %d id %04x csum %04x\n", 43876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman inet_ntoa ( iphdr->src ), ntohs ( iphdr->len ), iphdr->protocol, 43976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman ntohs ( iphdr->ident ), ntohs ( iphdr->chksum ) ); 44076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 44176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Truncate packet to correct length, calculate pseudo-header 44276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * checksum and then strip off the IPv4 header. 44376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 44476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman iob_unput ( iobuf, ( iob_len ( iobuf ) - len ) ); 44576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman pshdr_csum = ipv4_pshdr_chksum ( iobuf, TCPIP_EMPTY_CSUM ); 44676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman iob_pull ( iobuf, hdrlen ); 44776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 44876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Fragment reassembly */ 44976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( ( iphdr->frags & htons ( IP_MASK_MOREFRAGS ) ) || 45076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman ( ( iphdr->frags & htons ( IP_MASK_OFFSET ) ) != 0 ) ) { 45176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Pass the fragment to ipv4_reassemble() which either 45276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * returns a fully reassembled I/O buffer or NULL. 45376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 45476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman iobuf = ipv4_reassemble ( iobuf ); 45576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( ! iobuf ) 45676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return 0; 45776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 45876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 45976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Construct socket addresses and hand off to transport layer */ 46076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman memset ( &src, 0, sizeof ( src ) ); 46176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman src.sin.sin_family = AF_INET; 46276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman src.sin.sin_addr = iphdr->src; 46376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman memset ( &dest, 0, sizeof ( dest ) ); 46476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman dest.sin.sin_family = AF_INET; 46576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman dest.sin.sin_addr = iphdr->dest; 46676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( ( rc = tcpip_rx ( iobuf, iphdr->protocol, &src.st, 46776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman &dest.st, pshdr_csum ) ) != 0 ) { 46876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBG ( "IPv4 received packet rejected by stack: %s\n", 46976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman strerror ( rc ) ); 47076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return rc; 47176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 47276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 47376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return 0; 47476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 47576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman err: 47676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman free_iob ( iobuf ); 47776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return -EINVAL; 47876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman} 47976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 48076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** 48176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Check existence of IPv4 address for ARP 48276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 48376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v netdev Network device 48476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v net_addr Network-layer address 48576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret rc Return status code 48676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 48776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic int ipv4_arp_check ( struct net_device *netdev, const void *net_addr ) { 48876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman const struct in_addr *address = net_addr; 48976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct ipv4_miniroute *miniroute; 49076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 49176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman list_for_each_entry ( miniroute, &ipv4_miniroutes, list ) { 49276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( ( miniroute->netdev == netdev ) && 49376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman ( miniroute->address.s_addr == address->s_addr ) ) { 49476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Found matching address */ 49576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return 0; 49676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 49776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 49876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return -ENOENT; 49976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman} 50076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 50176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** 50276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Convert IPv4 address to dotted-quad notation 50376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 50476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v in IP address 50576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret string IP address in dotted-quad notation 50676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 50776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanchar * inet_ntoa ( struct in_addr in ) { 50876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman static char buf[16]; /* "xxx.xxx.xxx.xxx" */ 50976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman uint8_t *bytes = ( uint8_t * ) ∈ 51076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 51176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman sprintf ( buf, "%d.%d.%d.%d", bytes[0], bytes[1], bytes[2], bytes[3] ); 51276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return buf; 51376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman} 51476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 51576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** 51676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Transcribe IP address 51776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 51876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v net_addr IP address 51976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret string IP address in dotted-quad notation 52076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 52176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 52276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic const char * ipv4_ntoa ( const void *net_addr ) { 52376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return inet_ntoa ( * ( ( struct in_addr * ) net_addr ) ); 52476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman} 52576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 52676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** IPv4 protocol */ 52776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstruct net_protocol ipv4_protocol __net_protocol = { 52876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman .name = "IP", 52976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman .net_proto = htons ( ETH_P_IP ), 53076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman .net_addr_len = sizeof ( struct in_addr ), 53176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman .rx = ipv4_rx, 53276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman .ntoa = ipv4_ntoa, 53376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}; 53476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 53576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** IPv4 TCPIP net protocol */ 53676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstruct tcpip_net_protocol ipv4_tcpip_protocol __tcpip_net_protocol = { 53776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman .name = "IPv4", 53876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman .sa_family = AF_INET, 53976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman .tx = ipv4_tx, 54076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}; 54176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 54276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** IPv4 ARP protocol */ 54376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstruct arp_net_protocol ipv4_arp_protocol __arp_net_protocol = { 54476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman .net_protocol = &ipv4_protocol, 54576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman .check = ipv4_arp_check, 54676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}; 54776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 54876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/****************************************************************************** 54976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 55076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Settings 55176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 55276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman ****************************************************************************** 55376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 55476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 55576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** IPv4 address setting */ 55676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstruct setting ip_setting __setting = { 55776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman .name = "ip", 55876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman .description = "IPv4 address", 55976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman .tag = DHCP_EB_YIADDR, 56076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman .type = &setting_type_ipv4, 56176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}; 56276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 56376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** IPv4 subnet mask setting */ 56476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstruct setting netmask_setting __setting = { 56576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman .name = "netmask", 56676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman .description = "IPv4 subnet mask", 56776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman .tag = DHCP_SUBNET_MASK, 56876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman .type = &setting_type_ipv4, 56976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}; 57076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 57176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** Default gateway setting */ 57276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstruct setting gateway_setting __setting = { 57376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman .name = "gateway", 57476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman .description = "Default gateway", 57576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman .tag = DHCP_ROUTERS, 57676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman .type = &setting_type_ipv4, 57776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}; 57876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 57976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** 58076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Create IPv4 routing table based on configured settings 58176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 58276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret rc Return status code 58376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 58476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic int ipv4_create_routes ( void ) { 58576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct ipv4_miniroute *miniroute; 58676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct ipv4_miniroute *tmp; 58776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct net_device *netdev; 58876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct settings *settings; 58976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct in_addr address = { 0 }; 59076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct in_addr netmask = { 0 }; 59176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct in_addr gateway = { 0 }; 59276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 59376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Delete all existing routes */ 59476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman list_for_each_entry_safe ( miniroute, tmp, &ipv4_miniroutes, list ) 59576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman del_ipv4_miniroute ( miniroute ); 59676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 59776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Create a route for each configured network device */ 59876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman for_each_netdev ( netdev ) { 59976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman settings = netdev_settings ( netdev ); 60076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Get IPv4 address */ 60176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman address.s_addr = 0; 60276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman fetch_ipv4_setting ( settings, &ip_setting, &address ); 60376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( ! address.s_addr ) 60476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman continue; 60576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Get subnet mask */ 60676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman fetch_ipv4_setting ( settings, &netmask_setting, &netmask ); 60776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Calculate default netmask, if necessary */ 60876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( ! netmask.s_addr ) { 60976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( IN_CLASSA ( ntohl ( address.s_addr ) ) ) { 61076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman netmask.s_addr = htonl ( IN_CLASSA_NET ); 61176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } else if ( IN_CLASSB ( ntohl ( address.s_addr ) ) ) { 61276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman netmask.s_addr = htonl ( IN_CLASSB_NET ); 61376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } else if ( IN_CLASSC ( ntohl ( address.s_addr ) ) ) { 61476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman netmask.s_addr = htonl ( IN_CLASSC_NET ); 61576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 61676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 61776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Get default gateway, if present */ 61876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman fetch_ipv4_setting ( settings, &gateway_setting, &gateway ); 61976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Configure route */ 62076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman miniroute = add_ipv4_miniroute ( netdev, address, 62176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman netmask, gateway ); 62276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( ! miniroute ) 62376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return -ENOMEM; 62476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 62576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 62676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return 0; 62776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman} 62876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 62976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** IPv4 settings applicator */ 63076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstruct settings_applicator ipv4_settings_applicator __settings_applicator = { 63176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman .apply = ipv4_create_routes, 63276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}; 63376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 63476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/* Drag in ICMP */ 63576d05dc695b06c4e987bb8078f78032441e1430cGreg HartmanREQUIRE_OBJECT ( icmp ); 636