[e16e8f2] | 1 | #include <string.h> |
---|
| 2 | #include <stdint.h> |
---|
| 3 | #include <stdlib.h> |
---|
| 4 | #include <stdio.h> |
---|
| 5 | #include <errno.h> |
---|
| 6 | #include <byteswap.h> |
---|
| 7 | #include <gpxe/list.h> |
---|
| 8 | #include <gpxe/in.h> |
---|
| 9 | #include <gpxe/arp.h> |
---|
| 10 | #include <gpxe/if_ether.h> |
---|
| 11 | #include <gpxe/iobuf.h> |
---|
| 12 | #include <gpxe/netdevice.h> |
---|
| 13 | #include <gpxe/ip.h> |
---|
| 14 | #include <gpxe/tcpip.h> |
---|
| 15 | #include <gpxe/dhcp.h> |
---|
| 16 | #include <gpxe/settings.h> |
---|
| 17 | |
---|
| 18 | /** @file |
---|
| 19 | * |
---|
| 20 | * IPv4 protocol |
---|
| 21 | * |
---|
| 22 | */ |
---|
| 23 | |
---|
| 24 | FILE_LICENCE ( GPL2_OR_LATER ); |
---|
| 25 | |
---|
| 26 | /* Unique IP datagram identification number */ |
---|
| 27 | static uint16_t next_ident = 0; |
---|
| 28 | |
---|
| 29 | struct net_protocol ipv4_protocol; |
---|
| 30 | |
---|
| 31 | /** List of IPv4 miniroutes */ |
---|
| 32 | struct list_head ipv4_miniroutes = LIST_HEAD_INIT ( ipv4_miniroutes ); |
---|
| 33 | |
---|
| 34 | /** List of fragment reassembly buffers */ |
---|
| 35 | static LIST_HEAD ( frag_buffers ); |
---|
| 36 | |
---|
| 37 | /** |
---|
| 38 | * Add IPv4 minirouting table entry |
---|
| 39 | * |
---|
| 40 | * @v netdev Network device |
---|
| 41 | * @v address IPv4 address |
---|
| 42 | * @v netmask Subnet mask |
---|
| 43 | * @v gateway Gateway address (if any) |
---|
| 44 | * @ret miniroute Routing table entry, or NULL |
---|
| 45 | */ |
---|
| 46 | static struct ipv4_miniroute * __malloc |
---|
| 47 | add_ipv4_miniroute ( struct net_device *netdev, struct in_addr address, |
---|
| 48 | struct in_addr netmask, struct in_addr gateway ) { |
---|
| 49 | struct ipv4_miniroute *miniroute; |
---|
| 50 | |
---|
| 51 | DBG ( "IPv4 add %s", inet_ntoa ( address ) ); |
---|
| 52 | DBG ( "/%s ", inet_ntoa ( netmask ) ); |
---|
| 53 | if ( gateway.s_addr ) |
---|
| 54 | DBG ( "gw %s ", inet_ntoa ( gateway ) ); |
---|
| 55 | DBG ( "via %s\n", netdev->name ); |
---|
| 56 | |
---|
| 57 | /* Allocate and populate miniroute structure */ |
---|
| 58 | miniroute = malloc ( sizeof ( *miniroute ) ); |
---|
| 59 | if ( ! miniroute ) { |
---|
| 60 | DBG ( "IPv4 could not add miniroute\n" ); |
---|
| 61 | return NULL; |
---|
| 62 | } |
---|
| 63 | |
---|
| 64 | /* Record routing information */ |
---|
| 65 | miniroute->netdev = netdev_get ( netdev ); |
---|
| 66 | miniroute->address = address; |
---|
| 67 | miniroute->netmask = netmask; |
---|
| 68 | miniroute->gateway = gateway; |
---|
| 69 | |
---|
| 70 | /* Add to end of list if we have a gateway, otherwise |
---|
| 71 | * to start of list. |
---|
| 72 | */ |
---|
| 73 | if ( gateway.s_addr ) { |
---|
| 74 | list_add_tail ( &miniroute->list, &ipv4_miniroutes ); |
---|
| 75 | } else { |
---|
| 76 | list_add ( &miniroute->list, &ipv4_miniroutes ); |
---|
| 77 | } |
---|
| 78 | |
---|
| 79 | return miniroute; |
---|
| 80 | } |
---|
| 81 | |
---|
| 82 | /** |
---|
| 83 | * Delete IPv4 minirouting table entry |
---|
| 84 | * |
---|
| 85 | * @v miniroute Routing table entry |
---|
| 86 | */ |
---|
| 87 | static void del_ipv4_miniroute ( struct ipv4_miniroute *miniroute ) { |
---|
| 88 | |
---|
| 89 | DBG ( "IPv4 del %s", inet_ntoa ( miniroute->address ) ); |
---|
| 90 | DBG ( "/%s ", inet_ntoa ( miniroute->netmask ) ); |
---|
| 91 | if ( miniroute->gateway.s_addr ) |
---|
| 92 | DBG ( "gw %s ", inet_ntoa ( miniroute->gateway ) ); |
---|
| 93 | DBG ( "via %s\n", miniroute->netdev->name ); |
---|
| 94 | |
---|
| 95 | netdev_put ( miniroute->netdev ); |
---|
| 96 | list_del ( &miniroute->list ); |
---|
| 97 | free ( miniroute ); |
---|
| 98 | } |
---|
| 99 | |
---|
| 100 | /** |
---|
| 101 | * Perform IPv4 routing |
---|
| 102 | * |
---|
| 103 | * @v dest Final destination address |
---|
| 104 | * @ret dest Next hop destination address |
---|
| 105 | * @ret miniroute Routing table entry to use, or NULL if no route |
---|
| 106 | * |
---|
| 107 | * If the route requires use of a gateway, the next hop destination |
---|
| 108 | * address will be overwritten with the gateway address. |
---|
| 109 | */ |
---|
| 110 | static struct ipv4_miniroute * ipv4_route ( struct in_addr *dest ) { |
---|
| 111 | struct ipv4_miniroute *miniroute; |
---|
| 112 | int local; |
---|
| 113 | int has_gw; |
---|
| 114 | |
---|
| 115 | /* Never attempt to route the broadcast address */ |
---|
| 116 | if ( dest->s_addr == INADDR_BROADCAST ) |
---|
| 117 | return NULL; |
---|
| 118 | |
---|
| 119 | /* Find first usable route in routing table */ |
---|
| 120 | list_for_each_entry ( miniroute, &ipv4_miniroutes, list ) { |
---|
| 121 | if ( ! ( miniroute->netdev->state & NETDEV_OPEN ) ) |
---|
| 122 | continue; |
---|
| 123 | local = ( ( ( dest->s_addr ^ miniroute->address.s_addr ) |
---|
| 124 | & miniroute->netmask.s_addr ) == 0 ); |
---|
| 125 | has_gw = ( miniroute->gateway.s_addr ); |
---|
| 126 | if ( local || has_gw ) { |
---|
| 127 | if ( ! local ) |
---|
| 128 | *dest = miniroute->gateway; |
---|
| 129 | return miniroute; |
---|
| 130 | } |
---|
| 131 | } |
---|
| 132 | |
---|
| 133 | return NULL; |
---|
| 134 | } |
---|
| 135 | |
---|
| 136 | /** |
---|
| 137 | * Fragment reassembly counter timeout |
---|
| 138 | * |
---|
| 139 | * @v timer Retry timer |
---|
| 140 | * @v over If asserted, the timer is greater than @c MAX_TIMEOUT |
---|
| 141 | */ |
---|
| 142 | static void ipv4_frag_expired ( struct retry_timer *timer __unused, |
---|
| 143 | int over ) { |
---|
| 144 | if ( over ) { |
---|
| 145 | DBG ( "Fragment reassembly timeout" ); |
---|
| 146 | /* Free the fragment buffer */ |
---|
| 147 | } |
---|
| 148 | } |
---|
| 149 | |
---|
| 150 | /** |
---|
| 151 | * Free fragment buffer |
---|
| 152 | * |
---|
| 153 | * @v fragbug Fragment buffer |
---|
| 154 | */ |
---|
| 155 | static void free_fragbuf ( struct frag_buffer *fragbuf ) { |
---|
| 156 | free ( fragbuf ); |
---|
| 157 | } |
---|
| 158 | |
---|
| 159 | /** |
---|
| 160 | * Fragment reassembler |
---|
| 161 | * |
---|
| 162 | * @v iobuf I/O buffer, fragment of the datagram |
---|
| 163 | * @ret frag_iob Reassembled packet, or NULL |
---|
| 164 | */ |
---|
| 165 | static struct io_buffer * ipv4_reassemble ( struct io_buffer * iobuf ) { |
---|
| 166 | struct iphdr *iphdr = iobuf->data; |
---|
| 167 | struct frag_buffer *fragbuf; |
---|
| 168 | |
---|
| 169 | /** |
---|
| 170 | * Check if the fragment belongs to any fragment series |
---|
| 171 | */ |
---|
| 172 | list_for_each_entry ( fragbuf, &frag_buffers, list ) { |
---|
| 173 | if ( fragbuf->ident == iphdr->ident && |
---|
| 174 | fragbuf->src.s_addr == iphdr->src.s_addr ) { |
---|
| 175 | /** |
---|
| 176 | * Check if the packet is the expected fragment |
---|
| 177 | * |
---|
| 178 | * The offset of the new packet must be equal to the |
---|
| 179 | * length of the data accumulated so far (the length of |
---|
| 180 | * the reassembled I/O buffer |
---|
| 181 | */ |
---|
| 182 | if ( iob_len ( fragbuf->frag_iob ) == |
---|
| 183 | ( iphdr->frags & IP_MASK_OFFSET ) ) { |
---|
| 184 | /** |
---|
| 185 | * Append the contents of the fragment to the |
---|
| 186 | * reassembled I/O buffer |
---|
| 187 | */ |
---|
| 188 | iob_pull ( iobuf, sizeof ( *iphdr ) ); |
---|
| 189 | memcpy ( iob_put ( fragbuf->frag_iob, |
---|
| 190 | iob_len ( iobuf ) ), |
---|
| 191 | iobuf->data, iob_len ( iobuf ) ); |
---|
| 192 | free_iob ( iobuf ); |
---|
| 193 | |
---|
| 194 | /** Check if the fragment series is over */ |
---|
| 195 | if ( ! ( iphdr->frags & IP_MASK_MOREFRAGS ) ) { |
---|
| 196 | iobuf = fragbuf->frag_iob; |
---|
| 197 | free_fragbuf ( fragbuf ); |
---|
| 198 | return iobuf; |
---|
| 199 | } |
---|
| 200 | |
---|
| 201 | } else { |
---|
| 202 | /* Discard the fragment series */ |
---|
| 203 | free_fragbuf ( fragbuf ); |
---|
| 204 | free_iob ( iobuf ); |
---|
| 205 | } |
---|
| 206 | return NULL; |
---|
| 207 | } |
---|
| 208 | } |
---|
| 209 | |
---|
| 210 | /** Check if the fragment is the first in the fragment series */ |
---|
| 211 | if ( iphdr->frags & IP_MASK_MOREFRAGS && |
---|
| 212 | ( ( iphdr->frags & IP_MASK_OFFSET ) == 0 ) ) { |
---|
| 213 | |
---|
| 214 | /** Create a new fragment buffer */ |
---|
| 215 | fragbuf = ( struct frag_buffer* ) malloc ( sizeof( *fragbuf ) ); |
---|
| 216 | fragbuf->ident = iphdr->ident; |
---|
| 217 | fragbuf->src = iphdr->src; |
---|
| 218 | |
---|
| 219 | /* Set up the reassembly I/O buffer */ |
---|
| 220 | fragbuf->frag_iob = alloc_iob ( IP_FRAG_IOB_SIZE ); |
---|
| 221 | iob_pull ( iobuf, sizeof ( *iphdr ) ); |
---|
| 222 | memcpy ( iob_put ( fragbuf->frag_iob, iob_len ( iobuf ) ), |
---|
| 223 | iobuf->data, iob_len ( iobuf ) ); |
---|
| 224 | free_iob ( iobuf ); |
---|
| 225 | |
---|
| 226 | /* Set the reassembly timer */ |
---|
| 227 | fragbuf->frag_timer.timeout = IP_FRAG_TIMEOUT; |
---|
| 228 | fragbuf->frag_timer.expired = ipv4_frag_expired; |
---|
| 229 | start_timer ( &fragbuf->frag_timer ); |
---|
| 230 | |
---|
| 231 | /* Add the fragment buffer to the list of fragment buffers */ |
---|
| 232 | list_add ( &fragbuf->list, &frag_buffers ); |
---|
| 233 | } |
---|
| 234 | |
---|
| 235 | return NULL; |
---|
| 236 | } |
---|
| 237 | |
---|
| 238 | /** |
---|
| 239 | * Add IPv4 pseudo-header checksum to existing checksum |
---|
| 240 | * |
---|
| 241 | * @v iobuf I/O buffer |
---|
| 242 | * @v csum Existing checksum |
---|
| 243 | * @ret csum Updated checksum |
---|
| 244 | */ |
---|
| 245 | static uint16_t ipv4_pshdr_chksum ( struct io_buffer *iobuf, uint16_t csum ) { |
---|
| 246 | struct ipv4_pseudo_header pshdr; |
---|
| 247 | struct iphdr *iphdr = iobuf->data; |
---|
| 248 | size_t hdrlen = ( ( iphdr->verhdrlen & IP_MASK_HLEN ) * 4 ); |
---|
| 249 | |
---|
| 250 | /* Build pseudo-header */ |
---|
| 251 | pshdr.src = iphdr->src; |
---|
| 252 | pshdr.dest = iphdr->dest; |
---|
| 253 | pshdr.zero_padding = 0x00; |
---|
| 254 | pshdr.protocol = iphdr->protocol; |
---|
| 255 | pshdr.len = htons ( iob_len ( iobuf ) - hdrlen ); |
---|
| 256 | |
---|
| 257 | /* Update the checksum value */ |
---|
| 258 | return tcpip_continue_chksum ( csum, &pshdr, sizeof ( pshdr ) ); |
---|
| 259 | } |
---|
| 260 | |
---|
| 261 | /** |
---|
| 262 | * Determine link-layer address |
---|
| 263 | * |
---|
| 264 | * @v dest IPv4 destination address |
---|
| 265 | * @v src IPv4 source address |
---|
| 266 | * @v netdev Network device |
---|
| 267 | * @v ll_dest Link-layer destination address buffer |
---|
| 268 | * @ret rc Return status code |
---|
| 269 | */ |
---|
| 270 | static int ipv4_ll_addr ( struct in_addr dest, struct in_addr src, |
---|
| 271 | struct net_device *netdev, uint8_t *ll_dest ) { |
---|
| 272 | struct ll_protocol *ll_protocol = netdev->ll_protocol; |
---|
| 273 | |
---|
| 274 | if ( dest.s_addr == INADDR_BROADCAST ) { |
---|
| 275 | /* Broadcast address */ |
---|
| 276 | memcpy ( ll_dest, netdev->ll_broadcast, |
---|
| 277 | ll_protocol->ll_addr_len ); |
---|
| 278 | return 0; |
---|
| 279 | } else if ( IN_MULTICAST ( ntohl ( dest.s_addr ) ) ) { |
---|
| 280 | return ll_protocol->mc_hash ( AF_INET, &dest, ll_dest ); |
---|
| 281 | } else { |
---|
| 282 | /* Unicast address: resolve via ARP */ |
---|
| 283 | return arp_resolve ( netdev, &ipv4_protocol, &dest, |
---|
| 284 | &src, ll_dest ); |
---|
| 285 | } |
---|
| 286 | } |
---|
| 287 | |
---|
| 288 | /** |
---|
| 289 | * Transmit IP packet |
---|
| 290 | * |
---|
| 291 | * @v iobuf I/O buffer |
---|
| 292 | * @v tcpip Transport-layer protocol |
---|
| 293 | * @v st_src Source network-layer address |
---|
| 294 | * @v st_dest Destination network-layer address |
---|
| 295 | * @v netdev Network device to use if no route found, or NULL |
---|
| 296 | * @v trans_csum Transport-layer checksum to complete, or NULL |
---|
| 297 | * @ret rc Status |
---|
| 298 | * |
---|
| 299 | * This function expects a transport-layer segment and prepends the IP header |
---|
| 300 | */ |
---|
| 301 | static int ipv4_tx ( struct io_buffer *iobuf, |
---|
| 302 | struct tcpip_protocol *tcpip_protocol, |
---|
| 303 | struct sockaddr_tcpip *st_src, |
---|
| 304 | struct sockaddr_tcpip *st_dest, |
---|
| 305 | struct net_device *netdev, |
---|
| 306 | uint16_t *trans_csum ) { |
---|
| 307 | struct iphdr *iphdr = iob_push ( iobuf, sizeof ( *iphdr ) ); |
---|
| 308 | struct sockaddr_in *sin_src = ( ( struct sockaddr_in * ) st_src ); |
---|
| 309 | struct sockaddr_in *sin_dest = ( ( struct sockaddr_in * ) st_dest ); |
---|
| 310 | struct ipv4_miniroute *miniroute; |
---|
| 311 | struct in_addr next_hop; |
---|
| 312 | uint8_t ll_dest[MAX_LL_ADDR_LEN]; |
---|
| 313 | int rc; |
---|
| 314 | |
---|
| 315 | /* Fill up the IP header, except source address */ |
---|
| 316 | memset ( iphdr, 0, sizeof ( *iphdr ) ); |
---|
| 317 | iphdr->verhdrlen = ( IP_VER | ( sizeof ( *iphdr ) / 4 ) ); |
---|
| 318 | iphdr->service = IP_TOS; |
---|
| 319 | iphdr->len = htons ( iob_len ( iobuf ) ); |
---|
| 320 | iphdr->ident = htons ( ++next_ident ); |
---|
| 321 | iphdr->ttl = IP_TTL; |
---|
| 322 | iphdr->protocol = tcpip_protocol->tcpip_proto; |
---|
| 323 | iphdr->dest = sin_dest->sin_addr; |
---|
| 324 | |
---|
| 325 | /* Use routing table to identify next hop and transmitting netdev */ |
---|
| 326 | next_hop = iphdr->dest; |
---|
| 327 | if ( sin_src ) |
---|
| 328 | iphdr->src = sin_src->sin_addr; |
---|
| 329 | if ( ( next_hop.s_addr != INADDR_BROADCAST ) && |
---|
| 330 | ( ! IN_MULTICAST ( ntohl ( next_hop.s_addr ) ) ) && |
---|
| 331 | ( ( miniroute = ipv4_route ( &next_hop ) ) != NULL ) ) { |
---|
| 332 | iphdr->src = miniroute->address; |
---|
| 333 | netdev = miniroute->netdev; |
---|
| 334 | } |
---|
| 335 | if ( ! netdev ) { |
---|
| 336 | DBG ( "IPv4 has no route to %s\n", inet_ntoa ( iphdr->dest ) ); |
---|
| 337 | rc = -ENETUNREACH; |
---|
| 338 | goto err; |
---|
| 339 | } |
---|
| 340 | |
---|
| 341 | /* Determine link-layer destination address */ |
---|
| 342 | if ( ( rc = ipv4_ll_addr ( next_hop, iphdr->src, netdev, |
---|
| 343 | ll_dest ) ) != 0 ) { |
---|
| 344 | DBG ( "IPv4 has no link-layer address for %s: %s\n", |
---|
| 345 | inet_ntoa ( next_hop ), strerror ( rc ) ); |
---|
| 346 | goto err; |
---|
| 347 | } |
---|
| 348 | |
---|
| 349 | /* Fix up checksums */ |
---|
| 350 | if ( trans_csum ) |
---|
| 351 | *trans_csum = ipv4_pshdr_chksum ( iobuf, *trans_csum ); |
---|
| 352 | iphdr->chksum = tcpip_chksum ( iphdr, sizeof ( *iphdr ) ); |
---|
| 353 | |
---|
| 354 | /* Print IP4 header for debugging */ |
---|
| 355 | DBG ( "IPv4 TX %s->", inet_ntoa ( iphdr->src ) ); |
---|
| 356 | DBG ( "%s len %d proto %d id %04x csum %04x\n", |
---|
| 357 | inet_ntoa ( iphdr->dest ), ntohs ( iphdr->len ), iphdr->protocol, |
---|
| 358 | ntohs ( iphdr->ident ), ntohs ( iphdr->chksum ) ); |
---|
| 359 | |
---|
| 360 | /* Hand off to link layer */ |
---|
| 361 | if ( ( rc = net_tx ( iobuf, netdev, &ipv4_protocol, ll_dest ) ) != 0 ) { |
---|
| 362 | DBG ( "IPv4 could not transmit packet via %s: %s\n", |
---|
| 363 | netdev->name, strerror ( rc ) ); |
---|
| 364 | return rc; |
---|
| 365 | } |
---|
| 366 | |
---|
| 367 | return 0; |
---|
| 368 | |
---|
| 369 | err: |
---|
| 370 | free_iob ( iobuf ); |
---|
| 371 | return rc; |
---|
| 372 | } |
---|
| 373 | |
---|
| 374 | /** |
---|
| 375 | * Process incoming packets |
---|
| 376 | * |
---|
| 377 | * @v iobuf I/O buffer |
---|
| 378 | * @v netdev Network device |
---|
| 379 | * @v ll_source Link-layer destination source |
---|
| 380 | * |
---|
| 381 | * This function expects an IP4 network datagram. It processes the headers |
---|
| 382 | * and sends it to the transport layer. |
---|
| 383 | */ |
---|
| 384 | static int ipv4_rx ( struct io_buffer *iobuf, struct net_device *netdev __unused, |
---|
| 385 | const void *ll_source __unused ) { |
---|
| 386 | struct iphdr *iphdr = iobuf->data; |
---|
| 387 | size_t hdrlen; |
---|
| 388 | size_t len; |
---|
| 389 | union { |
---|
| 390 | struct sockaddr_in sin; |
---|
| 391 | struct sockaddr_tcpip st; |
---|
| 392 | } src, dest; |
---|
| 393 | uint16_t csum; |
---|
| 394 | uint16_t pshdr_csum; |
---|
| 395 | int rc; |
---|
| 396 | |
---|
| 397 | /* Sanity check the IPv4 header */ |
---|
| 398 | if ( iob_len ( iobuf ) < sizeof ( *iphdr ) ) { |
---|
| 399 | DBG ( "IPv4 packet too short at %zd bytes (min %zd bytes)\n", |
---|
| 400 | iob_len ( iobuf ), sizeof ( *iphdr ) ); |
---|
| 401 | goto err; |
---|
| 402 | } |
---|
| 403 | if ( ( iphdr->verhdrlen & IP_MASK_VER ) != IP_VER ) { |
---|
| 404 | DBG ( "IPv4 version %#02x not supported\n", iphdr->verhdrlen ); |
---|
| 405 | goto err; |
---|
| 406 | } |
---|
| 407 | hdrlen = ( ( iphdr->verhdrlen & IP_MASK_HLEN ) * 4 ); |
---|
| 408 | if ( hdrlen < sizeof ( *iphdr ) ) { |
---|
| 409 | DBG ( "IPv4 header too short at %zd bytes (min %zd bytes)\n", |
---|
| 410 | hdrlen, sizeof ( *iphdr ) ); |
---|
| 411 | goto err; |
---|
| 412 | } |
---|
| 413 | if ( hdrlen > iob_len ( iobuf ) ) { |
---|
| 414 | DBG ( "IPv4 header too long at %zd bytes " |
---|
| 415 | "(packet is %zd bytes)\n", hdrlen, iob_len ( iobuf ) ); |
---|
| 416 | goto err; |
---|
| 417 | } |
---|
| 418 | if ( ( csum = tcpip_chksum ( iphdr, hdrlen ) ) != 0 ) { |
---|
| 419 | DBG ( "IPv4 checksum incorrect (is %04x including checksum " |
---|
| 420 | "field, should be 0000)\n", csum ); |
---|
| 421 | goto err; |
---|
| 422 | } |
---|
| 423 | len = ntohs ( iphdr->len ); |
---|
| 424 | if ( len < hdrlen ) { |
---|
| 425 | DBG ( "IPv4 length too short at %zd bytes " |
---|
| 426 | "(header is %zd bytes)\n", len, hdrlen ); |
---|
| 427 | goto err; |
---|
| 428 | } |
---|
| 429 | if ( len > iob_len ( iobuf ) ) { |
---|
| 430 | DBG ( "IPv4 length too long at %zd bytes " |
---|
| 431 | "(packet is %zd bytes)\n", len, iob_len ( iobuf ) ); |
---|
| 432 | goto err; |
---|
| 433 | } |
---|
| 434 | |
---|
| 435 | /* Print IPv4 header for debugging */ |
---|
| 436 | DBG ( "IPv4 RX %s<-", inet_ntoa ( iphdr->dest ) ); |
---|
| 437 | DBG ( "%s len %d proto %d id %04x csum %04x\n", |
---|
| 438 | inet_ntoa ( iphdr->src ), ntohs ( iphdr->len ), iphdr->protocol, |
---|
| 439 | ntohs ( iphdr->ident ), ntohs ( iphdr->chksum ) ); |
---|
| 440 | |
---|
| 441 | /* Truncate packet to correct length, calculate pseudo-header |
---|
| 442 | * checksum and then strip off the IPv4 header. |
---|
| 443 | */ |
---|
| 444 | iob_unput ( iobuf, ( iob_len ( iobuf ) - len ) ); |
---|
| 445 | pshdr_csum = ipv4_pshdr_chksum ( iobuf, TCPIP_EMPTY_CSUM ); |
---|
| 446 | iob_pull ( iobuf, hdrlen ); |
---|
| 447 | |
---|
| 448 | /* Fragment reassembly */ |
---|
| 449 | if ( ( iphdr->frags & htons ( IP_MASK_MOREFRAGS ) ) || |
---|
| 450 | ( ( iphdr->frags & htons ( IP_MASK_OFFSET ) ) != 0 ) ) { |
---|
| 451 | /* Pass the fragment to ipv4_reassemble() which either |
---|
| 452 | * returns a fully reassembled I/O buffer or NULL. |
---|
| 453 | */ |
---|
| 454 | iobuf = ipv4_reassemble ( iobuf ); |
---|
| 455 | if ( ! iobuf ) |
---|
| 456 | return 0; |
---|
| 457 | } |
---|
| 458 | |
---|
| 459 | /* Construct socket addresses and hand off to transport layer */ |
---|
| 460 | memset ( &src, 0, sizeof ( src ) ); |
---|
| 461 | src.sin.sin_family = AF_INET; |
---|
| 462 | src.sin.sin_addr = iphdr->src; |
---|
| 463 | memset ( &dest, 0, sizeof ( dest ) ); |
---|
| 464 | dest.sin.sin_family = AF_INET; |
---|
| 465 | dest.sin.sin_addr = iphdr->dest; |
---|
| 466 | if ( ( rc = tcpip_rx ( iobuf, iphdr->protocol, &src.st, |
---|
| 467 | &dest.st, pshdr_csum ) ) != 0 ) { |
---|
| 468 | DBG ( "IPv4 received packet rejected by stack: %s\n", |
---|
| 469 | strerror ( rc ) ); |
---|
| 470 | return rc; |
---|
| 471 | } |
---|
| 472 | |
---|
| 473 | return 0; |
---|
| 474 | |
---|
| 475 | err: |
---|
| 476 | free_iob ( iobuf ); |
---|
| 477 | return -EINVAL; |
---|
| 478 | } |
---|
| 479 | |
---|
| 480 | /** |
---|
| 481 | * Check existence of IPv4 address for ARP |
---|
| 482 | * |
---|
| 483 | * @v netdev Network device |
---|
| 484 | * @v net_addr Network-layer address |
---|
| 485 | * @ret rc Return status code |
---|
| 486 | */ |
---|
| 487 | static int ipv4_arp_check ( struct net_device *netdev, const void *net_addr ) { |
---|
| 488 | const struct in_addr *address = net_addr; |
---|
| 489 | struct ipv4_miniroute *miniroute; |
---|
| 490 | |
---|
| 491 | list_for_each_entry ( miniroute, &ipv4_miniroutes, list ) { |
---|
| 492 | if ( ( miniroute->netdev == netdev ) && |
---|
| 493 | ( miniroute->address.s_addr == address->s_addr ) ) { |
---|
| 494 | /* Found matching address */ |
---|
| 495 | return 0; |
---|
| 496 | } |
---|
| 497 | } |
---|
| 498 | return -ENOENT; |
---|
| 499 | } |
---|
| 500 | |
---|
| 501 | /** |
---|
| 502 | * Convert IPv4 address to dotted-quad notation |
---|
| 503 | * |
---|
| 504 | * @v in IP address |
---|
| 505 | * @ret string IP address in dotted-quad notation |
---|
| 506 | */ |
---|
| 507 | char * inet_ntoa ( struct in_addr in ) { |
---|
| 508 | static char buf[16]; /* "xxx.xxx.xxx.xxx" */ |
---|
| 509 | uint8_t *bytes = ( uint8_t * ) ∈ |
---|
| 510 | |
---|
| 511 | sprintf ( buf, "%d.%d.%d.%d", bytes[0], bytes[1], bytes[2], bytes[3] ); |
---|
| 512 | return buf; |
---|
| 513 | } |
---|
| 514 | |
---|
| 515 | /** |
---|
| 516 | * Transcribe IP address |
---|
| 517 | * |
---|
| 518 | * @v net_addr IP address |
---|
| 519 | * @ret string IP address in dotted-quad notation |
---|
| 520 | * |
---|
| 521 | */ |
---|
| 522 | static const char * ipv4_ntoa ( const void *net_addr ) { |
---|
| 523 | return inet_ntoa ( * ( ( struct in_addr * ) net_addr ) ); |
---|
| 524 | } |
---|
| 525 | |
---|
| 526 | /** IPv4 protocol */ |
---|
| 527 | struct net_protocol ipv4_protocol __net_protocol = { |
---|
| 528 | .name = "IP", |
---|
| 529 | .net_proto = htons ( ETH_P_IP ), |
---|
| 530 | .net_addr_len = sizeof ( struct in_addr ), |
---|
| 531 | .rx = ipv4_rx, |
---|
| 532 | .ntoa = ipv4_ntoa, |
---|
| 533 | }; |
---|
| 534 | |
---|
| 535 | /** IPv4 TCPIP net protocol */ |
---|
| 536 | struct tcpip_net_protocol ipv4_tcpip_protocol __tcpip_net_protocol = { |
---|
| 537 | .name = "IPv4", |
---|
| 538 | .sa_family = AF_INET, |
---|
| 539 | .tx = ipv4_tx, |
---|
| 540 | }; |
---|
| 541 | |
---|
| 542 | /** IPv4 ARP protocol */ |
---|
| 543 | struct arp_net_protocol ipv4_arp_protocol __arp_net_protocol = { |
---|
| 544 | .net_protocol = &ipv4_protocol, |
---|
| 545 | .check = ipv4_arp_check, |
---|
| 546 | }; |
---|
| 547 | |
---|
| 548 | /****************************************************************************** |
---|
| 549 | * |
---|
| 550 | * Settings |
---|
| 551 | * |
---|
| 552 | ****************************************************************************** |
---|
| 553 | */ |
---|
| 554 | |
---|
| 555 | /** IPv4 address setting */ |
---|
| 556 | struct setting ip_setting __setting = { |
---|
| 557 | .name = "ip", |
---|
| 558 | .description = "IPv4 address", |
---|
| 559 | .tag = DHCP_EB_YIADDR, |
---|
| 560 | .type = &setting_type_ipv4, |
---|
| 561 | }; |
---|
| 562 | |
---|
| 563 | /** IPv4 subnet mask setting */ |
---|
| 564 | struct setting netmask_setting __setting = { |
---|
| 565 | .name = "netmask", |
---|
| 566 | .description = "IPv4 subnet mask", |
---|
| 567 | .tag = DHCP_SUBNET_MASK, |
---|
| 568 | .type = &setting_type_ipv4, |
---|
| 569 | }; |
---|
| 570 | |
---|
| 571 | /** Default gateway setting */ |
---|
| 572 | struct setting gateway_setting __setting = { |
---|
| 573 | .name = "gateway", |
---|
| 574 | .description = "Default gateway", |
---|
| 575 | .tag = DHCP_ROUTERS, |
---|
| 576 | .type = &setting_type_ipv4, |
---|
| 577 | }; |
---|
| 578 | |
---|
| 579 | /** |
---|
| 580 | * Create IPv4 routing table based on configured settings |
---|
| 581 | * |
---|
| 582 | * @ret rc Return status code |
---|
| 583 | */ |
---|
| 584 | static int ipv4_create_routes ( void ) { |
---|
| 585 | struct ipv4_miniroute *miniroute; |
---|
| 586 | struct ipv4_miniroute *tmp; |
---|
| 587 | struct net_device *netdev; |
---|
| 588 | struct settings *settings; |
---|
| 589 | struct in_addr address = { 0 }; |
---|
| 590 | struct in_addr netmask = { 0 }; |
---|
| 591 | struct in_addr gateway = { 0 }; |
---|
| 592 | |
---|
| 593 | /* Delete all existing routes */ |
---|
| 594 | list_for_each_entry_safe ( miniroute, tmp, &ipv4_miniroutes, list ) |
---|
| 595 | del_ipv4_miniroute ( miniroute ); |
---|
| 596 | |
---|
| 597 | /* Create a route for each configured network device */ |
---|
| 598 | for_each_netdev ( netdev ) { |
---|
| 599 | settings = netdev_settings ( netdev ); |
---|
| 600 | /* Get IPv4 address */ |
---|
| 601 | address.s_addr = 0; |
---|
| 602 | fetch_ipv4_setting ( settings, &ip_setting, &address ); |
---|
| 603 | if ( ! address.s_addr ) |
---|
| 604 | continue; |
---|
| 605 | /* Get subnet mask */ |
---|
| 606 | fetch_ipv4_setting ( settings, &netmask_setting, &netmask ); |
---|
| 607 | /* Calculate default netmask, if necessary */ |
---|
| 608 | if ( ! netmask.s_addr ) { |
---|
| 609 | if ( IN_CLASSA ( ntohl ( address.s_addr ) ) ) { |
---|
| 610 | netmask.s_addr = htonl ( IN_CLASSA_NET ); |
---|
| 611 | } else if ( IN_CLASSB ( ntohl ( address.s_addr ) ) ) { |
---|
| 612 | netmask.s_addr = htonl ( IN_CLASSB_NET ); |
---|
| 613 | } else if ( IN_CLASSC ( ntohl ( address.s_addr ) ) ) { |
---|
| 614 | netmask.s_addr = htonl ( IN_CLASSC_NET ); |
---|
| 615 | } |
---|
| 616 | } |
---|
| 617 | /* Get default gateway, if present */ |
---|
| 618 | fetch_ipv4_setting ( settings, &gateway_setting, &gateway ); |
---|
| 619 | /* Configure route */ |
---|
| 620 | miniroute = add_ipv4_miniroute ( netdev, address, |
---|
| 621 | netmask, gateway ); |
---|
| 622 | if ( ! miniroute ) |
---|
| 623 | return -ENOMEM; |
---|
| 624 | } |
---|
| 625 | |
---|
| 626 | return 0; |
---|
| 627 | } |
---|
| 628 | |
---|
| 629 | /** IPv4 settings applicator */ |
---|
| 630 | struct settings_applicator ipv4_settings_applicator __settings_applicator = { |
---|
| 631 | .apply = ipv4_create_routes, |
---|
| 632 | }; |
---|
| 633 | |
---|
| 634 | /* Drag in ICMP */ |
---|
| 635 | REQUIRE_OBJECT ( icmp ); |
---|