*** command.c.ORIG Sun Mar 13 04:37:18 1994 --- command.c Sat Mar 26 23:00:52 1994 *************** *** 819,826 **** --- 819,1119 ---- return(0); } + #include + #include + #define int32 unsigned long + #define int16 unsigned short + struct bootp { + char op; /* packet opcode type */ + char htype; /* hardware addr type */ + char hlen; /* hardware addr length */ + char hops; /* gateway hops */ + int32 xid; /* transaction ID */ + int16 secs; /* seconds since boot began */ + int16 unused; + int32 ciaddr; /* client IP address */ + int32 yiaddr; /* 'your' IP address */ + int32 siaddr; /* server IP address */ + int32 giaddr; /* gateway IP address */ + char chaddr[16]; /* client hardware address */ + char sname[64]; /* server host name */ + char file[128]; /* boot file name */ + char vend[64]; /* vendor-specific area */ + }; + #define BOOTREQUEST 1 + #define BOOTREPLY 2 + #define IPPORT_BOOTPS 67 + #define IPPORT_BOOTPC 68 + + /* SLIP protocol characters. */ + #define END 0300 /* indicates end of frame */ + #define ESC 0333 /* indicates byte stuffing */ + #define ESC_END 0334 /* ESC ESC_END means END 'data' */ + #define ESC_ESC 0335 /* ESC ESC_ESC means ESC 'data' */ + + static unsigned char packet_buff[512]; + static unsigned char *bufpt; + static unsigned char *bufend; + + static void put_buf(unsigned char c) + { + switch(c) { + case END: + *bufpt++ = ESC; + *bufpt++ = ESC_END; + break; + case ESC: + *bufpt++ = ESC; + *bufpt++ = ESC_ESC; + break; + default: + *bufpt++ = c; + } + } + + /* This is a version of ip_compute_csum() optimized for IP headers, which + always checksum on 4 octet boundaries. */ + static inline unsigned short + ip_fast_csum(unsigned char * buff, int wlen) + { + unsigned long sum = 0; + + if (wlen) { + unsigned long bogus; + __asm__("clc\n" + "1:\t" + "lodsl\n\t" + "adcl %3, %0\n\t" + "decl %2\n\t" + "jne 1b\n\t" + "adcl $0, %0\n\t" + "movl %0, %3\n\t" + "shrl $16, %3\n\t" + "addw %w3, %w0\n\t" + "adcw $0, %w0" + : "=r" (sum), "=S" (buff), "=r" (wlen), "=a" (bogus) + : "0" (sum), "1" (buff), "2" (wlen)); + } + return (~sum) & 0xffff; + } + + static unsigned short + udp_check(struct udphdr *uh, int len, + unsigned long saddr, unsigned long daddr) + { + unsigned long sum; + + __asm__("\t addl %%ecx,%%ebx\n" + "\t adcl %%edx,%%ebx\n" + "\t adcl $0, %%ebx\n" + : "=b"(sum) + : "0"(daddr), "c"(saddr), "d"((ntohs(len) << 16) + IPPROTO_UDP*256) + : "cx","bx","dx" ); + + if (len > 3) { + __asm__("\tclc\n" + "1:\n" + "\t lodsl\n" + "\t adcl %%eax, %%ebx\n" + "\t loop 1b\n" + "\t adcl $0, %%ebx\n" + : "=b"(sum) , "=S"(uh) + : "0"(sum), "c"(len/4) ,"1"(uh) + : "ax", "cx", "bx", "si" ); + } + + /* Convert from 32 bits to 16 bits. */ + __asm__("\t movl %%ebx, %%ecx\n" + "\t shrl $16,%%ecx\n" + "\t addw %%cx, %%bx\n" + "\t adcw $0, %%bx\n" + : "=b"(sum) + : "0"(sum) + : "bx", "cx"); + + /* Check for an extra word. */ + if ((len & 2) != 0) { + __asm__("\t lodsw\n" + "\t addw %%ax,%%bx\n" + "\t adcw $0, %%bx\n" + : "=b"(sum), "=S"(uh) + : "0"(sum) ,"1"(uh) + : "si", "ax", "bx"); + } + + /* Now check for the extra byte. */ + if ((len & 1) != 0) { + __asm__("\t lodsb\n" + "\t movb $0,%%ah\n" + "\t addw %%ax,%%bx\n" + "\t adcw $0, %%bx\n" + : "=b"(sum) + : "0"(sum) ,"S"(uh) + : "si", "ax", "bx"); + } + + /* We only want the bottom 16 bits, but we never cleared the top 16. */ + return((~sum) & 0xffff); + } + + static void put_bootreq(void) + { + struct iphdr ip; + struct udphdr udp; + struct bootp bootp; + unsigned char *cp; + int i; + + bufpt = packet_buff; + *bufpt++ = END; + + /* IP header */ + ip.ihl = 5; + ip.version = 4; + ip.tos = 0; + ip.tot_len = htons(sizeof(struct iphdr) + sizeof(struct udphdr) + + sizeof(struct bootp)); + ip.id = 1; + ip.frag_off = 0; + ip.ttl = 32; + ip.protocol = 17; + ip.check = 0; + ip.saddr = 0; + ip.daddr = INADDR_BROADCAST; + ip.check = ip_fast_csum((unsigned char *)&ip, 5); + for (cp = (unsigned char *)&ip, i = sizeof(struct iphdr); i > 0; i--) + put_buf(*cp++); + + /* UDP header */ + udp.source = htons(IPPORT_BOOTPC); + udp.dest = htons(IPPORT_BOOTPS); + udp.len = htons(sizeof(struct udphdr) + sizeof(struct bootp)); + udp.check = 0; + for (cp = (unsigned char *)&udp, i = sizeof(struct udphdr); i > 0; i--) + put_buf(*cp++); + + /* bootp request */ + memset(&bootp, 0, sizeof(bootp)); + bootp.op = BOOTREQUEST; + bootp.htype = 1; + bootp.hlen = 6; + for (cp = (unsigned char *)&bootp, i = sizeof(struct bootp); i > 0; i--) + put_buf(*cp++); + + *bufpt++ = END; + write(tty_askfd(), packet_buff, bufpt - packet_buff); + } + + static void get_buf(void) + { + unsigned char c; + struct iphdr *ip; + struct udphdr *udp; + struct bootp *bootp; + + bufend = bufpt + sizeof(packet_buff); + + while (1) { + bufpt = packet_buff; + + while (1) { + if (timeout) + return; + c = tty_getc(); + if (timeout) + return; + if (c == ESC) { + c = tty_getc(); + if (timeout) + return; + } + else if (c == END) + break; + if (bufpt < bufend) + *bufpt++ = c; + } + + ip = (struct iphdr *)packet_buff; + udp = (struct udphdr *)(packet_buff + (ip->ihl * 4)); + bootp = (struct bootp *)(udp + 1); + + if ((bufpt - packet_buff) >= + (sizeof(struct iphdr) + sizeof(struct udphdr)) && + ip->version == 4 && + ip->ihl >= 5 && + ip->protocol == 17 /* udp */ && + ip_fast_csum((unsigned char *)ip, ip->ihl) == 0 && + (udp->check == 0 || udp_check(udp, ntohs(ip->tot_len) - (ip->ihl*4), + ip->saddr, ip->daddr) == 0) && + ntohs(udp->source) == IPPORT_BOOTPS && + ntohs(udp->dest) == IPPORT_BOOTPC && + bootp->op == BOOTREPLY) + break; + } + } + + static int + do_bootp(int argc, char *argv[]) + { + struct iphdr *ip; + struct udphdr *udp; + struct bootp *bootp; + int howlong, howmany; + char buffer[128]; + void (*oldsig)(int); + + if (argc > 1) + howmany = atoi(argv[1]); + else + howmany = 3; + + if (argc > 2) + howlong = atoi(argv[2]); + else + howlong = 3; + + while (1) { + put_bootreq(); + + oldsig = signal(SIGALRM, TimeOut); + (void) alarm(howlong); + timeout = 0; + + get_buf(); + + alarm(0); + signal(SIGALRM, oldsig); + if (timeout) { + if (--howmany <= 0) { + if (opt_v) printf("Bootp timed out\n"); + return -1; + } + } else + break; + } + + ip = (struct iphdr *)packet_buff; + udp = (struct udphdr *)(packet_buff + (ip->ihl * 4)); + bootp = (struct bootp *)(udp + 1); + + mydip.loc_ip.s_addr = bootp->yiaddr; + mydip.rmt_ip.s_addr = bootp->siaddr; + + if (opt_v) { + /* + * inet_ntoa returns pointer into a single area. need to + * copy first return so second one doesn't overwrite it + */ + strcpy(buffer, inet_ntoa(mydip.loc_ip)); + printf("Address set to %s by bootp from %s\n", + buffer, inet_ntoa(mydip.rmt_ip)); + } + return(0); + } + struct commands commands[] = { + { "bootp", do_bootp }, { "databits", do_databits }, { "default", do_default }, { "dial", do_dial },