2005-03-08 18:53:11 +00:00
|
|
|
|
/**************************************************************************
|
|
|
|
|
Etherboot - Network Bootstrap Program
|
|
|
|
|
|
|
|
|
|
Literature dealing with the network protocols:
|
|
|
|
|
ARP - RFC826
|
|
|
|
|
RARP - RFC903
|
|
|
|
|
IP - RFC791
|
|
|
|
|
UDP - RFC768
|
|
|
|
|
BOOTP - RFC951, RFC2132 (vendor extensions)
|
|
|
|
|
DHCP - RFC2131, RFC2132, RFC3004 (options)
|
|
|
|
|
TFTP - RFC1350, RFC2347 (options), RFC2348 (blocksize), RFC2349 (tsize)
|
|
|
|
|
RPC - RFC1831, RFC1832 (XDR), RFC1833 (rpcbind/portmapper)
|
|
|
|
|
NFS - RFC1094, RFC1813 (v3, useful for clarifications, not implemented)
|
|
|
|
|
IGMP - RFC1112, RFC2113, RFC2365, RFC2236, RFC3171
|
|
|
|
|
|
|
|
|
|
**************************************************************************/
|
|
|
|
|
#include "etherboot.h"
|
2005-04-12 16:52:59 +00:00
|
|
|
|
#include "console.h"
|
|
|
|
|
#include "dev.h"
|
2005-03-08 18:53:11 +00:00
|
|
|
|
#include "nic.h"
|
|
|
|
|
#include "elf.h" /* FOR EM_CURRENT */
|
|
|
|
|
|
|
|
|
|
struct arptable_t arptable[MAX_ARP];
|
|
|
|
|
#if MULTICAST_LEVEL2
|
|
|
|
|
unsigned long last_igmpv1 = 0;
|
|
|
|
|
struct igmptable_t igmptable[MAX_IGMP];
|
|
|
|
|
#endif
|
|
|
|
|
/* Put rom_info in .nocompress section so romprefix.S can write to it */
|
|
|
|
|
struct rom_info rom __attribute__ ((section (".text16.nocompress"))) = {0,0};
|
|
|
|
|
static unsigned long netmask;
|
|
|
|
|
/* Used by nfs.c */
|
|
|
|
|
char *hostname = "";
|
|
|
|
|
int hostnamelen = 0;
|
|
|
|
|
static uint32_t xid;
|
|
|
|
|
unsigned char *end_of_rfc1533 = NULL;
|
|
|
|
|
static int vendorext_isvalid;
|
|
|
|
|
static const unsigned char vendorext_magic[] = {0xE4,0x45,0x74,0x68}; /* <20>Eth */
|
|
|
|
|
static const unsigned char broadcast[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
|
|
|
|
|
static const in_addr zeroIP = { 0L };
|
|
|
|
|
|
|
|
|
|
struct bootpd_t bootp_data;
|
|
|
|
|
|
|
|
|
|
#ifdef NO_DHCP_SUPPORT
|
|
|
|
|
static unsigned char rfc1533_cookie[5] = { RFC1533_COOKIE, RFC1533_END };
|
|
|
|
|
#else /* !NO_DHCP_SUPPORT */
|
|
|
|
|
static int dhcp_reply;
|
|
|
|
|
static in_addr dhcp_server = { 0L };
|
|
|
|
|
static in_addr dhcp_addr = { 0L };
|
|
|
|
|
static unsigned char rfc1533_cookie[] = { RFC1533_COOKIE };
|
|
|
|
|
#define DHCP_MACHINE_INFO_SIZE (sizeof dhcp_machine_info)
|
|
|
|
|
static unsigned char dhcp_machine_info[] = {
|
|
|
|
|
/* Our enclosing DHCP tag */
|
|
|
|
|
RFC1533_VENDOR_ETHERBOOT_ENCAP, 11,
|
|
|
|
|
/* Our boot device */
|
|
|
|
|
RFC1533_VENDOR_NIC_DEV_ID, 5, PCI_BUS_TYPE, 0, 0, 0, 0,
|
|
|
|
|
/* Our current architecture */
|
|
|
|
|
RFC1533_VENDOR_ARCH, 2, EM_CURRENT & 0xff, (EM_CURRENT >> 8) & 0xff,
|
|
|
|
|
#ifdef EM_CURRENT_64
|
|
|
|
|
/* The 64bit version of our current architecture */
|
|
|
|
|
RFC1533_VENDOR_ARCH, 2, EM_CURRENT_64 & 0xff, (EM_CURRENT_64 >> 8) & 0xff,
|
|
|
|
|
#undef DHCP_MACHINE_INFO_SIZE
|
|
|
|
|
#define DHCP_MACHINE_INFO_SIZE (sizeof(dhcp_machine_info) - (EM_CURRENT_64_PRESENT? 0: 4))
|
|
|
|
|
#endif /* EM_CURRENT_64 */
|
|
|
|
|
};
|
|
|
|
|
static const unsigned char dhcpdiscover[] = {
|
|
|
|
|
RFC2132_MSG_TYPE,1,DHCPDISCOVER,
|
|
|
|
|
RFC2132_MAX_SIZE,2, /* request as much as we can */
|
|
|
|
|
ETH_MAX_MTU / 256, ETH_MAX_MTU % 256,
|
|
|
|
|
#ifdef PXE_DHCP_STRICT
|
|
|
|
|
RFC3679_PXE_CLIENT_UUID,RFC3679_PXE_CLIENT_UUID_LENGTH,RFC3679_PXE_CLIENT_UUID_DEFAULT,
|
|
|
|
|
RFC3679_PXE_CLIENT_ARCH,RFC3679_PXE_CLIENT_ARCH_LENGTH,RFC3679_PXE_CLIENT_ARCH_IAX86PC,
|
|
|
|
|
RFC3679_PXE_CLIENT_NDI, RFC3679_PXE_CLIENT_NDI_LENGTH, RFC3679_PXE_CLIENT_NDI_21,
|
|
|
|
|
RFC2132_VENDOR_CLASS_ID,RFC2132_VENDOR_CLASS_ID_PXE_LENGTH,RFC2132_VENDOR_CLASS_ID_PXE,
|
|
|
|
|
#else
|
|
|
|
|
RFC2132_VENDOR_CLASS_ID,13,'E','t','h','e','r','b','o','o','t',
|
|
|
|
|
'-',VERSION_MAJOR+'0','.',VERSION_MINOR+'0',
|
|
|
|
|
#endif /* PXE_DHCP_STRICT */
|
|
|
|
|
#ifdef DHCP_CLIENT_ID
|
|
|
|
|
/* Client ID Option */
|
|
|
|
|
RFC2132_CLIENT_ID, ( DHCP_CLIENT_ID_LEN + 1 ),
|
|
|
|
|
DHCP_CLIENT_ID_TYPE, DHCP_CLIENT_ID,
|
|
|
|
|
#endif /* DHCP_CLIENT_ID */
|
|
|
|
|
#ifdef DHCP_USER_CLASS
|
|
|
|
|
/* User Class Option */
|
|
|
|
|
RFC3004_USER_CLASS, DHCP_USER_CLASS_LEN, DHCP_USER_CLASS,
|
|
|
|
|
#endif /* DHCP_USER_CLASS */
|
|
|
|
|
RFC2132_PARAM_LIST,
|
|
|
|
|
#define DHCPDISCOVER_PARAMS_BASE 4
|
|
|
|
|
#ifdef PXE_DHCP_STRICT
|
|
|
|
|
#define DHCPDISCOVER_PARAMS_PXE ( 1 + 8 )
|
|
|
|
|
#else
|
|
|
|
|
#define DHCPDISCOVER_PARAMS_PXE 0
|
|
|
|
|
#endif /* PXE_DHCP_STRICT */
|
|
|
|
|
#ifdef DNS_RESOLVER
|
|
|
|
|
#define DHCPDISCOVER_PARAMS_DNS 1
|
|
|
|
|
#else
|
|
|
|
|
#define DHCPDISCOVER_PARAMS_DNS 0
|
|
|
|
|
#endif /* DNS_RESOLVER */
|
|
|
|
|
( DHCPDISCOVER_PARAMS_BASE +
|
|
|
|
|
DHCPDISCOVER_PARAMS_PXE+
|
|
|
|
|
DHCPDISCOVER_PARAMS_DNS ),
|
|
|
|
|
RFC1533_NETMASK,
|
|
|
|
|
RFC1533_GATEWAY,
|
|
|
|
|
RFC1533_HOSTNAME,
|
|
|
|
|
RFC1533_VENDOR
|
|
|
|
|
#ifdef PXE_DHCP_STRICT
|
|
|
|
|
,RFC2132_VENDOR_CLASS_ID,
|
|
|
|
|
RFC1533_VENDOR_PXE_OPT128,
|
|
|
|
|
RFC1533_VENDOR_PXE_OPT129,
|
|
|
|
|
RFC1533_VENDOR_PXE_OPT130,
|
|
|
|
|
RFC1533_VENDOR_PXE_OPT131,
|
|
|
|
|
RFC1533_VENDOR_PXE_OPT132,
|
|
|
|
|
RFC1533_VENDOR_PXE_OPT133,
|
|
|
|
|
RFC1533_VENDOR_PXE_OPT134,
|
|
|
|
|
RFC1533_VENDOR_PXE_OPT135
|
|
|
|
|
#endif /* PXE_DHCP_STRICT */
|
|
|
|
|
#ifdef DNS_RESOLVER
|
|
|
|
|
,RFC1533_DNS
|
|
|
|
|
#endif
|
|
|
|
|
};
|
|
|
|
|
static const unsigned char dhcprequest [] = {
|
|
|
|
|
RFC2132_MSG_TYPE,1,DHCPREQUEST,
|
|
|
|
|
RFC2132_SRV_ID,4,0,0,0,0,
|
|
|
|
|
RFC2132_REQ_ADDR,4,0,0,0,0,
|
|
|
|
|
RFC2132_MAX_SIZE,2, /* request as much as we can */
|
|
|
|
|
ETH_MAX_MTU / 256, ETH_MAX_MTU % 256,
|
|
|
|
|
#ifdef PXE_DHCP_STRICT
|
|
|
|
|
RFC3679_PXE_CLIENT_UUID,RFC3679_PXE_CLIENT_UUID_LENGTH,RFC3679_PXE_CLIENT_UUID_DEFAULT,
|
|
|
|
|
RFC3679_PXE_CLIENT_ARCH,RFC3679_PXE_CLIENT_ARCH_LENGTH,RFC3679_PXE_CLIENT_ARCH_IAX86PC,
|
|
|
|
|
RFC3679_PXE_CLIENT_NDI, RFC3679_PXE_CLIENT_NDI_LENGTH, RFC3679_PXE_CLIENT_NDI_21,
|
|
|
|
|
RFC2132_VENDOR_CLASS_ID,RFC2132_VENDOR_CLASS_ID_PXE_LENGTH,RFC2132_VENDOR_CLASS_ID_PXE,
|
|
|
|
|
#else
|
|
|
|
|
RFC2132_VENDOR_CLASS_ID,13,'E','t','h','e','r','b','o','o','t',
|
|
|
|
|
'-',VERSION_MAJOR+'0','.',VERSION_MINOR+'0',
|
|
|
|
|
#endif /* PXE_DHCP_STRICT */
|
|
|
|
|
#ifdef DHCP_CLIENT_ID
|
|
|
|
|
/* Client ID Option */
|
|
|
|
|
RFC2132_CLIENT_ID, ( DHCP_CLIENT_ID_LEN + 1 ),
|
|
|
|
|
DHCP_CLIENT_ID_TYPE, DHCP_CLIENT_ID,
|
|
|
|
|
#endif /* DHCP_CLIENT_ID */
|
|
|
|
|
#ifdef DHCP_USER_CLASS
|
|
|
|
|
/* User Class Option */
|
|
|
|
|
RFC3004_USER_CLASS, DHCP_USER_CLASS_LEN, DHCP_USER_CLASS,
|
|
|
|
|
#endif /* DHCP_USER_CLASS */
|
|
|
|
|
/* request parameters */
|
|
|
|
|
RFC2132_PARAM_LIST,
|
|
|
|
|
#define DHCPREQUEST_PARAMS_BASE 5
|
|
|
|
|
#ifdef PXE_DHCP_STRICT
|
|
|
|
|
#define DHCPREQUEST_PARAMS_PXE 1
|
|
|
|
|
#define DHCPREQUEST_PARAMS_VENDOR_PXE 8
|
|
|
|
|
#define DHCPREQUEST_PARAMS_VENDOR_EB 0
|
|
|
|
|
#else
|
|
|
|
|
#define DHCPREQUEST_PARAMS_PXE 0
|
|
|
|
|
#define DHCPREQUEST_PARAMS_VENDOR_PXE 0
|
|
|
|
|
#define DHCPREQUEST_PARAMS_VENDOR_EB 4
|
|
|
|
|
#endif /* PXE_DHCP_STRICT */
|
|
|
|
|
#ifdef IMAGE_FREEBSD
|
|
|
|
|
#define DHCPREQUEST_PARAMS_FREEBSD 2
|
|
|
|
|
#else
|
|
|
|
|
#define DHCPREQUEST_PARAMS_FREEBSD 0
|
|
|
|
|
#endif /* IMAGE_FREEBSD */
|
|
|
|
|
#ifdef DNS_RESOLVER
|
|
|
|
|
#define DHCPREQUEST_PARAMS_DNS 1
|
|
|
|
|
#else
|
|
|
|
|
#define DHCPREQUEST_PARAMS_DNS 0
|
|
|
|
|
#endif /* DNS_RESOLVER */
|
|
|
|
|
( DHCPREQUEST_PARAMS_BASE +
|
|
|
|
|
DHCPREQUEST_PARAMS_PXE +
|
|
|
|
|
DHCPREQUEST_PARAMS_VENDOR_PXE +
|
|
|
|
|
DHCPREQUEST_PARAMS_VENDOR_EB +
|
|
|
|
|
DHCPREQUEST_PARAMS_DNS +
|
|
|
|
|
DHCPREQUEST_PARAMS_FREEBSD ),
|
|
|
|
|
/* 5 Standard parameters */
|
|
|
|
|
RFC1533_NETMASK,
|
|
|
|
|
RFC1533_GATEWAY,
|
|
|
|
|
RFC1533_HOSTNAME,
|
|
|
|
|
RFC1533_VENDOR,
|
|
|
|
|
RFC1533_ROOTPATH, /* only passed to the booted image */
|
|
|
|
|
#ifndef PXE_DHCP_STRICT
|
|
|
|
|
/* 4 Etherboot vendortags */
|
|
|
|
|
RFC1533_VENDOR_MAGIC,
|
|
|
|
|
RFC1533_VENDOR_ADDPARM,
|
|
|
|
|
RFC1533_VENDOR_ETHDEV,
|
|
|
|
|
RFC1533_VENDOR_ETHERBOOT_ENCAP,
|
|
|
|
|
#endif /* ! PXE_DHCP_STRICT */
|
|
|
|
|
#ifdef IMAGE_FREEBSD
|
|
|
|
|
/* 2 FreeBSD options */
|
|
|
|
|
RFC1533_VENDOR_HOWTO,
|
|
|
|
|
RFC1533_VENDOR_KERNEL_ENV,
|
|
|
|
|
#endif
|
|
|
|
|
#ifdef DNS_RESOLVER
|
|
|
|
|
/* 1 DNS option */
|
|
|
|
|
RFC1533_DNS,
|
|
|
|
|
#endif
|
|
|
|
|
#ifdef PXE_DHCP_STRICT
|
|
|
|
|
RFC2132_VENDOR_CLASS_ID,
|
|
|
|
|
RFC1533_VENDOR_PXE_OPT128,
|
|
|
|
|
RFC1533_VENDOR_PXE_OPT129,
|
|
|
|
|
RFC1533_VENDOR_PXE_OPT130,
|
|
|
|
|
RFC1533_VENDOR_PXE_OPT131,
|
|
|
|
|
RFC1533_VENDOR_PXE_OPT132,
|
|
|
|
|
RFC1533_VENDOR_PXE_OPT133,
|
|
|
|
|
RFC1533_VENDOR_PXE_OPT134,
|
|
|
|
|
RFC1533_VENDOR_PXE_OPT135,
|
|
|
|
|
#endif /* PXE_DHCP_STRICT */
|
|
|
|
|
};
|
|
|
|
|
#ifdef PXE_EXPORT
|
|
|
|
|
static const unsigned char proxydhcprequest [] = {
|
|
|
|
|
RFC2132_MSG_TYPE,1,DHCPREQUEST,
|
|
|
|
|
RFC2132_MAX_SIZE,2, /* request as much as we can */
|
|
|
|
|
ETH_MAX_MTU / 256, ETH_MAX_MTU % 256,
|
|
|
|
|
#ifdef PXE_DHCP_STRICT
|
|
|
|
|
RFC3679_PXE_CLIENT_UUID,RFC3679_PXE_CLIENT_UUID_LENGTH,RFC3679_PXE_CLIENT_UUID_DEFAULT,
|
|
|
|
|
RFC3679_PXE_CLIENT_ARCH,RFC3679_PXE_CLIENT_ARCH_LENGTH,RFC3679_PXE_CLIENT_ARCH_IAX86PC,
|
|
|
|
|
RFC3679_PXE_CLIENT_NDI, RFC3679_PXE_CLIENT_NDI_LENGTH, RFC3679_PXE_CLIENT_NDI_21,
|
|
|
|
|
RFC2132_VENDOR_CLASS_ID,RFC2132_VENDOR_CLASS_ID_PXE_LENGTH,RFC2132_VENDOR_CLASS_ID_PXE,
|
|
|
|
|
#endif /* PXE_DHCP_STRICT */
|
|
|
|
|
};
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef REQUIRE_VCI_ETHERBOOT
|
|
|
|
|
int vci_etherboot;
|
|
|
|
|
#endif
|
|
|
|
|
#endif /* NO_DHCP_SUPPORT */
|
|
|
|
|
|
|
|
|
|
#ifdef RARP_NOT_BOOTP
|
|
|
|
|
static int rarp(void);
|
|
|
|
|
#else
|
|
|
|
|
static int bootp(void);
|
|
|
|
|
#endif
|
|
|
|
|
static unsigned short tcpudpchksum(struct iphdr *ip);
|
|
|
|
|
|
|
|
|
|
|
2005-04-12 16:52:59 +00:00
|
|
|
|
struct nic *nic = &dev.nic;
|
2005-03-08 18:53:11 +00:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Find out what our boot parameters are
|
|
|
|
|
*/
|
2005-04-12 16:52:59 +00:00
|
|
|
|
static int nic_load_configuration(struct dev *dev __unused)
|
2005-03-08 18:53:11 +00:00
|
|
|
|
{
|
|
|
|
|
int server_found;
|
|
|
|
|
/* Find a server to get BOOTP reply from */
|
|
|
|
|
#ifdef RARP_NOT_BOOTP
|
|
|
|
|
printf("Searching for server (RARP)...");
|
|
|
|
|
#else
|
|
|
|
|
#ifndef NO_DHCP_SUPPORT
|
|
|
|
|
printf("Searching for server (DHCP)...");
|
|
|
|
|
#else
|
|
|
|
|
printf("Searching for server (BOOTP)...");
|
|
|
|
|
#endif
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef RARP_NOT_BOOTP
|
|
|
|
|
server_found = rarp();
|
|
|
|
|
#else
|
|
|
|
|
server_found = bootp();
|
|
|
|
|
#endif
|
|
|
|
|
if (!server_found) {
|
|
|
|
|
printf("No Server found\n");
|
2005-04-12 19:11:13 +00:00
|
|
|
|
return 0;
|
2005-03-08 18:53:11 +00:00
|
|
|
|
}
|
2005-04-12 19:11:13 +00:00
|
|
|
|
return 1;
|
2005-03-08 18:53:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**************************************************************************
|
|
|
|
|
LOAD - Try to get booted
|
|
|
|
|
**************************************************************************/
|
2005-04-12 16:52:59 +00:00
|
|
|
|
static int nic_load(struct dev *dev __unused)
|
2005-03-08 18:53:11 +00:00
|
|
|
|
{
|
|
|
|
|
const char *kernel;
|
|
|
|
|
printf("\nMe: %@", arptable[ARP_CLIENT].ipaddr.s_addr );
|
|
|
|
|
#ifndef NO_DHCP_SUPPORT
|
|
|
|
|
printf(", DHCP: %@", dhcp_server );
|
|
|
|
|
#ifdef PXE_EXPORT
|
|
|
|
|
if (arptable[ARP_PROXYDHCP].ipaddr.s_addr)
|
|
|
|
|
printf(" (& %@)",
|
|
|
|
|
arptable[ARP_PROXYDHCP].ipaddr.s_addr);
|
|
|
|
|
#endif /* PXE_EXPORT */
|
|
|
|
|
#endif /* ! NO_DHCP_SUPPORT */
|
|
|
|
|
printf(", TFTP: %@", arptable[ARP_SERVER].ipaddr.s_addr);
|
|
|
|
|
if (BOOTP_DATA_ADDR->bootp_reply.bp_giaddr.s_addr)
|
|
|
|
|
printf(", Relay: %@",
|
|
|
|
|
BOOTP_DATA_ADDR->bootp_reply.bp_giaddr.s_addr);
|
|
|
|
|
if (arptable[ARP_GATEWAY].ipaddr.s_addr)
|
|
|
|
|
printf(", Gateway %@", arptable[ARP_GATEWAY].ipaddr.s_addr);
|
|
|
|
|
#ifdef DNS_RESOLVER
|
|
|
|
|
if (arptable[ARP_NAMESERVER].ipaddr.s_addr)
|
|
|
|
|
printf(", Nameserver %@", arptable[ARP_NAMESERVER].ipaddr.s_addr);
|
|
|
|
|
#endif
|
|
|
|
|
putchar('\n');
|
|
|
|
|
|
|
|
|
|
#ifdef MDEBUG
|
|
|
|
|
printf("\n=>>"); getchar();
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/* Now use TFTP to load file */
|
|
|
|
|
#ifdef DOWNLOAD_PROTO_NFS
|
|
|
|
|
rpc_init();
|
|
|
|
|
#endif
|
|
|
|
|
kernel = KERNEL_BUF[0] == '\0' ?
|
|
|
|
|
#ifdef DEFAULT_BOOTFILE
|
|
|
|
|
DEFAULT_BOOTFILE
|
|
|
|
|
#else
|
|
|
|
|
NULL
|
|
|
|
|
#endif
|
|
|
|
|
: KERNEL_BUF;
|
|
|
|
|
if ( kernel ) {
|
|
|
|
|
loadkernel(kernel); /* We don't return except on error */
|
|
|
|
|
printf("Unable to load file.\n");
|
|
|
|
|
} else {
|
|
|
|
|
printf("No filename\n");
|
|
|
|
|
}
|
|
|
|
|
interruptible_sleep(2); /* lay off the server for a while */
|
2005-04-12 19:11:13 +00:00
|
|
|
|
return 0;
|
2005-03-08 18:53:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2005-04-12 16:52:59 +00:00
|
|
|
|
static void nic_disable ( struct dev *dev ) {
|
|
|
|
|
struct nic *nic = &dev->nic;
|
|
|
|
|
|
|
|
|
|
#ifdef MULTICAST_LEVEL2
|
|
|
|
|
int i;
|
|
|
|
|
for(i = 0; i < MAX_IGMP; i++) {
|
|
|
|
|
leave_group(i);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
nic->nic_op->disable ( nic );
|
|
|
|
|
}
|
|
|
|
|
|
2005-04-12 19:11:13 +00:00
|
|
|
|
static void nic_print_info ( struct dev *dev ) {
|
|
|
|
|
struct nic *nic = &dev->nic;
|
|
|
|
|
|
|
|
|
|
printf ( "Found %s NIC (MAC %!)\n", dev->name, nic->node_addr );
|
|
|
|
|
}
|
|
|
|
|
|
2005-04-12 16:52:59 +00:00
|
|
|
|
/*
|
|
|
|
|
* Device operations tables
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
static struct dev_operations nic_operations = {
|
|
|
|
|
.disable = nic_disable,
|
2005-04-12 19:11:13 +00:00
|
|
|
|
.print_info = nic_print_info,
|
2005-04-12 16:52:59 +00:00
|
|
|
|
.load_configuration = nic_load_configuration,
|
|
|
|
|
.load = nic_load,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* Careful. We need an aligned buffer to avoid problems on machines
|
|
|
|
|
* that care about alignment. To trivally align the ethernet data
|
|
|
|
|
* (the ip hdr and arp requests) we offset the packet by 2 bytes.
|
|
|
|
|
* leaving the ethernet data 16 byte aligned. Beyond this
|
|
|
|
|
* we use memmove but this makes the common cast simple and fast.
|
|
|
|
|
*/
|
|
|
|
|
static char packet[ETH_FRAME_LEN + ETH_DATA_ALIGN] __aligned;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Set up a struct dev to operate as a NIC, return the struct nic *
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
struct nic * nic_device ( struct dev *dev ) {
|
|
|
|
|
struct nic *nic = &dev->nic;
|
|
|
|
|
|
|
|
|
|
memset ( nic, 0, sizeof ( *nic ) );
|
|
|
|
|
nic->node_addr = arptable[ARP_CLIENT].node;
|
|
|
|
|
nic->packet = packet + ETH_DATA_ALIGN;
|
|
|
|
|
dev->dev_op = &nic_operations;
|
|
|
|
|
return nic;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2005-04-12 19:11:13 +00:00
|
|
|
|
int dummy_connect ( struct nic *nic ) {
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
2005-04-12 16:52:59 +00:00
|
|
|
|
|
2005-04-12 19:11:13 +00:00
|
|
|
|
int dummy_irq ( struct nic *nic ) {
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
2005-04-12 16:52:59 +00:00
|
|
|
|
|
2005-03-08 18:53:11 +00:00
|
|
|
|
/**************************************************************************
|
|
|
|
|
DEFAULT_NETMASK - Return default netmask for IP address
|
|
|
|
|
**************************************************************************/
|
|
|
|
|
static inline unsigned long default_netmask(void)
|
|
|
|
|
{
|
|
|
|
|
int net = ntohl(arptable[ARP_CLIENT].ipaddr.s_addr) >> 24;
|
|
|
|
|
if (net <= 127)
|
|
|
|
|
return(htonl(0xff000000));
|
|
|
|
|
else if (net < 192)
|
|
|
|
|
return(htonl(0xffff0000));
|
|
|
|
|
else
|
|
|
|
|
return(htonl(0xffffff00));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**************************************************************************
|
|
|
|
|
IP_TRANSMIT - Send an IP datagram
|
|
|
|
|
**************************************************************************/
|
|
|
|
|
static int await_arp(int ival, void *ptr,
|
|
|
|
|
unsigned short ptype, struct iphdr *ip __unused, struct udphdr *udp __unused,
|
|
|
|
|
struct tcphdr *tcp __unused)
|
|
|
|
|
{
|
|
|
|
|
struct arprequest *arpreply;
|
|
|
|
|
if (ptype != ETH_P_ARP)
|
|
|
|
|
return 0;
|
2005-04-12 16:52:59 +00:00
|
|
|
|
if (nic->packetlen < ETH_HLEN + sizeof(struct arprequest))
|
2005-03-08 18:53:11 +00:00
|
|
|
|
return 0;
|
2005-04-12 16:52:59 +00:00
|
|
|
|
arpreply = (struct arprequest *)&nic->packet[ETH_HLEN];
|
2005-03-08 18:53:11 +00:00
|
|
|
|
|
|
|
|
|
if (arpreply->opcode != htons(ARP_REPLY))
|
|
|
|
|
return 0;
|
|
|
|
|
if (memcmp(arpreply->sipaddr, ptr, sizeof(in_addr)) != 0)
|
|
|
|
|
return 0;
|
|
|
|
|
memcpy(arptable[ival].node, arpreply->shwaddr, ETH_ALEN);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int ip_transmit(int len, const void *buf)
|
|
|
|
|
{
|
|
|
|
|
unsigned long destip;
|
|
|
|
|
struct iphdr *ip;
|
|
|
|
|
struct arprequest arpreq;
|
|
|
|
|
int arpentry, i;
|
|
|
|
|
int retry;
|
|
|
|
|
|
|
|
|
|
ip = (struct iphdr *)buf;
|
|
|
|
|
destip = ip->dest.s_addr;
|
|
|
|
|
if (destip == IP_BROADCAST) {
|
|
|
|
|
eth_transmit(broadcast, ETH_P_IP, len, buf);
|
|
|
|
|
#ifdef MULTICAST_LEVEL1
|
|
|
|
|
} else if ((destip & htonl(MULTICAST_MASK)) == htonl(MULTICAST_NETWORK)) {
|
|
|
|
|
unsigned char multicast[6];
|
|
|
|
|
unsigned long hdestip;
|
|
|
|
|
hdestip = ntohl(destip);
|
|
|
|
|
multicast[0] = 0x01;
|
|
|
|
|
multicast[1] = 0x00;
|
|
|
|
|
multicast[2] = 0x5e;
|
|
|
|
|
multicast[3] = (hdestip >> 16) & 0x7;
|
|
|
|
|
multicast[4] = (hdestip >> 8) & 0xff;
|
|
|
|
|
multicast[5] = hdestip & 0xff;
|
|
|
|
|
eth_transmit(multicast, ETH_P_IP, len, buf);
|
|
|
|
|
#endif
|
|
|
|
|
} else {
|
|
|
|
|
if (((destip & netmask) !=
|
|
|
|
|
(arptable[ARP_CLIENT].ipaddr.s_addr & netmask)) &&
|
|
|
|
|
arptable[ARP_GATEWAY].ipaddr.s_addr)
|
|
|
|
|
destip = arptable[ARP_GATEWAY].ipaddr.s_addr;
|
|
|
|
|
for(arpentry = 0; arpentry<MAX_ARP; arpentry++)
|
|
|
|
|
if (arptable[arpentry].ipaddr.s_addr == destip) break;
|
|
|
|
|
if (arpentry == MAX_ARP) {
|
|
|
|
|
printf("%@ is not in my arp table!\n", destip);
|
|
|
|
|
return(0);
|
|
|
|
|
}
|
|
|
|
|
for (i = 0; i < ETH_ALEN; i++)
|
|
|
|
|
if (arptable[arpentry].node[i])
|
|
|
|
|
break;
|
|
|
|
|
if (i == ETH_ALEN) { /* Need to do arp request */
|
|
|
|
|
arpreq.hwtype = htons(1);
|
|
|
|
|
arpreq.protocol = htons(IP);
|
|
|
|
|
arpreq.hwlen = ETH_ALEN;
|
|
|
|
|
arpreq.protolen = 4;
|
|
|
|
|
arpreq.opcode = htons(ARP_REQUEST);
|
|
|
|
|
memcpy(arpreq.shwaddr, arptable[ARP_CLIENT].node, ETH_ALEN);
|
|
|
|
|
memcpy(arpreq.sipaddr, &arptable[ARP_CLIENT].ipaddr, sizeof(in_addr));
|
|
|
|
|
memset(arpreq.thwaddr, 0, ETH_ALEN);
|
|
|
|
|
memcpy(arpreq.tipaddr, &destip, sizeof(in_addr));
|
|
|
|
|
for (retry = 1; retry <= MAX_ARP_RETRIES; retry++) {
|
|
|
|
|
long timeout;
|
|
|
|
|
eth_transmit(broadcast, ETH_P_ARP, sizeof(arpreq),
|
|
|
|
|
&arpreq);
|
|
|
|
|
timeout = rfc2131_sleep_interval(TIMEOUT, retry);
|
|
|
|
|
if (await_reply(await_arp, arpentry,
|
|
|
|
|
arpreq.tipaddr, timeout)) goto xmit;
|
|
|
|
|
}
|
|
|
|
|
return(0);
|
|
|
|
|
}
|
|
|
|
|
xmit:
|
|
|
|
|
eth_transmit(arptable[arpentry].node, ETH_P_IP, len, buf);
|
|
|
|
|
}
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void build_ip_hdr(unsigned long destip, int ttl, int protocol, int option_len,
|
|
|
|
|
int len, const void *buf)
|
|
|
|
|
{
|
|
|
|
|
struct iphdr *ip;
|
|
|
|
|
ip = (struct iphdr *)buf;
|
|
|
|
|
ip->verhdrlen = 0x45;
|
|
|
|
|
ip->verhdrlen += (option_len/4);
|
|
|
|
|
ip->service = 0;
|
|
|
|
|
ip->len = htons(len);
|
|
|
|
|
ip->ident = 0;
|
|
|
|
|
ip->frags = 0; /* Should we set don't fragment? */
|
|
|
|
|
ip->ttl = ttl;
|
|
|
|
|
ip->protocol = protocol;
|
|
|
|
|
ip->chksum = 0;
|
|
|
|
|
ip->src.s_addr = arptable[ARP_CLIENT].ipaddr.s_addr;
|
|
|
|
|
ip->dest.s_addr = destip;
|
|
|
|
|
ip->chksum = ipchksum(buf, sizeof(struct iphdr) + option_len);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void build_udp_hdr(unsigned long destip,
|
|
|
|
|
unsigned int srcsock, unsigned int destsock, int ttl,
|
|
|
|
|
int len, const void *buf)
|
|
|
|
|
{
|
|
|
|
|
struct iphdr *ip;
|
|
|
|
|
struct udphdr *udp;
|
|
|
|
|
ip = (struct iphdr *)buf;
|
|
|
|
|
build_ip_hdr(destip, ttl, IP_UDP, 0, len, buf);
|
|
|
|
|
udp = (struct udphdr *)((char *)buf + sizeof(struct iphdr));
|
|
|
|
|
udp->src = htons(srcsock);
|
|
|
|
|
udp->dest = htons(destsock);
|
|
|
|
|
udp->len = htons(len - sizeof(struct iphdr));
|
|
|
|
|
udp->chksum = 0;
|
|
|
|
|
if ((udp->chksum = tcpudpchksum(ip)) == 0)
|
|
|
|
|
udp->chksum = 0xffff;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef DOWNLOAD_PROTO_HTTP
|
|
|
|
|
void build_tcp_hdr(unsigned long destip, unsigned int srcsock,
|
|
|
|
|
unsigned int destsock, long send_seq, long recv_seq,
|
|
|
|
|
int window, int flags, int ttl, int len, const void *buf)
|
|
|
|
|
{
|
|
|
|
|
struct iphdr *ip;
|
|
|
|
|
struct tcphdr *tcp;
|
|
|
|
|
ip = (struct iphdr *)buf;
|
|
|
|
|
build_ip_hdr(destip, ttl, IP_TCP, 0, len, buf);
|
|
|
|
|
tcp = (struct tcphdr *)(ip + 1);
|
|
|
|
|
tcp->src = htons(srcsock);
|
|
|
|
|
tcp->dst = htons(destsock);
|
|
|
|
|
tcp->seq = htonl(send_seq);
|
|
|
|
|
tcp->ack = htonl(recv_seq);
|
|
|
|
|
tcp->ctrl = htons(flags + (5 << 12)); /* No TCP options */
|
|
|
|
|
tcp->window = htons(window);
|
|
|
|
|
tcp->chksum = 0;
|
|
|
|
|
if ((tcp->chksum = tcpudpchksum(ip)) == 0)
|
|
|
|
|
tcp->chksum = 0xffff;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**************************************************************************
|
|
|
|
|
UDP_TRANSMIT - Send an UDP datagram
|
|
|
|
|
**************************************************************************/
|
|
|
|
|
int udp_transmit(unsigned long destip, unsigned int srcsock,
|
|
|
|
|
unsigned int destsock, int len, const void *buf)
|
|
|
|
|
{
|
|
|
|
|
build_udp_hdr(destip, srcsock, destsock, 60, len, buf);
|
|
|
|
|
return ip_transmit(len, buf);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**************************************************************************
|
|
|
|
|
TCP_TRANSMIT - Send a TCP packet
|
|
|
|
|
**************************************************************************/
|
|
|
|
|
#ifdef DOWNLOAD_PROTO_HTTP
|
|
|
|
|
int tcp_transmit(unsigned long destip, unsigned int srcsock,
|
|
|
|
|
unsigned int destsock, long send_seq, long recv_seq,
|
|
|
|
|
int window, int flags, int len, const void *buf)
|
|
|
|
|
{
|
|
|
|
|
build_tcp_hdr(destip, srcsock, destsock, send_seq, recv_seq,
|
|
|
|
|
window, flags, 60, len, buf);
|
|
|
|
|
return ip_transmit(len, buf);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int tcp_reset(struct iphdr *ip) {
|
|
|
|
|
struct tcphdr *tcp = (struct tcphdr *)(ip + 1);
|
|
|
|
|
char buf[sizeof(struct iphdr) + sizeof(struct tcphdr)];
|
|
|
|
|
|
|
|
|
|
if (!(tcp->ctrl & htons(RST))) {
|
|
|
|
|
long seq = ntohl(tcp->seq) + ntohs(ip->len) -
|
|
|
|
|
sizeof(struct iphdr) -
|
|
|
|
|
((ntohs(tcp->ctrl) >> 10) & 0x3C);
|
|
|
|
|
if (tcp->ctrl & htons(SYN|FIN))
|
|
|
|
|
seq++;
|
|
|
|
|
return tcp_transmit(ntohl(ip->src.s_addr),
|
|
|
|
|
ntohs(tcp->dst), ntohs(tcp->src),
|
|
|
|
|
tcp->ctrl&htons(ACK) ? ntohl(tcp->ack) : 0,
|
|
|
|
|
seq, TCP_MAX_WINDOW, RST, sizeof(buf), buf);
|
|
|
|
|
}
|
|
|
|
|
return (1);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/**************************************************************************
|
|
|
|
|
QDRAIN - clear the nic's receive queue
|
|
|
|
|
**************************************************************************/
|
|
|
|
|
static int await_qdrain(int ival __unused, void *ptr __unused,
|
|
|
|
|
unsigned short ptype __unused,
|
|
|
|
|
struct iphdr *ip __unused, struct udphdr *udp __unused,
|
|
|
|
|
struct tcphdr *tcp __unused)
|
|
|
|
|
{
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void rx_qdrain(void)
|
|
|
|
|
{
|
|
|
|
|
/* Clear out the Rx queue first. It contains nothing of interest,
|
|
|
|
|
* except possibly ARP requests from the DHCP/TFTP server. We use
|
|
|
|
|
* polling throughout Etherboot, so some time may have passed since we
|
|
|
|
|
* last polled the receive queue, which may now be filled with
|
|
|
|
|
* broadcast packets. This will cause the reply to the packets we are
|
|
|
|
|
* about to send to be lost immediately. Not very clever. */
|
|
|
|
|
await_reply(await_qdrain, 0, NULL, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef DOWNLOAD_PROTO_TFTP
|
|
|
|
|
/**************************************************************************
|
|
|
|
|
TFTP - Download extended BOOTP data, or kernel image
|
|
|
|
|
**************************************************************************/
|
|
|
|
|
static int await_tftp(int ival, void *ptr __unused,
|
|
|
|
|
unsigned short ptype __unused, struct iphdr *ip, struct udphdr *udp,
|
|
|
|
|
struct tcphdr *tcp __unused)
|
|
|
|
|
{
|
|
|
|
|
if (!udp) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
if (arptable[ARP_CLIENT].ipaddr.s_addr != ip->dest.s_addr)
|
|
|
|
|
return 0;
|
|
|
|
|
if (ntohs(udp->dest) != ival)
|
|
|
|
|
return 0;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int tftp ( const char *name,
|
|
|
|
|
int (*fnc)(unsigned char *, unsigned int, unsigned int, int) )
|
|
|
|
|
{
|
|
|
|
|
struct tftpreq_info_t request_data =
|
|
|
|
|
{ name, TFTP_PORT, TFTP_MAX_PACKET };
|
|
|
|
|
struct tftpreq_info_t *request = &request_data;
|
|
|
|
|
struct tftpblk_info_t block;
|
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
|
|
while ( tftp_block ( request, &block ) ) {
|
|
|
|
|
request = NULL; /* Send request only once */
|
|
|
|
|
rc = fnc ( block.data, block.block, block.len, block.eof );
|
|
|
|
|
if ( rc <= 0 ) return (rc);
|
|
|
|
|
if ( block.eof ) {
|
|
|
|
|
/* fnc should not have returned */
|
|
|
|
|
printf ( "TFTP download complete, but\n" );
|
|
|
|
|
return (0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return (0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int tftp_block ( struct tftpreq_info_t *request, struct tftpblk_info_t *block )
|
|
|
|
|
{
|
|
|
|
|
static unsigned short lport = 2000; /* local port */
|
|
|
|
|
static unsigned short rport = TFTP_PORT; /* remote port */
|
|
|
|
|
struct tftp_t *rcvd = NULL;
|
|
|
|
|
static struct tftpreq_t xmit;
|
|
|
|
|
static unsigned short xmitlen = 0;
|
|
|
|
|
static unsigned short blockidx = 0; /* Last block received */
|
|
|
|
|
static unsigned short retry = 0; /* Retry attempts on last block */
|
|
|
|
|
static int blksize = 0;
|
|
|
|
|
unsigned short recvlen = 0;
|
|
|
|
|
|
|
|
|
|
/* If this is a new request (i.e. if name is set), fill in
|
|
|
|
|
* transmit block with RRQ and send it.
|
|
|
|
|
*/
|
|
|
|
|
if ( request ) {
|
|
|
|
|
rx_qdrain(); /* Flush receive queue */
|
|
|
|
|
xmit.opcode = htons(TFTP_RRQ);
|
|
|
|
|
xmitlen = (void*)&xmit.u.rrq - (void*)&xmit +
|
|
|
|
|
sprintf((char*)xmit.u.rrq, "%s%coctet%cblksize%c%d",
|
|
|
|
|
request->name, 0, 0, 0, request->blksize)
|
|
|
|
|
+ 1; /* null terminator */
|
|
|
|
|
blockidx = 0; /* Reset counters */
|
|
|
|
|
retry = 0;
|
|
|
|
|
blksize = TFTP_DEFAULTSIZE_PACKET;
|
|
|
|
|
lport++; /* Use new local port */
|
|
|
|
|
rport = request->port;
|
|
|
|
|
if ( !udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr, lport,
|
|
|
|
|
rport, xmitlen, &xmit) )
|
|
|
|
|
return (0);
|
|
|
|
|
}
|
|
|
|
|
/* Exit if no transfer in progress */
|
|
|
|
|
if ( !blksize ) return (0);
|
|
|
|
|
/* Loop to wait until we get a packet we're interested in */
|
|
|
|
|
block->data = NULL; /* Used as flag */
|
|
|
|
|
while ( block->data == NULL ) {
|
|
|
|
|
long timeout = rfc2131_sleep_interval ( blockidx ? TFTP_REXMT :
|
|
|
|
|
TIMEOUT, retry );
|
|
|
|
|
if ( !await_reply(await_tftp, lport, NULL, timeout) ) {
|
|
|
|
|
/* No packet received */
|
|
|
|
|
if ( retry++ > MAX_TFTP_RETRIES ) break;
|
|
|
|
|
/* Retransmit last packet */
|
|
|
|
|
if ( !blockidx ) lport++; /* New lport if new RRQ */
|
|
|
|
|
if ( !udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr,
|
|
|
|
|
lport, rport, xmitlen, &xmit) )
|
|
|
|
|
return (0);
|
|
|
|
|
continue; /* Back to waiting for packet */
|
|
|
|
|
}
|
|
|
|
|
/* Packet has been received */
|
2005-04-12 16:52:59 +00:00
|
|
|
|
rcvd = (struct tftp_t *)&nic->packet[ETH_HLEN];
|
2005-03-08 18:53:11 +00:00
|
|
|
|
recvlen = ntohs(rcvd->udp.len) - sizeof(struct udphdr)
|
|
|
|
|
- sizeof(rcvd->opcode);
|
|
|
|
|
rport = ntohs(rcvd->udp.src);
|
|
|
|
|
retry = 0; /* Reset retry counter */
|
|
|
|
|
switch ( htons(rcvd->opcode) ) {
|
|
|
|
|
case TFTP_ERROR : {
|
|
|
|
|
printf ( "TFTP error %d (%s)\n",
|
|
|
|
|
ntohs(rcvd->u.err.errcode),
|
|
|
|
|
rcvd->u.err.errmsg );
|
|
|
|
|
return (0); /* abort */
|
|
|
|
|
}
|
|
|
|
|
case TFTP_OACK : {
|
|
|
|
|
const char *p = rcvd->u.oack.data;
|
|
|
|
|
const char *e = p + recvlen - 10; /* "blksize\0\d\0" */
|
|
|
|
|
|
|
|
|
|
*((char*)(p+recvlen-1)) = '\0'; /* Force final 0 */
|
|
|
|
|
if ( blockidx || !request ) break; /* Too late */
|
|
|
|
|
if ( recvlen <= TFTP_MAX_PACKET ) /* sanity */ {
|
|
|
|
|
/* Check for blksize option honoured */
|
|
|
|
|
while ( p < e ) {
|
|
|
|
|
if ( strcasecmp("blksize",p) == 0 &&
|
|
|
|
|
p[7] == '\0' ) {
|
|
|
|
|
blksize = strtoul(p+8,&p,10);
|
|
|
|
|
p++; /* skip null */
|
|
|
|
|
}
|
|
|
|
|
while ( *(p++) ) {};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if ( blksize < TFTP_DEFAULTSIZE_PACKET || blksize > request->blksize ) {
|
|
|
|
|
/* Incorrect blksize - error and abort */
|
|
|
|
|
xmit.opcode = htons(TFTP_ERROR);
|
|
|
|
|
xmit.u.err.errcode = 8;
|
|
|
|
|
xmitlen = (void*)&xmit.u.err.errmsg
|
|
|
|
|
- (void*)&xmit
|
|
|
|
|
+ sprintf((char*)xmit.u.err.errmsg,
|
|
|
|
|
"RFC1782 error")
|
|
|
|
|
+ 1;
|
|
|
|
|
udp_transmit(
|
|
|
|
|
arptable[ARP_SERVER].ipaddr.s_addr,
|
|
|
|
|
lport, rport, xmitlen, &xmit);
|
|
|
|
|
return (0);
|
|
|
|
|
}
|
|
|
|
|
} break;
|
|
|
|
|
case TFTP_DATA :
|
|
|
|
|
if ( ntohs(rcvd->u.data.block) != ( blockidx + 1 ) )
|
|
|
|
|
break; /* Re-ACK last block sent */
|
|
|
|
|
if ( recvlen > ( blksize+sizeof(rcvd->u.data.block) ) )
|
|
|
|
|
break; /* Too large; ignore */
|
|
|
|
|
block->data = rcvd->u.data.download;
|
|
|
|
|
block->block = ++blockidx;
|
|
|
|
|
block->len = recvlen - sizeof(rcvd->u.data.block);
|
|
|
|
|
block->eof = ( (unsigned short)block->len < blksize );
|
|
|
|
|
/* If EOF, zero blksize to indicate transfer done */
|
|
|
|
|
if ( block->eof ) blksize = 0;
|
|
|
|
|
break;
|
|
|
|
|
default: break; /* Do nothing */
|
|
|
|
|
}
|
|
|
|
|
/* Send ACK */
|
|
|
|
|
xmit.opcode = htons(TFTP_ACK);
|
|
|
|
|
xmit.u.ack.block = htons(blockidx);
|
|
|
|
|
xmitlen = TFTP_MIN_PACKET;
|
|
|
|
|
udp_transmit ( arptable[ARP_SERVER].ipaddr.s_addr,
|
|
|
|
|
lport, rport, xmitlen, &xmit );
|
|
|
|
|
}
|
|
|
|
|
return ( block->data ? 1 : 0 );
|
|
|
|
|
}
|
|
|
|
|
#endif /* DOWNLOAD_PROTO_TFTP */
|
|
|
|
|
|
|
|
|
|
#ifdef RARP_NOT_BOOTP
|
|
|
|
|
/**************************************************************************
|
|
|
|
|
RARP - Get my IP address and load information
|
|
|
|
|
**************************************************************************/
|
|
|
|
|
static int await_rarp(int ival, void *ptr,
|
|
|
|
|
unsigned short ptype, struct iphdr *ip, struct udphdr *udp,
|
|
|
|
|
struct tcphdr *tcp __unused)
|
|
|
|
|
{
|
|
|
|
|
struct arprequest *arpreply;
|
|
|
|
|
if (ptype != ETH_P_RARP)
|
|
|
|
|
return 0;
|
2005-04-12 16:52:59 +00:00
|
|
|
|
if (nic->packetlen < ETH_HLEN + sizeof(struct arprequest))
|
2005-03-08 18:53:11 +00:00
|
|
|
|
return 0;
|
2005-04-12 16:52:59 +00:00
|
|
|
|
arpreply = (struct arprequest *)&nic->packet[ETH_HLEN];
|
2005-03-08 18:53:11 +00:00
|
|
|
|
if (arpreply->opcode != htons(RARP_REPLY))
|
|
|
|
|
return 0;
|
|
|
|
|
if ((arpreply->opcode == htons(RARP_REPLY)) &&
|
|
|
|
|
(memcmp(arpreply->thwaddr, ptr, ETH_ALEN) == 0)) {
|
|
|
|
|
memcpy(arptable[ARP_SERVER].node, arpreply->shwaddr, ETH_ALEN);
|
|
|
|
|
memcpy(&arptable[ARP_SERVER].ipaddr, arpreply->sipaddr, sizeof(in_addr));
|
|
|
|
|
memcpy(&arptable[ARP_CLIENT].ipaddr, arpreply->tipaddr, sizeof(in_addr));
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int rarp(void)
|
|
|
|
|
{
|
|
|
|
|
int retry;
|
|
|
|
|
|
|
|
|
|
/* arp and rarp requests share the same packet structure. */
|
|
|
|
|
struct arprequest rarpreq;
|
|
|
|
|
|
|
|
|
|
memset(&rarpreq, 0, sizeof(rarpreq));
|
|
|
|
|
|
|
|
|
|
rarpreq.hwtype = htons(1);
|
|
|
|
|
rarpreq.protocol = htons(IP);
|
|
|
|
|
rarpreq.hwlen = ETH_ALEN;
|
|
|
|
|
rarpreq.protolen = 4;
|
|
|
|
|
rarpreq.opcode = htons(RARP_REQUEST);
|
|
|
|
|
memcpy(&rarpreq.shwaddr, arptable[ARP_CLIENT].node, ETH_ALEN);
|
|
|
|
|
/* sipaddr is already zeroed out */
|
|
|
|
|
memcpy(&rarpreq.thwaddr, arptable[ARP_CLIENT].node, ETH_ALEN);
|
|
|
|
|
/* tipaddr is already zeroed out */
|
|
|
|
|
|
|
|
|
|
for (retry = 0; retry < MAX_ARP_RETRIES; ++retry) {
|
|
|
|
|
long timeout;
|
|
|
|
|
eth_transmit(broadcast, ETH_P_RARP, sizeof(rarpreq), &rarpreq);
|
|
|
|
|
|
|
|
|
|
timeout = rfc2131_sleep_interval(TIMEOUT, retry);
|
|
|
|
|
if (await_reply(await_rarp, 0, rarpreq.shwaddr, timeout))
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (retry < MAX_ARP_RETRIES) {
|
|
|
|
|
(void)sprintf(KERNEL_BUF, DEFAULT_KERNELPATH, arptable[ARP_CLIENT].ipaddr);
|
|
|
|
|
|
|
|
|
|
return (1);
|
|
|
|
|
}
|
|
|
|
|
return (0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
|
|
/**************************************************************************
|
|
|
|
|
BOOTP - Get my IP address and load information
|
|
|
|
|
**************************************************************************/
|
|
|
|
|
static int await_bootp(int ival __unused, void *ptr __unused,
|
|
|
|
|
unsigned short ptype __unused, struct iphdr *ip __unused,
|
|
|
|
|
struct udphdr *udp, struct tcphdr *tcp __unused)
|
|
|
|
|
{
|
|
|
|
|
struct bootp_t *bootpreply;
|
|
|
|
|
if (!udp) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2005-04-12 16:52:59 +00:00
|
|
|
|
bootpreply = (struct bootp_t *)&nic->packet[ETH_HLEN +
|
2005-03-08 18:53:11 +00:00
|
|
|
|
sizeof(struct iphdr) + sizeof(struct udphdr)];
|
2005-04-12 16:52:59 +00:00
|
|
|
|
if (nic->packetlen < ETH_HLEN + sizeof(struct iphdr) +
|
2005-03-08 18:53:11 +00:00
|
|
|
|
sizeof(struct udphdr) +
|
|
|
|
|
#ifdef NO_DHCP_SUPPORT
|
|
|
|
|
sizeof(struct bootp_t)
|
|
|
|
|
#else
|
|
|
|
|
sizeof(struct bootp_t) - DHCP_OPT_LEN
|
|
|
|
|
#endif /* NO_DHCP_SUPPORT */
|
|
|
|
|
) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
if (udp->dest != htons(BOOTP_CLIENT))
|
|
|
|
|
return 0;
|
|
|
|
|
if (bootpreply->bp_op != BOOTP_REPLY)
|
|
|
|
|
return 0;
|
|
|
|
|
if (bootpreply->bp_xid != xid)
|
|
|
|
|
return 0;
|
|
|
|
|
if (memcmp(&bootpreply->bp_siaddr, &zeroIP, sizeof(in_addr)) == 0)
|
|
|
|
|
return 0;
|
|
|
|
|
if ((memcmp(broadcast, bootpreply->bp_hwaddr, ETH_ALEN) != 0) &&
|
|
|
|
|
(memcmp(arptable[ARP_CLIENT].node, bootpreply->bp_hwaddr, ETH_ALEN) != 0)) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
if ( bootpreply->bp_siaddr.s_addr ) {
|
|
|
|
|
arptable[ARP_SERVER].ipaddr.s_addr = bootpreply->bp_siaddr.s_addr;
|
|
|
|
|
memset(arptable[ARP_SERVER].node, 0, ETH_ALEN); /* Kill arp */
|
|
|
|
|
}
|
|
|
|
|
if ( bootpreply->bp_giaddr.s_addr ) {
|
|
|
|
|
arptable[ARP_GATEWAY].ipaddr.s_addr = bootpreply->bp_giaddr.s_addr;
|
|
|
|
|
memset(arptable[ARP_GATEWAY].node, 0, ETH_ALEN); /* Kill arp */
|
|
|
|
|
}
|
|
|
|
|
if (bootpreply->bp_yiaddr.s_addr) {
|
|
|
|
|
/* Offer with an IP address */
|
|
|
|
|
arptable[ARP_CLIENT].ipaddr.s_addr = bootpreply->bp_yiaddr.s_addr;
|
|
|
|
|
#ifndef NO_DHCP_SUPPORT
|
|
|
|
|
dhcp_addr.s_addr = bootpreply->bp_yiaddr.s_addr;
|
|
|
|
|
#endif /* NO_DHCP_SUPPORT */
|
|
|
|
|
netmask = default_netmask();
|
|
|
|
|
/* bootpreply->bp_file will be copied to KERNEL_BUF in the memcpy */
|
|
|
|
|
memcpy((char *)BOOTP_DATA_ADDR, (char *)bootpreply, sizeof(struct bootpd_t));
|
|
|
|
|
decode_rfc1533(BOOTP_DATA_ADDR->bootp_reply.bp_vend, 0,
|
|
|
|
|
#ifdef NO_DHCP_SUPPORT
|
|
|
|
|
BOOTP_VENDOR_LEN + MAX_BOOTP_EXTLEN,
|
|
|
|
|
#else
|
|
|
|
|
DHCP_OPT_LEN + MAX_BOOTP_EXTLEN,
|
|
|
|
|
#endif /* NO_DHCP_SUPPORT */
|
|
|
|
|
1);
|
|
|
|
|
#ifdef PXE_EXPORT
|
|
|
|
|
} else {
|
|
|
|
|
/* Offer without an IP address - use as ProxyDHCP server */
|
|
|
|
|
arptable[ARP_PROXYDHCP].ipaddr.s_addr = bootpreply->bp_siaddr.s_addr;
|
|
|
|
|
memset(arptable[ARP_PROXYDHCP].node, 0, ETH_ALEN); /* Kill arp */
|
|
|
|
|
/* Grab only the bootfile name from a ProxyDHCP packet */
|
|
|
|
|
memcpy(KERNEL_BUF, bootpreply->bp_file, sizeof(KERNEL_BUF));
|
|
|
|
|
#endif /* PXE_EXPORT */
|
|
|
|
|
}
|
|
|
|
|
#ifdef REQUIRE_VCI_ETHERBOOT
|
|
|
|
|
if (!vci_etherboot)
|
|
|
|
|
return (0);
|
|
|
|
|
#endif
|
|
|
|
|
return(1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int bootp(void)
|
|
|
|
|
{
|
|
|
|
|
int retry;
|
|
|
|
|
#ifndef NO_DHCP_SUPPORT
|
|
|
|
|
int reqretry;
|
|
|
|
|
#endif /* NO_DHCP_SUPPORT */
|
|
|
|
|
struct bootpip_t ip;
|
|
|
|
|
unsigned long starttime;
|
|
|
|
|
unsigned char *bp_vend;
|
|
|
|
|
|
|
|
|
|
#ifndef NO_DHCP_SUPPORT
|
2005-04-12 16:52:59 +00:00
|
|
|
|
dhcp_machine_info[4] = dev.devid.bus_type;
|
|
|
|
|
dhcp_machine_info[5] = dev.devid.vendor_id & 0xff;
|
|
|
|
|
dhcp_machine_info[6] = ((dev.devid.vendor_id) >> 8) & 0xff;
|
|
|
|
|
dhcp_machine_info[7] = dev.devid.device_id & 0xff;
|
|
|
|
|
dhcp_machine_info[8] = ((dev.devid.device_id) >> 8) & 0xff;
|
2005-03-08 18:53:11 +00:00
|
|
|
|
#endif /* NO_DHCP_SUPPORT */
|
|
|
|
|
memset(&ip, 0, sizeof(struct bootpip_t));
|
|
|
|
|
ip.bp.bp_op = BOOTP_REQUEST;
|
|
|
|
|
ip.bp.bp_htype = 1;
|
|
|
|
|
ip.bp.bp_hlen = ETH_ALEN;
|
|
|
|
|
starttime = currticks();
|
|
|
|
|
/* Use lower 32 bits of node address, more likely to be
|
|
|
|
|
distinct than the time since booting */
|
|
|
|
|
memcpy(&xid, &arptable[ARP_CLIENT].node[2], sizeof(xid));
|
|
|
|
|
ip.bp.bp_xid = xid += htonl(starttime);
|
|
|
|
|
memcpy(ip.bp.bp_hwaddr, arptable[ARP_CLIENT].node, ETH_ALEN);
|
|
|
|
|
#ifdef NO_DHCP_SUPPORT
|
|
|
|
|
memcpy(ip.bp.bp_vend, rfc1533_cookie, 5); /* request RFC-style options */
|
|
|
|
|
#else
|
|
|
|
|
memcpy(ip.bp.bp_vend, rfc1533_cookie, sizeof rfc1533_cookie); /* request RFC-style options */
|
|
|
|
|
memcpy(ip.bp.bp_vend + sizeof rfc1533_cookie, dhcpdiscover, sizeof dhcpdiscover);
|
|
|
|
|
/* Append machine_info to end, in encapsulated option */
|
|
|
|
|
bp_vend = ip.bp.bp_vend + sizeof rfc1533_cookie + sizeof dhcpdiscover;
|
|
|
|
|
memcpy(bp_vend, dhcp_machine_info, DHCP_MACHINE_INFO_SIZE);
|
|
|
|
|
bp_vend += DHCP_MACHINE_INFO_SIZE;
|
|
|
|
|
*bp_vend++ = RFC1533_END;
|
|
|
|
|
#endif /* NO_DHCP_SUPPORT */
|
|
|
|
|
|
|
|
|
|
for (retry = 0; retry < MAX_BOOTP_RETRIES; ) {
|
|
|
|
|
uint8_t my_hwaddr[ETH_ALEN];
|
|
|
|
|
unsigned long stop_time;
|
|
|
|
|
long remaining_time;
|
|
|
|
|
|
|
|
|
|
rx_qdrain();
|
|
|
|
|
|
|
|
|
|
/* Kill arptable to avoid keeping stale entries */
|
|
|
|
|
memcpy ( my_hwaddr, arptable[ARP_CLIENT].node, ETH_ALEN );
|
|
|
|
|
memset ( arptable, 0, sizeof(arptable) );
|
|
|
|
|
memcpy ( arptable[ARP_CLIENT].node, my_hwaddr, ETH_ALEN );
|
|
|
|
|
|
|
|
|
|
udp_transmit(IP_BROADCAST, BOOTP_CLIENT, BOOTP_SERVER,
|
|
|
|
|
sizeof(struct bootpip_t), &ip);
|
|
|
|
|
remaining_time = rfc2131_sleep_interval(BOOTP_TIMEOUT, retry++);
|
|
|
|
|
stop_time = currticks() + remaining_time;
|
|
|
|
|
#ifdef NO_DHCP_SUPPORT
|
|
|
|
|
if (await_reply(await_bootp, 0, NULL, timeout))
|
|
|
|
|
return(1);
|
|
|
|
|
#else
|
|
|
|
|
while ( remaining_time > 0 ) {
|
|
|
|
|
if (await_reply(await_bootp, 0, NULL, remaining_time)){
|
|
|
|
|
}
|
|
|
|
|
remaining_time = stop_time - currticks();
|
|
|
|
|
}
|
|
|
|
|
if ( ! arptable[ARP_CLIENT].ipaddr.s_addr ) {
|
|
|
|
|
printf("No IP address\n");
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
/* If not a DHCPOFFER then must be just a BOOTP reply,
|
|
|
|
|
* be backward compatible with BOOTP then */
|
|
|
|
|
if (dhcp_reply != DHCPOFFER)
|
|
|
|
|
return(1);
|
|
|
|
|
dhcp_reply = 0;
|
|
|
|
|
/* Construct the DHCPREQUEST packet */
|
|
|
|
|
memcpy(ip.bp.bp_vend, rfc1533_cookie, sizeof rfc1533_cookie);
|
|
|
|
|
memcpy(ip.bp.bp_vend + sizeof rfc1533_cookie, dhcprequest, sizeof dhcprequest);
|
|
|
|
|
/* Beware: the magic numbers 9 and 15 depend on
|
|
|
|
|
the layout of dhcprequest */
|
|
|
|
|
memcpy(&ip.bp.bp_vend[9], &dhcp_server, sizeof(in_addr));
|
|
|
|
|
memcpy(&ip.bp.bp_vend[15], &dhcp_addr, sizeof(in_addr));
|
|
|
|
|
bp_vend = ip.bp.bp_vend + sizeof rfc1533_cookie + sizeof dhcprequest;
|
|
|
|
|
/* Append machine_info to end, in encapsulated option */
|
|
|
|
|
memcpy(bp_vend, dhcp_machine_info, DHCP_MACHINE_INFO_SIZE);
|
|
|
|
|
bp_vend += DHCP_MACHINE_INFO_SIZE;
|
|
|
|
|
*bp_vend++ = RFC1533_END;
|
|
|
|
|
for (reqretry = 0; reqretry < MAX_BOOTP_RETRIES; ) {
|
|
|
|
|
unsigned long timeout;
|
|
|
|
|
|
|
|
|
|
udp_transmit(IP_BROADCAST, BOOTP_CLIENT, BOOTP_SERVER,
|
|
|
|
|
sizeof(struct bootpip_t), &ip);
|
|
|
|
|
dhcp_reply=0;
|
|
|
|
|
timeout = rfc2131_sleep_interval(TIMEOUT, reqretry++);
|
|
|
|
|
if (!await_reply(await_bootp, 0, NULL, timeout))
|
|
|
|
|
continue;
|
|
|
|
|
if (dhcp_reply != DHCPACK)
|
|
|
|
|
continue;
|
|
|
|
|
dhcp_reply = 0;
|
|
|
|
|
#ifdef PXE_EXPORT
|
|
|
|
|
if ( arptable[ARP_PROXYDHCP].ipaddr.s_addr ) {
|
|
|
|
|
/* Construct the ProxyDHCPREQUEST packet */
|
|
|
|
|
memcpy(ip.bp.bp_vend, rfc1533_cookie, sizeof rfc1533_cookie);
|
|
|
|
|
memcpy(ip.bp.bp_vend + sizeof rfc1533_cookie, proxydhcprequest, sizeof proxydhcprequest);
|
|
|
|
|
for (reqretry = 0; reqretry < MAX_BOOTP_RETRIES; ) {
|
|
|
|
|
printf ( "\nSending ProxyDHCP request to %@...", arptable[ARP_PROXYDHCP].ipaddr.s_addr);
|
|
|
|
|
udp_transmit(arptable[ARP_PROXYDHCP].ipaddr.s_addr, BOOTP_CLIENT, PROXYDHCP_SERVER,
|
|
|
|
|
sizeof(struct bootpip_t), &ip);
|
|
|
|
|
timeout = rfc2131_sleep_interval(TIMEOUT, reqretry++);
|
|
|
|
|
if (await_reply(await_bootp, 0, NULL, timeout)) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif /* PXE_EXPORT */
|
|
|
|
|
return(1);
|
|
|
|
|
}
|
|
|
|
|
#endif /* NO_DHCP_SUPPORT */
|
|
|
|
|
ip.bp.bp_secs = htons((currticks()-starttime)/TICKS_PER_SEC);
|
|
|
|
|
}
|
|
|
|
|
return(0);
|
|
|
|
|
}
|
|
|
|
|
#endif /* RARP_NOT_BOOTP */
|
|
|
|
|
|
|
|
|
|
static uint16_t tcpudpchksum(struct iphdr *ip)
|
|
|
|
|
{
|
|
|
|
|
struct udp_pseudo_hdr pseudo;
|
|
|
|
|
uint16_t checksum;
|
|
|
|
|
|
|
|
|
|
/* Compute the pseudo header */
|
|
|
|
|
pseudo.src.s_addr = ip->src.s_addr;
|
|
|
|
|
pseudo.dest.s_addr = ip->dest.s_addr;
|
|
|
|
|
pseudo.unused = 0;
|
|
|
|
|
pseudo.protocol = ip->protocol;
|
|
|
|
|
pseudo.len = htons(ntohs(ip->len) - sizeof(struct iphdr));
|
|
|
|
|
|
|
|
|
|
/* Sum the pseudo header */
|
|
|
|
|
checksum = ipchksum(&pseudo, 12);
|
|
|
|
|
|
|
|
|
|
/* Sum the rest of the tcp/udp packet */
|
|
|
|
|
checksum = add_ipchksums(12, checksum, ipchksum(ip + 1,
|
|
|
|
|
ntohs(ip->len) - sizeof(struct iphdr)));
|
|
|
|
|
return checksum;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef MULTICAST_LEVEL2
|
|
|
|
|
static void send_igmp_reports(unsigned long now)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
for(i = 0; i < MAX_IGMP; i++) {
|
|
|
|
|
if (igmptable[i].time && (now >= igmptable[i].time)) {
|
|
|
|
|
struct igmp_ip_t igmp;
|
|
|
|
|
igmp.router_alert[0] = 0x94;
|
|
|
|
|
igmp.router_alert[1] = 0x04;
|
|
|
|
|
igmp.router_alert[2] = 0;
|
|
|
|
|
igmp.router_alert[3] = 0;
|
|
|
|
|
build_ip_hdr(igmptable[i].group.s_addr,
|
|
|
|
|
1, IP_IGMP, sizeof(igmp.router_alert), sizeof(igmp), &igmp);
|
|
|
|
|
igmp.igmp.type = IGMPv2_REPORT;
|
|
|
|
|
if (last_igmpv1 &&
|
|
|
|
|
(now < last_igmpv1 + IGMPv1_ROUTER_PRESENT_TIMEOUT)) {
|
|
|
|
|
igmp.igmp.type = IGMPv1_REPORT;
|
|
|
|
|
}
|
|
|
|
|
igmp.igmp.response_time = 0;
|
|
|
|
|
igmp.igmp.chksum = 0;
|
|
|
|
|
igmp.igmp.group.s_addr = igmptable[i].group.s_addr;
|
|
|
|
|
igmp.igmp.chksum = ipchksum(&igmp.igmp, sizeof(igmp.igmp));
|
|
|
|
|
ip_transmit(sizeof(igmp), &igmp);
|
|
|
|
|
#ifdef MDEBUG
|
|
|
|
|
printf("Sent IGMP report to: %@\n", igmp.igmp.group.s_addr);
|
|
|
|
|
#endif
|
|
|
|
|
/* Don't send another igmp report until asked */
|
|
|
|
|
igmptable[i].time = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void process_igmp(struct iphdr *ip, unsigned long now)
|
|
|
|
|
{
|
|
|
|
|
struct igmp *igmp;
|
|
|
|
|
int i;
|
|
|
|
|
unsigned iplen;
|
|
|
|
|
if (!ip || (ip->protocol == IP_IGMP) ||
|
2005-04-12 16:52:59 +00:00
|
|
|
|
(nic->packetlen < sizeof(struct iphdr) + sizeof(struct igmp))) {
|
2005-03-08 18:53:11 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
iplen = (ip->verhdrlen & 0xf)*4;
|
2005-04-12 16:52:59 +00:00
|
|
|
|
igmp = (struct igmp *)&nic->packet[sizeof(struct iphdr)];
|
2005-03-08 18:53:11 +00:00
|
|
|
|
if (ipchksum(igmp, ntohs(ip->len) - iplen) != 0)
|
|
|
|
|
return;
|
|
|
|
|
if ((igmp->type == IGMP_QUERY) &&
|
|
|
|
|
(ip->dest.s_addr == htonl(GROUP_ALL_HOSTS))) {
|
|
|
|
|
unsigned long interval = IGMP_INTERVAL;
|
|
|
|
|
if (igmp->response_time == 0) {
|
|
|
|
|
last_igmpv1 = now;
|
|
|
|
|
} else {
|
|
|
|
|
interval = (igmp->response_time * TICKS_PER_SEC)/10;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef MDEBUG
|
|
|
|
|
printf("Received IGMP query for: %@\n", igmp->group.s_addr);
|
|
|
|
|
#endif
|
|
|
|
|
for(i = 0; i < MAX_IGMP; i++) {
|
|
|
|
|
uint32_t group = igmptable[i].group.s_addr;
|
|
|
|
|
if ((group == 0) || (group == igmp->group.s_addr)) {
|
|
|
|
|
unsigned long time;
|
|
|
|
|
time = currticks() + rfc1112_sleep_interval(interval, 0);
|
|
|
|
|
if (time < igmptable[i].time) {
|
|
|
|
|
igmptable[i].time = time;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (((igmp->type == IGMPv1_REPORT) || (igmp->type == IGMPv2_REPORT)) &&
|
|
|
|
|
(ip->dest.s_addr == igmp->group.s_addr)) {
|
|
|
|
|
#ifdef MDEBUG
|
|
|
|
|
printf("Received IGMP report for: %@\n", igmp->group.s_addr);
|
|
|
|
|
#endif
|
|
|
|
|
for(i = 0; i < MAX_IGMP; i++) {
|
|
|
|
|
if ((igmptable[i].group.s_addr == igmp->group.s_addr) &&
|
|
|
|
|
igmptable[i].time != 0) {
|
|
|
|
|
igmptable[i].time = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void leave_group(int slot)
|
|
|
|
|
{
|
|
|
|
|
/* Be very stupid and always send a leave group message if
|
|
|
|
|
* I have subscribed. Imperfect but it is standards
|
|
|
|
|
* compliant, easy and reliable to implement.
|
|
|
|
|
*
|
|
|
|
|
* The optimal group leave method is to only send leave when,
|
|
|
|
|
* we were the last host to respond to a query on this group,
|
|
|
|
|
* and igmpv1 compatibility is not enabled.
|
|
|
|
|
*/
|
|
|
|
|
if (igmptable[slot].group.s_addr) {
|
|
|
|
|
struct igmp_ip_t igmp;
|
|
|
|
|
igmp.router_alert[0] = 0x94;
|
|
|
|
|
igmp.router_alert[1] = 0x04;
|
|
|
|
|
igmp.router_alert[2] = 0;
|
|
|
|
|
igmp.router_alert[3] = 0;
|
|
|
|
|
build_ip_hdr(htonl(GROUP_ALL_HOSTS),
|
|
|
|
|
1, IP_IGMP, sizeof(igmp.router_alert), sizeof(igmp), &igmp);
|
|
|
|
|
igmp.igmp.type = IGMP_LEAVE;
|
|
|
|
|
igmp.igmp.response_time = 0;
|
|
|
|
|
igmp.igmp.chksum = 0;
|
|
|
|
|
igmp.igmp.group.s_addr = igmptable[slot].group.s_addr;
|
|
|
|
|
igmp.igmp.chksum = ipchksum(&igmp.igmp, sizeof(igmp));
|
|
|
|
|
ip_transmit(sizeof(igmp), &igmp);
|
|
|
|
|
#ifdef MDEBUG
|
|
|
|
|
printf("Sent IGMP leave for: %@\n", igmp.igmp.group.s_addr);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
memset(&igmptable[slot], 0, sizeof(igmptable[0]));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void join_group(int slot, unsigned long group)
|
|
|
|
|
{
|
|
|
|
|
/* I have already joined */
|
|
|
|
|
if (igmptable[slot].group.s_addr == group)
|
|
|
|
|
return;
|
|
|
|
|
if (igmptable[slot].group.s_addr) {
|
|
|
|
|
leave_group(slot);
|
|
|
|
|
}
|
|
|
|
|
/* Only join a group if we are given a multicast ip, this way
|
|
|
|
|
* code can be given a non-multicast (broadcast or unicast ip)
|
|
|
|
|
* and still work...
|
|
|
|
|
*/
|
|
|
|
|
if ((group & htonl(MULTICAST_MASK)) == htonl(MULTICAST_NETWORK)) {
|
|
|
|
|
igmptable[slot].group.s_addr = group;
|
|
|
|
|
igmptable[slot].time = currticks();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#else
|
|
|
|
|
#define send_igmp_reports(now) do {} while(0)
|
|
|
|
|
#define process_igmp(ip, now) do {} while(0)
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#include "proto_eth_slow.c"
|
|
|
|
|
|
|
|
|
|
/**************************************************************************
|
|
|
|
|
TCP - Simple-minded TCP stack. Can only send data once and then
|
|
|
|
|
receive the response. The algorithm for computing window
|
|
|
|
|
sizes and delaying ack's is currently broken, and thus
|
|
|
|
|
disabled. Performance would probably improve a little, if
|
|
|
|
|
this gets fixed. FIXME
|
|
|
|
|
**************************************************************************/
|
|
|
|
|
#ifdef DOWNLOAD_PROTO_HTTP
|
|
|
|
|
static int await_tcp(int ival, void *ptr, unsigned short ptype __unused,
|
|
|
|
|
struct iphdr *ip, struct udphdr *udp __unused,
|
|
|
|
|
struct tcphdr *tcp)
|
|
|
|
|
{
|
|
|
|
|
if (!tcp) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
if (arptable[ARP_CLIENT].ipaddr.s_addr != ip->dest.s_addr)
|
|
|
|
|
return 0;
|
|
|
|
|
if (ntohs(tcp->dst) != ival) {
|
|
|
|
|
tcp_reset(ip);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
*(void **)ptr = tcp;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int tcp_transaction(unsigned long destip, unsigned int destsock, void *ptr,
|
|
|
|
|
int (*send)(int len, void *buf, void *ptr),
|
|
|
|
|
int (*recv)(int len, const void *buf, void *ptr)) {
|
|
|
|
|
static uint16_t srcsock = 0;
|
|
|
|
|
int rc = 1;
|
|
|
|
|
long send_seq = currticks();
|
|
|
|
|
long recv_seq = 0;
|
|
|
|
|
int can_send = 0;
|
|
|
|
|
int sent_all = 0;
|
|
|
|
|
struct iphdr *ip;
|
|
|
|
|
struct tcphdr *tcp;
|
|
|
|
|
int ctrl = SYN;
|
|
|
|
|
char buf[128]; /* Small outgoing buffer */
|
|
|
|
|
long payload;
|
|
|
|
|
int header_size;
|
|
|
|
|
int window = 3*TCP_MIN_WINDOW;
|
|
|
|
|
long last_ack = 0;
|
|
|
|
|
long last_sent = 0;
|
|
|
|
|
long rtt = 0;
|
|
|
|
|
long srtt = 0;
|
|
|
|
|
long rto = TCP_INITIAL_TIMEOUT;
|
|
|
|
|
int retry = TCP_MAX_TIMEOUT/TCP_INITIAL_TIMEOUT;
|
|
|
|
|
enum { CLOSED, SYN_RCVD, ESTABLISHED,
|
|
|
|
|
FIN_WAIT_1, FIN_WAIT_2 } state = CLOSED;
|
|
|
|
|
|
|
|
|
|
if (!srcsock) {
|
|
|
|
|
srcsock = currticks();
|
|
|
|
|
}
|
|
|
|
|
if (++srcsock < 1024)
|
|
|
|
|
srcsock += 1024;
|
|
|
|
|
|
|
|
|
|
await_reply(await_qdrain, 0, NULL, 0);
|
|
|
|
|
|
|
|
|
|
send_data:
|
|
|
|
|
if (ctrl & ACK)
|
|
|
|
|
last_ack = recv_seq;
|
|
|
|
|
if (!tcp_transmit(destip, srcsock, destsock, send_seq,
|
|
|
|
|
recv_seq, window, ctrl,
|
|
|
|
|
sizeof(struct iphdr) + sizeof(struct tcphdr)+
|
|
|
|
|
can_send, buf)) {
|
|
|
|
|
return (0);
|
|
|
|
|
}
|
|
|
|
|
last_sent = currticks();
|
|
|
|
|
|
|
|
|
|
recv_data:
|
|
|
|
|
if (!await_reply(await_tcp, srcsock, &tcp,
|
|
|
|
|
(state == ESTABLISHED && !can_send)
|
|
|
|
|
? TCP_MAX_TIMEOUT : rto)) {
|
|
|
|
|
if (state == ESTABLISHED) {
|
|
|
|
|
close:
|
|
|
|
|
ctrl = FIN|ACK;
|
|
|
|
|
state = FIN_WAIT_1;
|
|
|
|
|
rc = 0;
|
|
|
|
|
goto send_data;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (state == FIN_WAIT_1 || state == FIN_WAIT_2)
|
|
|
|
|
return (rc);
|
|
|
|
|
|
|
|
|
|
if (--retry <= 0) {
|
|
|
|
|
/* time out */
|
|
|
|
|
if (state == SYN_RCVD) {
|
|
|
|
|
tcp_transmit(destip, srcsock, destsock,
|
|
|
|
|
send_seq, 0, window, RST,
|
|
|
|
|
sizeof(struct iphdr) +
|
|
|
|
|
sizeof(struct tcphdr), buf);
|
|
|
|
|
}
|
|
|
|
|
return (0);
|
|
|
|
|
}
|
|
|
|
|
/* retransmit */
|
|
|
|
|
goto send_data;
|
|
|
|
|
}
|
|
|
|
|
got_data:
|
|
|
|
|
retry = TCP_MAX_RETRY;
|
|
|
|
|
|
|
|
|
|
if (tcp->ctrl & htons(ACK) ) {
|
|
|
|
|
char *data;
|
|
|
|
|
int syn_ack, consumed;
|
|
|
|
|
|
|
|
|
|
if (state == FIN_WAIT_1 || state == FIN_WAIT_2) {
|
|
|
|
|
state = FIN_WAIT_2;
|
|
|
|
|
ctrl = ACK;
|
|
|
|
|
goto consume_data;
|
|
|
|
|
}
|
|
|
|
|
syn_ack = state == CLOSED || state == SYN_RCVD;
|
|
|
|
|
consumed = ntohl(tcp->ack) - send_seq - syn_ack;
|
|
|
|
|
if (consumed < 0 || consumed > can_send) {
|
2005-04-12 16:52:59 +00:00
|
|
|
|
tcp_reset((struct iphdr *)&nic->packet[ETH_HLEN]);
|
2005-03-08 18:53:11 +00:00
|
|
|
|
goto recv_data;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rtt = currticks() - last_sent;
|
|
|
|
|
srtt = !srtt ? rtt : (srtt*4 + rtt)/5;
|
|
|
|
|
rto = srtt + srtt/2;
|
|
|
|
|
if (rto < TCP_MIN_TIMEOUT)
|
|
|
|
|
rto = TCP_MIN_TIMEOUT;
|
|
|
|
|
else if (rto > TCP_MAX_TIMEOUT)
|
|
|
|
|
rto = TCP_MAX_TIMEOUT;
|
|
|
|
|
|
|
|
|
|
can_send -= consumed;
|
|
|
|
|
send_seq += consumed + syn_ack;
|
|
|
|
|
data = buf + sizeof(struct iphdr) + sizeof(struct tcphdr);
|
|
|
|
|
if (can_send) {
|
|
|
|
|
memmove(data, data + consumed, can_send);
|
|
|
|
|
}
|
|
|
|
|
if (!sent_all) {
|
|
|
|
|
int more_data;
|
|
|
|
|
data += can_send;
|
|
|
|
|
more_data = buf + sizeof(buf) - data;
|
|
|
|
|
if (more_data > 0) {
|
|
|
|
|
more_data = send(more_data, data, ptr);
|
|
|
|
|
can_send += more_data;
|
|
|
|
|
}
|
|
|
|
|
sent_all = !more_data;
|
|
|
|
|
}
|
|
|
|
|
if (state == SYN_RCVD) {
|
|
|
|
|
state = ESTABLISHED;
|
|
|
|
|
ctrl = PSH|ACK;
|
|
|
|
|
goto consume_data;
|
|
|
|
|
}
|
|
|
|
|
if (tcp->ctrl & htons(RST))
|
|
|
|
|
return (0);
|
|
|
|
|
} else if (tcp->ctrl & htons(RST)) {
|
|
|
|
|
if (state == CLOSED)
|
|
|
|
|
goto recv_data;
|
|
|
|
|
return (0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
consume_data:
|
2005-04-12 16:52:59 +00:00
|
|
|
|
ip = (struct iphdr *)&nic->packet[ETH_HLEN];
|
2005-03-08 18:53:11 +00:00
|
|
|
|
header_size = sizeof(struct iphdr) + ((ntohs(tcp->ctrl)>>10)&0x3C);
|
|
|
|
|
payload = ntohs(ip->len) - header_size;
|
|
|
|
|
if (payload > 0 && state == ESTABLISHED) {
|
|
|
|
|
int old_bytes = recv_seq - (long)ntohl(tcp->seq);
|
|
|
|
|
if (old_bytes >= 0 && payload - old_bytes > 0) {
|
|
|
|
|
recv_seq += payload - old_bytes;
|
|
|
|
|
if (state != FIN_WAIT_1 && state != FIN_WAIT_2 &&
|
|
|
|
|
!recv(payload - old_bytes,
|
2005-04-12 16:52:59 +00:00
|
|
|
|
&nic->packet[ETH_HLEN+header_size+old_bytes],
|
2005-03-08 18:53:11 +00:00
|
|
|
|
ptr)) {
|
|
|
|
|
goto close;
|
|
|
|
|
}
|
|
|
|
|
if ((state == ESTABLISHED || state == SYN_RCVD) &&
|
|
|
|
|
!(tcp->ctrl & htons(FIN))) {
|
|
|
|
|
int in_window = window - 2*TCP_MIN_WINDOW >
|
|
|
|
|
recv_seq - last_ack;
|
|
|
|
|
ctrl = can_send ? PSH|ACK : ACK;
|
|
|
|
|
if (!can_send && in_window) {
|
|
|
|
|
/* Window scaling is broken right now, just fall back to acknowledging every */
|
|
|
|
|
/* packet immediately and unconditionally. FIXME */ /***/
|
|
|
|
|
/* if (await_reply(await_tcp, srcsock,
|
|
|
|
|
&tcp, rto))
|
|
|
|
|
goto got_data;
|
|
|
|
|
else */
|
|
|
|
|
goto send_data;
|
|
|
|
|
}
|
|
|
|
|
if (!in_window) {
|
|
|
|
|
window += TCP_MIN_WINDOW;
|
|
|
|
|
if (window > TCP_MAX_WINDOW)
|
|
|
|
|
window = TCP_MAX_WINDOW;
|
|
|
|
|
}
|
|
|
|
|
goto send_data;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
/* saw old data again, must have lost packets */
|
|
|
|
|
window /= 2;
|
|
|
|
|
if (window < 2*TCP_MIN_WINDOW)
|
|
|
|
|
window = 2*TCP_MIN_WINDOW;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (tcp->ctrl & htons(FIN)) {
|
|
|
|
|
if (state == ESTABLISHED) {
|
|
|
|
|
ctrl = FIN|ACK;
|
|
|
|
|
} else if (state == FIN_WAIT_1 || state == FIN_WAIT_2) {
|
|
|
|
|
ctrl = ACK;
|
|
|
|
|
} else {
|
|
|
|
|
ctrl = RST;
|
|
|
|
|
}
|
|
|
|
|
return (tcp_transmit(destip, srcsock, destsock,
|
|
|
|
|
send_seq, recv_seq + 1, window, ctrl,
|
|
|
|
|
sizeof(struct iphdr) +
|
|
|
|
|
sizeof(struct tcphdr), buf) &&
|
|
|
|
|
(state == ESTABLISHED ||
|
|
|
|
|
state == FIN_WAIT_1 || state == FIN_WAIT_2) &&
|
|
|
|
|
!can_send);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (state == CLOSED) {
|
|
|
|
|
if (tcp->ctrl & htons(SYN)) {
|
|
|
|
|
recv_seq = ntohl(tcp->seq) + 1;
|
|
|
|
|
if (!(tcp->ctrl & htons(ACK))) {
|
|
|
|
|
state = SYN_RCVD;
|
|
|
|
|
ctrl = SYN|ACK|PSH;
|
|
|
|
|
goto send_data;
|
|
|
|
|
} else {
|
|
|
|
|
state = ESTABLISHED;
|
|
|
|
|
ctrl = PSH|ACK;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (can_send || payload) {
|
|
|
|
|
goto send_data;
|
|
|
|
|
}
|
|
|
|
|
goto recv_data;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/**************************************************************************
|
|
|
|
|
AWAIT_REPLY - Wait until we get a response for our request
|
|
|
|
|
************f**************************************************************/
|
|
|
|
|
int await_reply(reply_t reply, int ival, void *ptr, long timeout)
|
|
|
|
|
{
|
|
|
|
|
unsigned long time, now;
|
|
|
|
|
struct iphdr *ip;
|
|
|
|
|
unsigned iplen = 0;
|
|
|
|
|
struct udphdr *udp;
|
|
|
|
|
struct tcphdr *tcp;
|
|
|
|
|
unsigned short ptype;
|
|
|
|
|
int result;
|
|
|
|
|
|
|
|
|
|
time = timeout + currticks();
|
|
|
|
|
/* The timeout check is done below. The timeout is only checked if
|
|
|
|
|
* there is no packet in the Rx queue. This assumes that eth_poll()
|
|
|
|
|
* needs a negligible amount of time.
|
|
|
|
|
*/
|
|
|
|
|
for (;;) {
|
|
|
|
|
now = currticks();
|
|
|
|
|
send_eth_slow_reports(now);
|
|
|
|
|
send_igmp_reports(now);
|
|
|
|
|
result = eth_poll(1);
|
|
|
|
|
if (result == 0) {
|
|
|
|
|
/* We don't have anything */
|
|
|
|
|
|
|
|
|
|
/* Check for abort key only if the Rx queue is empty -
|
|
|
|
|
* as long as we have something to process, don't
|
|
|
|
|
* assume that something failed. It is unlikely that
|
|
|
|
|
* we have no processing time left between packets. */
|
|
|
|
|
poll_interruptions();
|
|
|
|
|
/* Do the timeout after at least a full queue walk. */
|
|
|
|
|
if ((timeout == 0) || (currticks() > time)) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* We have something! */
|
|
|
|
|
|
|
|
|
|
/* Find the Ethernet packet type */
|
2005-04-12 16:52:59 +00:00
|
|
|
|
if (nic->packetlen >= ETH_HLEN) {
|
|
|
|
|
ptype = ((unsigned short) nic->packet[12]) << 8
|
|
|
|
|
| ((unsigned short) nic->packet[13]);
|
2005-03-08 18:53:11 +00:00
|
|
|
|
} else continue; /* what else could we do with it? */
|
|
|
|
|
/* Verify an IP header */
|
|
|
|
|
ip = 0;
|
2005-04-12 16:52:59 +00:00
|
|
|
|
if ((ptype == ETH_P_IP) && (nic->packetlen >= ETH_HLEN + sizeof(struct iphdr))) {
|
2005-03-08 18:53:11 +00:00
|
|
|
|
unsigned ipoptlen;
|
2005-04-12 16:52:59 +00:00
|
|
|
|
ip = (struct iphdr *)&nic->packet[ETH_HLEN];
|
2005-03-08 18:53:11 +00:00
|
|
|
|
if ((ip->verhdrlen < 0x45) || (ip->verhdrlen > 0x4F))
|
|
|
|
|
continue;
|
|
|
|
|
iplen = (ip->verhdrlen & 0xf) * 4;
|
|
|
|
|
if (ipchksum(ip, iplen) != 0)
|
|
|
|
|
continue;
|
|
|
|
|
if (ip->frags & htons(0x3FFF)) {
|
|
|
|
|
static int warned_fragmentation = 0;
|
|
|
|
|
if (!warned_fragmentation) {
|
|
|
|
|
printf("ALERT: got a fragmented packet - reconfigure your server\n");
|
|
|
|
|
warned_fragmentation = 1;
|
|
|
|
|
}
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (ntohs(ip->len) > ETH_MAX_MTU)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
ipoptlen = iplen - sizeof(struct iphdr);
|
|
|
|
|
if (ipoptlen) {
|
|
|
|
|
/* Delete the ip options, to guarantee
|
|
|
|
|
* good alignment, and make etherboot simpler.
|
|
|
|
|
*/
|
2005-04-12 16:52:59 +00:00
|
|
|
|
memmove(&nic->packet[ETH_HLEN + sizeof(struct iphdr)],
|
|
|
|
|
&nic->packet[ETH_HLEN + iplen],
|
|
|
|
|
nic->packetlen - ipoptlen);
|
|
|
|
|
nic->packetlen -= ipoptlen;
|
2005-03-08 18:53:11 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
udp = 0;
|
|
|
|
|
if (ip && (ip->protocol == IP_UDP) &&
|
2005-04-12 16:52:59 +00:00
|
|
|
|
(nic->packetlen >=
|
2005-03-08 18:53:11 +00:00
|
|
|
|
ETH_HLEN + sizeof(struct iphdr) + sizeof(struct udphdr))) {
|
2005-04-12 16:52:59 +00:00
|
|
|
|
udp = (struct udphdr *)&nic->packet[ETH_HLEN + sizeof(struct iphdr)];
|
2005-03-08 18:53:11 +00:00
|
|
|
|
|
|
|
|
|
/* Make certain we have a reasonable packet length */
|
|
|
|
|
if (ntohs(udp->len) > (ntohs(ip->len) - iplen))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (udp->chksum && tcpudpchksum(ip)) {
|
|
|
|
|
printf("UDP checksum error\n");
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
tcp = 0;
|
|
|
|
|
#ifdef DOWNLOAD_PROTO_HTTP
|
|
|
|
|
if (ip && (ip->protocol == IP_TCP) &&
|
2005-04-12 16:52:59 +00:00
|
|
|
|
(nic->packetlen >=
|
2005-03-08 18:53:11 +00:00
|
|
|
|
ETH_HLEN + sizeof(struct iphdr) + sizeof(struct tcphdr))){
|
2005-04-12 16:52:59 +00:00
|
|
|
|
tcp = (struct tcphdr *)&nic->packet[ETH_HLEN +
|
2005-03-08 18:53:11 +00:00
|
|
|
|
sizeof(struct iphdr)];
|
|
|
|
|
/* Make certain we have a reasonable packet length */
|
|
|
|
|
if (((ntohs(tcp->ctrl) >> 10) & 0x3C) >
|
|
|
|
|
ntohs(ip->len) - (int)iplen)
|
|
|
|
|
continue;
|
|
|
|
|
if (tcpudpchksum(ip)) {
|
|
|
|
|
printf("TCP checksum error\n");
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
result = reply(ival, ptr, ptype, ip, udp, tcp);
|
|
|
|
|
if (result > 0) {
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If it isn't a packet the upper layer wants see if there is a default
|
|
|
|
|
* action. This allows us reply to arp, igmp, and lacp queries.
|
|
|
|
|
*/
|
|
|
|
|
if ((ptype == ETH_P_ARP) &&
|
2005-04-12 16:52:59 +00:00
|
|
|
|
(nic->packetlen >= ETH_HLEN + sizeof(struct arprequest))) {
|
2005-03-08 18:53:11 +00:00
|
|
|
|
struct arprequest *arpreply;
|
|
|
|
|
unsigned long tmp;
|
|
|
|
|
|
2005-04-12 16:52:59 +00:00
|
|
|
|
arpreply = (struct arprequest *)&nic->packet[ETH_HLEN];
|
2005-03-08 18:53:11 +00:00
|
|
|
|
memcpy(&tmp, arpreply->tipaddr, sizeof(in_addr));
|
|
|
|
|
if ((arpreply->opcode == htons(ARP_REQUEST)) &&
|
|
|
|
|
(tmp == arptable[ARP_CLIENT].ipaddr.s_addr)) {
|
|
|
|
|
arpreply->opcode = htons(ARP_REPLY);
|
|
|
|
|
memcpy(arpreply->tipaddr, arpreply->sipaddr, sizeof(in_addr));
|
|
|
|
|
memcpy(arpreply->thwaddr, arpreply->shwaddr, ETH_ALEN);
|
|
|
|
|
memcpy(arpreply->sipaddr, &arptable[ARP_CLIENT].ipaddr, sizeof(in_addr));
|
|
|
|
|
memcpy(arpreply->shwaddr, arptable[ARP_CLIENT].node, ETH_ALEN);
|
|
|
|
|
eth_transmit(arpreply->thwaddr, ETH_P_ARP,
|
|
|
|
|
sizeof(struct arprequest),
|
|
|
|
|
arpreply);
|
|
|
|
|
#ifdef MDEBUG
|
|
|
|
|
memcpy(&tmp, arpreply->tipaddr, sizeof(in_addr));
|
|
|
|
|
printf("Sent ARP reply to: %@\n",tmp);
|
|
|
|
|
#endif /* MDEBUG */
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
process_eth_slow(ptype, now);
|
|
|
|
|
process_igmp(ip, now);
|
|
|
|
|
}
|
|
|
|
|
return(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef REQUIRE_VCI_ETHERBOOT
|
|
|
|
|
/**************************************************************************
|
|
|
|
|
FIND_VCI_ETHERBOOT - Looks for "Etherboot" in Vendor Encapsulated Identifiers
|
|
|
|
|
On entry p points to byte count of VCI options
|
|
|
|
|
**************************************************************************/
|
|
|
|
|
static int find_vci_etherboot(unsigned char *p)
|
|
|
|
|
{
|
|
|
|
|
unsigned char *end = p + 1 + *p;
|
|
|
|
|
|
|
|
|
|
for (p++; p < end; ) {
|
|
|
|
|
if (*p == RFC2132_VENDOR_CLASS_ID) {
|
|
|
|
|
if (strncmp("Etherboot", p + 2, sizeof("Etherboot") - 1) == 0)
|
|
|
|
|
return (1);
|
|
|
|
|
} else if (*p == RFC1533_END)
|
|
|
|
|
return (0);
|
|
|
|
|
p += TAG_LEN(p) + 2;
|
|
|
|
|
}
|
|
|
|
|
return (0);
|
|
|
|
|
}
|
|
|
|
|
#endif /* REQUIRE_VCI_ETHERBOOT */
|
|
|
|
|
|
|
|
|
|
/**************************************************************************
|
|
|
|
|
DECODE_RFC1533 - Decodes RFC1533 header
|
|
|
|
|
**************************************************************************/
|
|
|
|
|
int decode_rfc1533(unsigned char *p, unsigned int block, unsigned int len, int eof)
|
|
|
|
|
{
|
|
|
|
|
static unsigned char *extdata = NULL, *extend = NULL;
|
|
|
|
|
unsigned char *extpath = NULL;
|
|
|
|
|
unsigned char *endp;
|
|
|
|
|
static unsigned char in_encapsulated_options = 0;
|
|
|
|
|
|
|
|
|
|
if (eof == -1) {
|
|
|
|
|
/* Encapsulated option block */
|
|
|
|
|
endp = p + len;
|
|
|
|
|
}
|
|
|
|
|
else if (block == 0) {
|
|
|
|
|
#ifdef REQUIRE_VCI_ETHERBOOT
|
|
|
|
|
vci_etherboot = 0;
|
|
|
|
|
#endif
|
|
|
|
|
end_of_rfc1533 = NULL;
|
|
|
|
|
#ifdef IMAGE_FREEBSD
|
|
|
|
|
/* yes this is a pain FreeBSD uses this for swap, however,
|
|
|
|
|
there are cases when you don't want swap and then
|
|
|
|
|
you want this set to get the extra features so lets
|
|
|
|
|
just set if dealing with FreeBSD. I haven't run into
|
|
|
|
|
any troubles with this but I have without it
|
|
|
|
|
*/
|
|
|
|
|
vendorext_isvalid = 1;
|
|
|
|
|
#ifdef FREEBSD_KERNEL_ENV
|
|
|
|
|
memcpy(freebsd_kernel_env, FREEBSD_KERNEL_ENV,
|
|
|
|
|
sizeof(FREEBSD_KERNEL_ENV));
|
|
|
|
|
/* FREEBSD_KERNEL_ENV had better be a string constant */
|
|
|
|
|
#else
|
|
|
|
|
freebsd_kernel_env[0]='\0';
|
|
|
|
|
#endif
|
|
|
|
|
#else
|
|
|
|
|
vendorext_isvalid = 0;
|
|
|
|
|
#endif
|
|
|
|
|
if (memcmp(p, rfc1533_cookie, 4))
|
|
|
|
|
return(0); /* no RFC 1533 header found */
|
|
|
|
|
p += 4;
|
|
|
|
|
endp = p + len;
|
|
|
|
|
} else {
|
|
|
|
|
if (block == 1) {
|
|
|
|
|
if (memcmp(p, rfc1533_cookie, 4))
|
|
|
|
|
return(0); /* no RFC 1533 header found */
|
|
|
|
|
p += 4;
|
|
|
|
|
len -= 4; }
|
|
|
|
|
if (extend + len <= (unsigned char *)&(BOOTP_DATA_ADDR->bootp_extension[MAX_BOOTP_EXTLEN])) {
|
|
|
|
|
memcpy(extend, p, len);
|
|
|
|
|
extend += len;
|
|
|
|
|
} else {
|
|
|
|
|
printf("Overflow in vendor data buffer! Aborting...\n");
|
|
|
|
|
*extdata = RFC1533_END;
|
|
|
|
|
return(0);
|
|
|
|
|
}
|
|
|
|
|
p = extdata; endp = extend;
|
|
|
|
|
}
|
|
|
|
|
if (!eof)
|
|
|
|
|
return 1;
|
|
|
|
|
while (p < endp) {
|
|
|
|
|
unsigned char c = *p;
|
|
|
|
|
if (c == RFC1533_PAD) {
|
|
|
|
|
p++;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
else if (c == RFC1533_END) {
|
|
|
|
|
end_of_rfc1533 = endp = p;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
else if (NON_ENCAP_OPT c == RFC1533_NETMASK)
|
|
|
|
|
memcpy(&netmask, p+2, sizeof(in_addr));
|
|
|
|
|
else if (NON_ENCAP_OPT c == RFC1533_GATEWAY) {
|
|
|
|
|
/* This is a little simplistic, but it will
|
|
|
|
|
usually be sufficient.
|
|
|
|
|
Take only the first entry */
|
|
|
|
|
if (TAG_LEN(p) >= sizeof(in_addr))
|
|
|
|
|
memcpy(&arptable[ARP_GATEWAY].ipaddr, p+2, sizeof(in_addr));
|
|
|
|
|
}
|
|
|
|
|
else if (c == RFC1533_EXTENSIONPATH)
|
|
|
|
|
extpath = p;
|
|
|
|
|
#ifndef NO_DHCP_SUPPORT
|
|
|
|
|
#ifdef REQUIRE_VCI_ETHERBOOT
|
|
|
|
|
else if (NON_ENCAP_OPT c == RFC1533_VENDOR) {
|
|
|
|
|
vci_etherboot = find_vci_etherboot(p+1);
|
|
|
|
|
#ifdef MDEBUG
|
|
|
|
|
printf("vci_etherboot %d\n", vci_etherboot);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
#endif /* REQUIRE_VCI_ETHERBOOT */
|
|
|
|
|
else if (NON_ENCAP_OPT c == RFC2132_MSG_TYPE)
|
|
|
|
|
dhcp_reply=*(p+2);
|
|
|
|
|
else if (NON_ENCAP_OPT c == RFC2132_SRV_ID)
|
|
|
|
|
memcpy(&dhcp_server, p+2, sizeof(in_addr));
|
|
|
|
|
#endif /* NO_DHCP_SUPPORT */
|
|
|
|
|
else if (NON_ENCAP_OPT c == RFC1533_HOSTNAME) {
|
|
|
|
|
hostname = p + 2;
|
|
|
|
|
hostnamelen = *(p + 1);
|
|
|
|
|
}
|
|
|
|
|
else if (ENCAP_OPT c == RFC1533_VENDOR_MAGIC
|
|
|
|
|
&& TAG_LEN(p) >= 6 &&
|
|
|
|
|
!memcmp(p+2,vendorext_magic,4) &&
|
|
|
|
|
p[6] == RFC1533_VENDOR_MAJOR
|
|
|
|
|
)
|
|
|
|
|
vendorext_isvalid++;
|
|
|
|
|
else if (NON_ENCAP_OPT c == RFC1533_VENDOR_ETHERBOOT_ENCAP) {
|
|
|
|
|
in_encapsulated_options = 1;
|
|
|
|
|
decode_rfc1533(p+2, 0, TAG_LEN(p), -1);
|
|
|
|
|
in_encapsulated_options = 0;
|
|
|
|
|
}
|
|
|
|
|
#ifdef IMAGE_FREEBSD
|
|
|
|
|
else if (NON_ENCAP_OPT c == RFC1533_VENDOR_HOWTO)
|
|
|
|
|
freebsd_howto = ((p[2]*256+p[3])*256+p[4])*256+p[5];
|
|
|
|
|
else if (NON_ENCAP_OPT c == RFC1533_VENDOR_KERNEL_ENV){
|
|
|
|
|
if(*(p + 1) < sizeof(freebsd_kernel_env)){
|
|
|
|
|
memcpy(freebsd_kernel_env,p+2,*(p+1));
|
|
|
|
|
}else{
|
|
|
|
|
printf("Only support %ld bytes in Kernel Env\n",
|
|
|
|
|
sizeof(freebsd_kernel_env));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
#ifdef DNS_RESOLVER
|
|
|
|
|
else if (NON_ENCAP_OPT c == RFC1533_DNS) {
|
|
|
|
|
// TODO: Copy the DNS IP somewhere reasonable
|
|
|
|
|
if (TAG_LEN(p) >= sizeof(in_addr))
|
|
|
|
|
memcpy(&arptable[ARP_NAMESERVER].ipaddr, p+2, sizeof(in_addr));
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
else {
|
|
|
|
|
#if 0
|
|
|
|
|
unsigned char *q;
|
|
|
|
|
printf("Unknown RFC1533-tag ");
|
|
|
|
|
for(q=p;q<p+2+TAG_LEN(p);q++)
|
|
|
|
|
printf("%hhX ",*q);
|
|
|
|
|
putchar('\n');
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
p += TAG_LEN(p) + 2;
|
|
|
|
|
}
|
|
|
|
|
extdata = extend = endp;
|
|
|
|
|
if (block <= 0 && extpath != NULL) {
|
|
|
|
|
char fname[64];
|
|
|
|
|
memcpy(fname, extpath+2, TAG_LEN(extpath));
|
|
|
|
|
fname[(int)TAG_LEN(extpath)] = '\0';
|
|
|
|
|
printf("Loading BOOTP-extension file: %s\n",fname);
|
|
|
|
|
tftp(fname, decode_rfc1533);
|
|
|
|
|
}
|
|
|
|
|
return 1; /* proceed with next block */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* FIXME double check TWO_SECOND_DIVISOR */
|
|
|
|
|
#define TWO_SECOND_DIVISOR (RAND_MAX/TICKS_PER_SEC)
|
|
|
|
|
/**************************************************************************
|
|
|
|
|
RFC2131_SLEEP_INTERVAL - sleep for expotentially longer times (base << exp) +- 1 sec)
|
|
|
|
|
**************************************************************************/
|
|
|
|
|
long rfc2131_sleep_interval(long base, int exp)
|
|
|
|
|
{
|
|
|
|
|
unsigned long tmo;
|
|
|
|
|
#ifdef BACKOFF_LIMIT
|
|
|
|
|
if (exp > BACKOFF_LIMIT)
|
|
|
|
|
exp = BACKOFF_LIMIT;
|
|
|
|
|
#endif
|
|
|
|
|
tmo = (base << exp) + (TICKS_PER_SEC - (random()/TWO_SECOND_DIVISOR));
|
|
|
|
|
return tmo;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef MULTICAST_LEVEL2
|
|
|
|
|
/**************************************************************************
|
|
|
|
|
RFC1112_SLEEP_INTERVAL - sleep for expotentially longer times, up to (base << exp)
|
|
|
|
|
**************************************************************************/
|
|
|
|
|
long rfc1112_sleep_interval(long base, int exp)
|
|
|
|
|
{
|
|
|
|
|
unsigned long divisor, tmo;
|
|
|
|
|
#ifdef BACKOFF_LIMIT
|
|
|
|
|
if (exp > BACKOFF_LIMIT)
|
|
|
|
|
exp = BACKOFF_LIMIT;
|
|
|
|
|
#endif
|
|
|
|
|
divisor = RAND_MAX/(base << exp);
|
|
|
|
|
tmo = random()/divisor;
|
|
|
|
|
return tmo;
|
|
|
|
|
}
|
|
|
|
|
#endif /* MULTICAST_LEVEL_2 */
|