ipv4.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
* ipv4.c, Code implementing the IPv4 internet protocol.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <socket_impl.h>
#include <socket_inet.h>
#include <sys/sysmacros.h>
#include <netinet/in_systm.h>
#include <sys/bootconf.h>
#include "icmp4.h"
#include "ipv4.h"
#include "ipv4_impl.h"
#include "mac.h"
#include "mac_impl.h"
#include "v4_sum_impl.h"
#include <sys/bootdebug.h>
static int fragments; /* Number of fragments */
{ 0xff, 0xff, 0xff, 0xff }; /* our network-order netmask */
static int promiscuous; /* promiscuous mode */
#ifdef DEBUG
#define FRAG_DEBUG
#endif /* DEBUG */
#ifdef FRAG_DEBUG
/*
* display the fragment list. For debugging purposes.
*/
static void
{
int i;
printf("More:\tOffset:\tDatap:\t\tIPid:\t\tIPlen:\tIPhlen:\n");
for (i = 0; i < FRAG_MAX; i++) {
continue;
}
}
#endif /* FRAG_DEBUG */
/*
* This function returns index of fragment 0 of the current fragmented DGRAM
* (which would contain the transport header). Return the fragment number
* for success, -1 if we don't yet have the first fragment.
*/
static int
frag_first(void)
{
int i;
if (fragments == 0)
return (-1);
for (i = 0; i < FRAG_MAX; i++) {
return (i);
}
return (-1);
}
/*
* This function returns index of the last fragment of the current DGRAM.
* Returns the fragment number for success, -1 if we don't yet have the
* last fragment.
*/
static int
frag_last(void)
{
int i;
if (fragments == 0)
return (-1);
for (i = 0; i < FRAG_MAX; i++) {
return (i);
}
return (-1);
}
/*
* This function adds a fragment to the current pkt fragment list. Returns
* FRAG_NOSLOTS if there are no more slots, FRAG_DUP if the fragment is
* a duplicate, or FRAG_SUCCESS if it is successful.
*/
static int
{
int i;
/* first pass - look for duplicates */
for (i = 0; i < FRAG_MAX; i++) {
return (FRAG_DUP);
}
/* second pass - fill in empty slot */
for (i = 0; i < FRAG_MAX; i++) {
fragments++;
return (FRAG_SUCCESS);
}
}
return (FRAG_NOSLOTS);
}
/*
* Nuke a fragment.
*/
static void
{
fragments--;
}
}
/*
* zero the frag list.
*/
static void
frag_flush(void)
{
int i;
for (i = 0; i < FRAG_MAX; i++)
frag_free(i);
fragments = 0;
}
/*
* Analyze the fragment list - see if we captured all our fragments.
*
* Returns TRUE if we've got all the fragments, and FALSE if we don't.
*/
static int
frag_chk(void)
{
int i, first_frag, last_frag;
return (FALSE);
/*
* Validate the ipid's of our fragments - nuke those that don't
* match the id of the first fragment or don't match the IP
* protocol of the first fragment.
*/
for (i = 0; i < FRAG_MAX; i++) {
#ifdef FRAG_DEBUG
printf("ipv4: Frag id mismatch: %x != %x\n",
#endif /* FRAG_DEBUG */
frag_free(i);
}
}
if (frag_last() < 0)
return (FALSE);
#ifdef FRAG_DEBUG
#endif /* FRAG_DEBUG */
}
/*
* Load the assembled fragments into igp. Returns 0 for success, nonzero
* otherwise.
*/
static int
{
int i;
int first_iph_len;
if (fragments == 0)
return (ENOENT);
/* Get the IP header length of the first fragment. */
i = frag_first();
assert(i >= 0);
/*
* Copy just the data (omit the ip header of all
* fragments except the first one which contains
* all the info...)
*/
first_frag = B_TRUE;
} else {
}
return (E2BIG);
if (first_frag) {
} else {
}
}
}
/* Fix the total length in the IP header. */
return (0);
}
/*
* Locate a routing table entry based upon arguments. IP addresses expected
* in network order. Returns index for success, -1 if entry not found.
*/
static int
{
int i, table_entry = -1;
table_entry = i;
}
table_entry = i;
else
table_entry = -1;
}
table_entry = i;
else
table_entry = -1;
}
}
return (table_entry);
}
/*
* ADD or DEL a routing table entry. Returns 0 for success, -1 and errno
* otherwise. IP addresses are expected in network order.
*/
int
{
static int routing_table_initialized;
int index;
return (-1);
}
/* initialize routing table */
if (routing_table_initialized == 0) {
}
switch (cmd) {
case IPV4_ADD_ROUTE:
dprintf("ipv4_route: routing table full.\n");
return (-1);
}
else
break;
case IPV4_BAD_ROUTE:
/* FALLTHRU */
case IPV4_DEL_ROUTE:
dprintf("ipv4_route: No such routing entry.\n");
return (-1);
}
if (cmd == IPV4_DEL_ROUTE) {
} else
default:
return (-1);
}
return (0);
}
/*
* Return gateway to destination. Returns gateway IP address in network order
* for success, NULL if no route to destination exists.
*/
struct in_addr *
{
int index;
return (NULL);
}
/*
* Initialize the IPv4 generic parts of the socket, as well as the routing
* table.
*/
void
{
}
/*
* Initialize a raw ipv4 socket.
*/
void
{
if (proto == 0)
else
}
/*
* Return the size of an IPv4 header (no options)
*/
/* ARGSUSED */
int
{
return (sizeof (struct ip));
}
/*
* Set our source address.
* Argument is assumed to be host order.
*/
void
{
}
/*
* Returns our current source address in host order.
*/
void
{
}
/*
* Set our netmask.
* Argument is assumed to be host order.
*/
void
{
}
void
{
struct in_addr my_netmask;
else {
}
}
/*
* Returns our current netmask in host order.
* Neither OBP nor the standalone DHCP client mandate
* that the netmask be specified, so in the absence of
* a netmask, we attempt to derive it using class-based
* heuristics.
*/
void
{
else {
/* base the netmask on our IP address */
else
}
}
/*
* Set our default router.
* Argument is assumed to be host order, and *MUST* be on the same network
* as our source IP address.
*/
void
{
}
/*
* Returns our current default router in host order.
*/
void
{
}
/*
* Toggle promiscuous flag. If set, client disregards destination IP
* address. Otherwise, only limited broadcast, network broadcast, and
* unicast traffic get through. Returns previous setting.
*/
int
{
int old = promiscuous;
return (old);
}
/*
* Set IP TTL.
*/
void
{
}
/*
* Convert an ipv4 address to dotted notation.
* Returns ptr to statically allocated buffer containing dotted string.
*/
char *
{
uint8_t *p;
static char ipaddr[16];
return (ipaddr);
}
/*
* Construct a transport datagram from a series of IP fragments (igp == NULL)
* or from a single IP datagram (igp != NULL). Return the address of the
* contructed transport datagram.
*/
struct inetgram *
{
int first_frag, last_frag;
if (fragmented)
frag_flush();
return (NULL);
}
if (fragmented) {
first_frag = frag_first();
/*
* The returned buffer contains the IP header of the
* first fragment.
*/
} else {
/*
* Note that igm_len may not be the real length of an
* IP packet because some network interface, such as
* Ethernet, as a minimum frame size. So we should not
* use the interface frame size to determine the
* length of an IP packet. We should use the IP
* length field in the IP header.
*/
}
/*
* Align to 16bit value. Checksum code may require an extra byte
* for padding.
*/
~(sizeof (int16_t) - 1));
if (fragmented)
frag_flush();
return (NULL);
}
if (fragmented) {
frag_flush();
return (NULL);
}
frag_flush();
} else {
}
return (ngp);
}
/*
* ipv4_input: Pull in IPv4 datagrams addressed to us. Handle IP fragmentation
* (fragments received in any order) and ICMP at this level.
*
* Note that because our network is serviced by polling when we expect
* something (upon a referenced socket), we don't go through the work of
* locating the appropriate socket a datagram is destined for. We'll only
* accept data for the referenced socket. This means we don't have
* asynchronous networking, but since we can't service the net using an
* interrupt handler, it doesn't do us any good to try to service datagrams
* destined for sockets other than the referenced one. Data is handled in
* a fifo manner.
*
* The mac layer will grab all frames for us. If we find we don't have all
* the necessary fragments to reassemble the datagram, we'll call the mac
* layer again for FRAG_ATTEMPTS to see if it has any more frames.
*
* Supported protocols: IPPROTO_IP, IPPROTO_ICMP, IPPROTO_UDP.
*
* Returns: number of NETWORK_LVL datagrams placed on socket , -1 if error
* occurred.
*
* Note: errno is set to ETIMEDOUT if fragment reassembly fails.
*/
int
ipv4_input(int index)
{
int datagrams = 0;
int frag_stat, input_attempts = 0;
#ifdef DEBUG
printf("ipv4_input(%d): start ######################################\n",
index);
#endif /* DEBUG */
frag_flush();
#ifdef DEBUG
printf("ipv4_input(%d): unexpected frame type: %d\n",
#endif /* DEBUG */
continue;
}
dprintf("ipv4_input(%d): IPv%d datagram discarded\n",
continue;
}
dprintf("ipv4_input(%d): IP msg too short (%d < %u)\n",
continue;
}
continue;
}
sizeof (ipdst));
sizeof (ipsrc));
/* igp->igm_mp->b_datap is guaranteed to be 64 bit aligned] */
dprintf("ipv4_input(%d): Bad IP header checksum "
continue;
}
if (!promiscuous) {
/* validate destination address */
#ifdef DEBUG
printf("ipv4_input(%d): msg to %s discarded.\n",
#endif /* DEBUG */
/* not ours */
continue;
}
}
/* Intercept ICMP first */
continue;
}
#ifdef DEBUG
printf("ipv4_input(%d): processing ID: 0x%x protocol %d "
"(0x%x) (0x%x,%d)\n",
#endif /* DEBUG */
if (type == INETBOOT_RAW) {
/* No fragmentation - Just the raw packet. */
#ifdef DEBUG
#endif /* DEBUG */
datagrams++;
continue;
}
/* Wrong protocol. */
dprintf("ipv4_input(%d): unexpected protocol: "
continue;
}
/*
* The following code is common to both STREAM and DATAGRAM
* sockets.
*/
/*
* Once we process the first fragment, we won't have
* the transport header, so we'll have to match on
* IP id.
*/
/* Validate transport header. */
dprintf("ipv4_input(%d): datagram 0 "
"too small to hold transport header "
continue;
}
/*
* check alignment - transport elements are 16
* bit aligned..
*/
dprintf("ipv4_input(%d): Transport "
"header is not 16-bit aligned "
continue;
}
/* fragment 0 of fragmented datagram */
if (frag_stat != FRAG_SUCCESS) {
#ifdef FRAG_DEBUG
printf("ipv4_input"
"(%d): Frag dup.\n", index);
} else {
printf("ipv4_input"
"(%d): too many "
"frags\n", index);
}
#endif /* FRAG_DEBUG */
continue;
}
/* keep the data, lose the inetgram */
sizeof (struct inetgram));
#ifdef FRAG_DEBUG
#endif /* FRAG_DEBUG */
} else {
/* Single, unfragmented datagram */
datagrams++;
}
TRUE);
continue;
}
} else {
/* fragments other than 0 */
if (frag_stat == FRAG_SUCCESS) {
#ifdef FRAG_DEBUG
printf("ipv4_input(%d): Frag(%d) "
"off(%d) id(%x)\n", index,
#endif /* FRAG_DEBUG */
/* keep the data, lose the inetgram */
sizeof (struct inetgram));
} else {
#ifdef FRAG_DEBUG
printf("ipv4_input(%d): Frag "
"dup.\n", index);
else {
printf("ipv4_input(%d): too "
"many frags\n", index);
}
#endif /* FRAG_DEBUG */
continue;
}
}
/*
* Determine if we have all of the fragments.
*
* NOTE: at this point, we've placed the data in the
* fragment table, and the inetgram (igp) has been
* deleted.
*/
if (!frag_chk())
continue;
continue;
datagrams++;
}
if (++input_attempts > FRAG_ATTEMPTS) {
dprintf("ipv4_input(%d): reassembly(%d) timed out in "
frag_flush();
return (-1);
} else {
/*
* Call the media layer again... there may be more
* packets waiting.
*/
/* errno will be set appropriately */
frag_flush();
return (-1);
}
goto ipv4_try_again;
}
}
return (datagrams);
}
/*
* ipv4_output: Generate IPv4 datagram(s) for the payload and deliver them.
* Routing is handled here as well, by reusing the saddr field to hold the
* router's IP address.
*
* We don't deal with fragmentation on the outgoing side.
*
* Arguments: index to socket, inetgram to send.
*
* Returns: 0 for success, -1 if error occurred.
*/
int
{
#ifdef DEBUG
#endif /* DEBUG */
/* we don't deal (yet) with fragmentation. Maybe never will */
dprintf("ipv4: datagram too big for MAC layer.\n");
return (-1);
}
#ifdef DEBUG
#endif /* DEBUG */
return (-1);
}
sizeof (struct ip));
/* struct copies */
/*
* On local / limited broadcasts, don't route. From a purist's
* perspective, we should be setting the TTL to 1. But
* operational experience has shown that some BOOTP relay agents
* (ciscos) discard our packets. Furthermore, these devices also
* *don't* reset the TTL to MAXTTL on the unicast side of the
* BOOTP relay agent! Sigh. Thus to work correctly in these
* environments, we leave the TTL as it has been been set by
* the application layer, and simply don't check for a route.
*/
}
/* Routing necessary? */
}
dprintf("ipv4(%d): No route to %s.\n",
return (-1);
}
} else
sizeof (struct ip));
return (0);
}
/*
* Function to be called by TCP to send out a packet. This is used
* when TCP wants to send out packets which it has already filled in
* most of the header fields.
*/
int
{
/*
* Bootparams doesn't know about subnet masks, so we need to
* explicitly check for this flag.
*/
/* Routing necessary? */
}
dprintf("ipv4(%d): No route to %s.\n",
return (-1);
}
}
#if DEBUG > 1
#endif
/* Call the MAC layer output routine to send it out. */
else
}
/*
* Internet address interpretation routine.
* All the network library routines call this
* routine to interpret entries in the data bases
* which are expected to be an address.
* The value returned is in network order.
*/
{
char c;
if (*cp == '\0')
/*
* Collect number up to ``.''.
* Values are specified as for C:
* 0x=hex, 0=octal, other=decimal.
*/
if (*cp == '0') {
else
base = 8;
}
if (isdigit(c)) {
if ((c - '0') >= base)
break;
cp++;
continue;
}
cp++;
continue;
}
break;
}
if (*cp == '.') {
/*
* Internet format:
* a.b.c.d
* a.b.c (with c treated as 16-bits)
* a.b (with b treated as 24 bits)
*/
return ((uint32_t)-1);
}
goto again;
}
/*
* Check for trailing characters.
*/
return ((uint32_t)-1);
}
/*
* Concoct the address according to
* the number of parts specified.
*/
switch (n) {
case 1: /* a -- 32 bits */
break;
case 2: /* a.b -- 8.24 bits */
return ((uint32_t)-1);
break;
case 3: /* a.b.c -- 8.8.16 bits */
return ((uint32_t)-1);
break;
case 4: /* a.b.c.d -- 8.8.8.8 bits */
return ((uint32_t)-1);
break;
default:
return ((uint32_t)-1);
}
return (val);
}
void
{
char *p;
printf("\n");
for (i = 0; i < (len / 2); i++)
if (len % 2) {
}
printf(" ");
printf(" ");
printf("\n");
}
printf("\n");
}