| #include "netboot.h" |
| #include "netboot_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 */ |
| } |