blob: fc27caf055fc4e60da001cea8bf8dce750dbeb68 [file] [log] [blame] [raw]
#include "netboot.h"
#include "config.h"
#include "nic.h"
#include "ip.h"
struct arptable_t arptable[MAX_ARP];
int _pending_key;
static unsigned long netmask;
static char rfc1533_cookie[5] = { RFC1533_COOKIE, RFC1533_END };
static char broadcast[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
static char *kernel;
static char kernel_buf[128];
static struct bootpd_t bootp_data;
static int vendorext_isvalid;
static unsigned char vendorext_magic[] = {0xE4,0x45,0x74,0x68}; /* äEth */
static unsigned char *end_of_rfc1533 = NULL;
int ip_abort = 0;
static int bootp(void);
static void default_netmask(void);
static void convert_ipaddr(register char *d, register char *s);
static int decode_rfc1533(register unsigned char *p, int block, int len,
int eof);
int ip_init(void)
{
static int bootp_completed = 0,
probe_completed = 0;
if (!probe_completed && !eth_probe())
{
printf("No ethernet adapter found\n");
return 0;
}
probe_completed = 1;
#if 1
if ((!bootp_completed ||
!arptable[ARP_CLIENT].ipaddr || !arptable[ARP_SERVER].ipaddr))
{
#endif
if (!bootp())
{
printf("No BOOTP server found\n");
return 0;
}
printf("My IP 0x%x, Server IP 0x%x, GW IP 0x%x\n",
arptable[ARP_CLIENT].ipaddr,
arptable[ARP_SERVER].ipaddr,
arptable[ARP_GATEWAY].ipaddr);
#ifdef CONFIG_FILE_EXT
{
char *ext = &config_file;
/* seek to end of config file name */
while(*ext)
ext++;
sprintf(ext, ".%d", arptable[ARP_CLIENT].ipaddr & 0xff);
}
#endif
#if 1
}
bootp_completed = 1;
#endif
return 1;
}
/**************************************************************************
UDP_TRANSMIT - Send a UDP datagram
**************************************************************************/
int udp_transmit(destip, srcsock, destsock, len, buf)
unsigned long destip;
unsigned int srcsock, destsock;
int len;
char *buf;
{
struct iphdr *ip;
struct udphdr *udp;
struct arprequest arpreq;
int arpentry, i;
int retry;
ip = (struct iphdr *)buf;
udp = (struct udphdr *)(buf + sizeof(struct iphdr));
ip->verhdrlen = 0x45;
ip->service = 0;
ip->len = htons(len);
ip->ident = 0;
ip->frags = 0;
ip->ttl = 60;
ip->protocol = IP_UDP;
ip->chksum = 0;
convert_ipaddr(ip->src, (char *)&arptable[ARP_CLIENT].ipaddr);
convert_ipaddr(ip->dest, (char *)&destip);
ip->chksum = ipchksum((unsigned short *)buf, sizeof(struct iphdr));
udp->src = htons(srcsock);
udp->dest = htons(destsock);
udp->len = htons(len - sizeof(struct iphdr));
udp->chksum = 0;
if (destip == IP_BROADCAST) {
eth_transmit(broadcast, IP, len, buf);
} else {
long h_netmask = ntohl(netmask);
/* Check to see if we need gateway */
if (((destip & h_netmask) !=
(arptable[ARP_CLIENT].ipaddr & h_netmask)) &&
arptable[ARP_GATEWAY].ipaddr)
destip = arptable[ARP_GATEWAY].ipaddr;
for(arpentry = 0; arpentry<MAX_ARP; arpentry++)
if (arptable[arpentry].ipaddr == destip) break;
if (arpentry == MAX_ARP) {
printf("%I is not in my arp table!\r\n", destip);
return(0);
}
for (i = 0; i<ETHER_ADDR_SIZE; i++)
if (arptable[arpentry].node[i]) break;
if (i == ETHER_ADDR_SIZE) { /* Need to do arp request */
arpreq.hwtype = htons(1);
arpreq.protocol = htons(IP);
arpreq.hwlen = ETHER_ADDR_SIZE;
arpreq.protolen = 4;
arpreq.opcode = htons(ARP_REQUEST);
bcopy(arptable[ARP_CLIENT].node, arpreq.shwaddr,
ETHER_ADDR_SIZE);
convert_ipaddr(arpreq.sipaddr,
(char *)&arptable[ARP_CLIENT].ipaddr);
bzero(arpreq.thwaddr, ETHER_ADDR_SIZE);
convert_ipaddr(arpreq.tipaddr, (char *)&destip);
for (retry = 0; retry < MAX_ARP_RETRIES;
rfc951_sleep(++retry)) {
eth_transmit(broadcast, ARP, sizeof(arpreq),
(char *)&arpreq);
if (await_reply(AWAIT_ARP, arpentry,
arpreq.tipaddr)) goto xmit;
if (ip_abort)
return 0;
}
return(0);
}
xmit: eth_transmit(arptable[arpentry].node, IP, len, buf);
}
return(1);
}
/**************************************************************************
AWAIT_REPLY - Wait until we get a response for our request
**************************************************************************/
int await_reply(type, ival, ptr)
int type, ival;
char *ptr;
{
unsigned long time;
struct iphdr *ip;
struct udphdr *udp;
struct arprequest *arpreply;
struct bootp_t *bootpreply;
struct rpc_t *rpc;
int protohdrlen = ETHER_HDR_SIZE + sizeof(struct iphdr) +
sizeof(struct udphdr);
ip_abort = 0;
time = currticks() + TIMEOUT;
while(time > currticks()) {
#if 0
if (iskey() && (getchar() == ESC)) longjmp(jmp_bootmenu,1);
#else
if (iskey() && (getchar() == ESC))
{
ip_abort = 1;
return 0;
}
#endif
if (eth_poll())
{ /* We have something! */
/* Check for ARP - No IP hdr */
if ((nic.packetlen >= ETHER_HDR_SIZE +
sizeof(struct arprequest)) &&
(((nic.packet[12] << 8) | nic.packet[13]) == ARP))
{
unsigned long target_ip;
arpreply = (struct arprequest *)
&nic.packet[ETHER_HDR_SIZE];
if (arpreply->opcode == ntohs(ARP_REQUEST)
&& (convert_ipaddr(&target_ip,
&arpreply->tipaddr),
arptable[ARP_CLIENT].ipaddr == target_ip))
{
/* request for our ether address -- send a reply */
unsigned long my_ip_net
= *(unsigned long*)(arpreply->tipaddr);
bcopy(arpreply->shwaddr, arpreply->thwaddr, 10);
bcopy(arptable[ARP_CLIENT].node,
arpreply->shwaddr, ETHER_ADDR_SIZE);
*(unsigned long*)(arpreply->sipaddr) = my_ip_net;
arpreply->opcode = htons(ARP_REPLY);
eth_transmit(arpreply->thwaddr, ARP,
sizeof(struct arprequest),
(char *) arpreply);
continue;
}
else if (type == AWAIT_ARP &&
(arpreply->opcode == ntohs(ARP_REPLY)) &&
!bcmp(arpreply->sipaddr, ptr, 4)) {
bcopy(arpreply->shwaddr,
arptable[ival].node,
ETHER_ADDR_SIZE);
return(1);
}
continue;
}
/* Anything else has IP header */
if ((nic.packetlen < protohdrlen) ||
(((nic.packet[12] << 8) | nic.packet[13]) != IP)) continue;
ip = (struct iphdr *)&nic.packet[ETHER_HDR_SIZE];
if ((ip->verhdrlen != 0x45) ||
ipchksum((unsigned short *)ip, sizeof(struct iphdr)) ||
(ip->protocol != IP_UDP)) continue;
udp = (struct udphdr *)&nic.packet[ETHER_HDR_SIZE +
sizeof(struct iphdr)];
/* BOOTP ? */
bootpreply = (struct bootp_t *)&nic.packet[ETHER_HDR_SIZE];
if ((type == AWAIT_BOOTP) &&
(nic.packetlen >= (ETHER_HDR_SIZE +
sizeof(struct bootp_t))) &&
(ntohs(udp->dest) == BOOTP_CLIENT) &&
(bootpreply->bp_op == BOOTP_REPLY)) {
convert_ipaddr((char *)&arptable[ARP_CLIENT].ipaddr,
bootpreply->bp_yiaddr);
default_netmask();
convert_ipaddr((char *)&arptable[ARP_SERVER].ipaddr,
bootpreply->bp_siaddr);
bzero(arptable[ARP_SERVER].node,
ETHER_ADDR_SIZE); /* Kill arp */
convert_ipaddr((char *)&arptable[ARP_GATEWAY].ipaddr,
bootpreply->bp_giaddr);
bzero(arptable[ARP_GATEWAY].node,
ETHER_ADDR_SIZE); /* Kill arp */
if (bootpreply->bp_file[0]) {
bcopy(bootpreply->bp_file,
kernel_buf, 128);
kernel = kernel_buf;
}
bcopy((char *)bootpreply,
(char *)&bootp_data,
sizeof(struct bootp_t));
decode_rfc1533(bootp_data.bootp_reply.bp_vend,
0, BOOTP_VENDOR_LEN, 1);
return(1);
}
/* TFTP ? */
if ((type == AWAIT_TFTP) &&
(ntohs(udp->dest) == ival)) return(1);
/* RPC */
rpc = (struct rpc_t *)&nic.packet[ETHER_HDR_SIZE];
#ifdef NFS_BOOT
if ((type == AWAIT_RPC) &&
(ntohs(udp->dest) == RPC_SOCKET) &&
(ntohl(rpc->u.reply.id) == ival) &&
(ntohl(rpc->u.reply.type) == MSG_REPLY)) {
rpc_id++;
return(1);
}
#endif /* NFS_BOOT */
}
}
return(0);
}
/**************************************************************************
IPCHKSUM - Checksum IP Header
**************************************************************************/
unsigned short ipchksum(ip, len)
register unsigned short *ip;
register int len;
{
unsigned long sum = 0;
len >>= 1;
while (len--) {
sum += *(ip++);
if (sum > 0xFFFF)
sum -= 0xFFFF;
}
return((~sum) & 0x0000FFFF);
}
/**************************************************************************
TFTP - Download extended BOOTP data, configuration file or kernel image
**************************************************************************/
int tftp(name,fnc)
char *name;
int (*fnc)(unsigned char *, int, int, int);
{
int retry = 0;
static unsigned short isocket = 2000;
unsigned short osocket;
unsigned short len, block = 0, prevblock = 0;
struct tftp_t *tr;
struct tftp_t tp;
int rc;
int packetsize = TFTP_DEFAULTSIZE_PACKET;
tp.opcode = htons(TFTP_RRQ);
len = 30 + sprintf((char *)tp.u.rrq, "%s%coctet%cblksize%c%d",
name, 0, 0, 0, TFTP_MAX_PACKET) + 1;
if (!udp_transmit(arptable[ARP_SERVER].ipaddr, ++isocket, TFTP,
len, (char *)&tp))
return (0);
for (;;)
{
if (!await_reply(AWAIT_TFTP, isocket, NULL))
{
if (ip_abort)
return 0;
if (prevblock == 0 && retry++ < MAX_TFTP_RETRIES)
{ /* maybe initial request was lost */
rfc951_sleep(retry);
if (!udp_transmit(arptable[ARP_SERVER].ipaddr,
++isocket, TFTP, len, (char *)&tp))
return (0);
continue;
}
break; /* timeout on other blocks */
}
tr = (struct tftp_t *)&nic.packet[ETHER_HDR_SIZE];
if (tr->opcode == ntohs(TFTP_ERROR))
{
printf("TFTP error %d (%s)\r\n",
ntohs(tr->u.err.errcode),
tr->u.err.errmsg);
break;
}
if (tr->opcode == ntohs(TFTP_OACK)) {
#if 0
char *p = tr->u.oack.data, *e;
retry = MAX_TFTP_RETRIES;/*no more retries! */
if (prevblock) /* shouldn't happen */
continue; /* ignore it */
len = ntohs(tr->udp.len) - sizeof(struct udphdr) - 2;
if (len > TFTP_MAX_PACKET)
goto noak;
e = p + len;
while (*p != '\000' && p < e) {
if (!strcasecmp("blksize", p)) {
p += 8;
if ((packetsize = getdec(&p)) <
TFTP_DEFAULTSIZE_PACKET)
goto noak;
while (p < e && *p) p++;
if (p < e)
p++;
}
else {
noak:
tp.opcode = htons(TFTP_ERROR);
tp.u.err.errcode = 8;
len = 30 + sprintf((char *)tp.u.err.errmsg,
"RFC1782 error")+ 1;
udp_transmit(arptable[ARP_SERVER].
ipaddr, isocket,
ntohs(tr->udp.src),
len, (char *)&tp);
return (0);
}
}
if (p > e)
goto noak;
block = tp.u.ack.block = 0; /* this ensures, that */
/* the packet does not get */
/* processed as data! */
#else
continue;
#endif
}
else if (tr->opcode == ntohs(TFTP_DATA)) {
retry = MAX_TFTP_RETRIES;/*no more retries! */
len = ntohs(tr->udp.len) - sizeof(struct udphdr) - 4;
if (len > packetsize) /* shouldn't happen */
continue; /* ignore it */
block = ntohs(tp.u.ack.block = tr->u.data.block); }
else /* neither TFTP_OACK nor TFTP_DATA */
break;
tp.opcode = htons(TFTP_ACK);
osocket = ntohs(tr->udp.src);
udp_transmit(arptable[ARP_SERVER].ipaddr, isocket,
osocket, TFTP_MIN_PACKET, (char *)&tp); /* ack */
if (block <= prevblock) /* retransmission or OACK */
continue; /* don't process */
prevblock = block;
if ((rc = fnc(tr->u.data.download,
block, len, len < packetsize)) >= 0)
return(rc);
if (len < packetsize) /* End of data */
return (1);
}
return (0);
}
/**************************************************************************
CONVERT_IPADDR - Convert IP address from net to machine order
**************************************************************************/
static void convert_ipaddr(d, s)
register char *d,*s;
{
d[3] = s[0];
d[2] = s[1];
d[1] = s[2];
d[0] = s[3];
}
/**************************************************************************
RFC951_SLEEP - sleep for expotentially longer times
**************************************************************************/
void rfc951_sleep(exp)
int exp;
{
static long seed = 0;
long q,z,tmo;
if (!seed) /* Initialize linear congruential generator */
seed = currticks() + *(long *)&arptable[ARP_CLIENT].node
+ ((short *)arptable[ARP_CLIENT].node)[2];
/* simplified version of the LCG given in Bruce Scheier's
"Applied Cryptography" */
q = seed/53668;
if ((seed = 40014*(seed-53668*q) - 12211*q) < 0) seed += 2147483563l;
/* compute mask */
for (tmo = 63; tmo <= 60*18 && --exp > 0; tmo = 2*tmo+1);
/* sleep */
printf("<sleep>\r\n");
for (tmo = (tmo&seed)+currticks(); currticks() < tmo; )
/* if (iskey() && (getchar() == ESC)) longjmp(jmp_bootmenu,1) */ ;
eth_reset();
return;
}
/**************************************************************************
TWIDDLE
**************************************************************************/
void twiddle()
{
static unsigned long lastticks = 0;
static int count=0;
static char tiddles[]="-\\|/";
unsigned long ticks;
if ((ticks = currticks()) < lastticks)
return;
lastticks = ticks+1;
putchar(tiddles[(count++)&3]);
putchar('\b');
}
/**************************************************************************
BOOTP - Get my IP address and load information
**************************************************************************/
static int bootp()
{
int retry;
struct bootp_t bp;
unsigned long starttime;
#ifdef T509HACK
int flag;
flag = 1;
#endif
bzero(&bp, sizeof(struct bootp_t));
bp.bp_op = BOOTP_REQUEST;
bp.bp_htype = 1;
bp.bp_hlen = ETHER_ADDR_SIZE;
bp.bp_xid = starttime = currticks();
bcopy(arptable[ARP_CLIENT].node, bp.bp_hwaddr, ETHER_ADDR_SIZE);
bcopy(rfc1533_cookie, bp.bp_vend, 5); /* request RFC-style options */
for (retry = 0; retry < MAX_BOOTP_RETRIES; ) {
udp_transmit(IP_BROADCAST, 0, BOOTP_SERVER,
sizeof(struct bootp_t), (char *)&bp);
#ifdef T509HACK
if (flag) {
flag--;
} else {
if (await_reply(AWAIT_BOOTP, 0, NULL))
return(1);
if (ip_abort) /* hohmuth */
return 0;
rfc951_sleep(++retry);
}
#else
if (await_reply(AWAIT_BOOTP, 0, NULL))
return(1);
if (ip_abort) /* hohmuth */
return 0;
rfc951_sleep(++retry);
#endif
bp.bp_secs = htons((currticks()-starttime)/20);
}
return(0);
}
/**************************************************************************
DEFAULT_NETMASK - Set a default netmask for IP address
**************************************************************************/
static void default_netmask()
{
int net = arptable[ARP_CLIENT].ipaddr >> 24;
if (net <= 127)
netmask = htonl(0xff000000);
else if (net < 192)
netmask = htonl(0xffff0000);
else
netmask = htonl(0xffffff00);
}
/**************************************************************************
DECODE_RFC1533 - Decodes RFC1533 header
**************************************************************************/
static int decode_rfc1533(p, block, len, eof)
register unsigned char *p;
int block, len, eof;
{
static unsigned char *extdata = NULL, *extend = NULL;
unsigned char *extpath = NULL;
unsigned char *end, *q;
if (block == 0) {
#ifdef IMAGE_MENU
bzero(imagelist, sizeof(imagelist));
menudefault = useimagemenu = 0;
menutmo = -1;
#endif
#ifdef MOTD
bzero(motd, sizeof(motd));
#endif
end_of_rfc1533 = NULL;
vendorext_isvalid = 0;
if (bcmp(p, rfc1533_cookie, 4))
return(0); /* no RFC 1533 header found */
p += 4;
end = p + len; }
else {
if (block == 1) {
if (bcmp(p, rfc1533_cookie, 4))
return(0); /* no RFC 1533 header found */
p += 4;
len -= 4; }
if (extend + len <= (unsigned char *)(&bootp_data+1)) {
bcopy(p, extend, len);
extend += len;
} else {
printf("Overflow in vendor data buffer! Aborting...\r\n");
*extdata = RFC1533_END;
return(0);
}
p = extdata; end = extend;
}
if (eof) {
while(p < end) {
unsigned char c = *p;
if (c == RFC1533_PAD) {p++; continue;}
else if (c == RFC1533_END) {
end_of_rfc1533 = end = p; continue; }
else if (c == RFC1533_NETMASK) {bcopy(p+2,&netmask,4);}
#ifdef NFS_BOOT
else if (c == RFC1533_HOSTNAME) {
if (TAG_LEN(p) < (MNAMELEN&~3)-1) {
bcopy(p+2,&nfsdiskless.my_hostnam,TAG_LEN(p));
nfsdiskless.my_hostnam[TAG_LEN(p)] = '\000';
hostnamelen = (TAG_LEN(p)+3)&~3;
}
}
#endif
else if (c == RFC1533_GATEWAY) {
/* This is a little simplistic, but it will
usually be sufficient; look into the gate-
way list, only if there has been no BOOTP
gateway. Take only the first entry */
if (TAG_LEN(p) >= 4 &&
arptable[ARP_GATEWAY].ipaddr == 0)
convert_ipaddr((char *)&arptable[ARP_GATEWAY].ipaddr,
p+2);
}
else if (c == RFC1533_EXTENSIONPATH)
extpath = p;
else if (c == RFC1533_VENDOR_MAGIC &&
TAG_LEN(p) >= 6 &&
!bcmp(p+2,vendorext_magic,4) &&
p[6] == RFC1533_VENDOR_MAJOR)
vendorext_isvalid++;
#ifdef IMAGE_MENU
else if (c == RFC1533_VENDOR_MNUOPTS) {
parse_menuopts(p+2, TAG_LEN(p));
}
else if (c >= RFC1533_VENDOR_IMG &&
c<RFC1533_VENDOR_IMG+RFC1533_VENDOR_NUMOFIMG){
imagelist[c - RFC1533_VENDOR_IMG] = p;
useimagemenu++;
}
#endif
#ifdef MOTD
else if (c >= RFC1533_VENDOR_MOTD &&
c < RFC1533_VENDOR_MOTD +
RFC1533_VENDOR_NUMOFMOTD)
motd[c - RFC1533_VENDOR_MOTD] = p;
#endif
else {
/* printf("Unknown RFC1533-tag ");
for(q=p;q<p+2+TAG_LEN(p);q++)
printf("%x ",*q);
printf("\n\r"); */ }
p += TAG_LEN(p) + 2;
}
extdata = extend = end;
if (block == 0 && extpath != NULL) {
char fname[64];
bcopy(extpath+2,fname,TAG_LEN(extpath));
fname[(int)TAG_LEN(extpath)] = '\000';
printf("Loading BOOTP-extension file: %s\r\n",fname);
tftp(fname,decode_rfc1533);
}
}
return(-1); /* proceed with next block */
}