|
|
1.1 ! root 1: /* netboot ! 2: * ! 3: * tftp.c,v ! 4: * Revision 1.1 1993/07/08 16:04:12 brezak ! 5: * Diskless boot prom code from Jim McKim ([email protected]) ! 6: * ! 7: * Revision 1.3 1993/06/30 20:14:16 mckim ! 8: * Added BOOTP support. ! 9: * ! 10: * Revision 1.2 1993/05/28 20:01:31 mckim ! 11: * Fixed various StartProg() problems. ! 12: * ! 13: * Revision 1.1.1.1 1993/05/28 11:41:07 mckim ! 14: * Initial version. ! 15: * ! 16: * ! 17: * source in this file came from ! 18: * the Mach ethernet boot written by Leendert van Doorn. ! 19: * ! 20: * Trivial File Transfer Protocol (see RFC 783). ! 21: * ! 22: * Copyright (c) 1992 by Leendert van Doorn ! 23: */ ! 24: #include "proto.h" ! 25: #include "assert.h" ! 26: #include "param.h" ! 27: #include "packet.h" ! 28: #include "ether.h" ! 29: #include "inet.h" ! 30: #include "tftp.h" ! 31: #include "arp.h" ! 32: ! 33: ipaddr_t tftp_server; /* IP address of TFTP server */ ! 34: ipaddr_t tftp_gateway; ! 35: static char tftp_file_name[100]; ! 36: static short block; /* current block */ ! 37: static int ctid, stid; /* UDP client and server TID (network order) */ ! 38: ! 39: extern u_long work_area_org; ! 40: ! 41: /* ! 42: * Print IP address in a readable form ! 43: */ ! 44: void ! 45: IpPrintAddr(ipaddr_t addr) { ! 46: inetaddr_t ip; ! 47: ! 48: ip.a = addr; ! 49: printf("%d.%d.%d.%d", ip.s.a0, ip.s.a1, ip.s.a2, ip.s.a3); ! 50: } ! 51: ! 52: /* ! 53: * Generic TFTP error routine ! 54: */ ! 55: static void ! 56: TftpFail(ipaddr_t fromaddr, ipaddr_t toaddr, char *filename, char *reason) { ! 57: printf("Tftp of file '%s' from ", filename); ! 58: IpPrintAddr(fromaddr); ! 59: printf(" failed, %s\n", reason); ! 60: } ! 61: ! 62: /* ! 63: * One complement check sum ! 64: */ ! 65: static u_short ! 66: InChecksum(char *cp, u_long count) { ! 67: u_short *sp; ! 68: u_long sum, oneword = 0x00010000; ! 69: ! 70: for (sum = 0, sp = (u_short *)cp, count >>= 1; count--; ) { ! 71: sum += *sp++; ! 72: if (sum >= oneword) { ! 73: /* wrap carry into low bit */ ! 74: sum -= oneword; ! 75: sum++; ! 76: } ! 77: } ! 78: return ~sum; ! 79: } ! 80: ! 81: /* ! 82: * Setup the standard IP header fields for a destination, ! 83: * and send packet (possibly using the gateway). ! 84: */ ! 85: void ! 86: IpSend(packet_t *pkt, ipaddr_t dst, ipaddr_t gateway) { ! 87: iphdr_t *ip; ! 88: u_char edst[ETH_ADDRSIZE]; ! 89: static int ipid = 0; ! 90: #if TRACE > 0 ! 91: DUMP_STRUCT("IpSend: pkt (front)", pkt, 100); ! 92: #endif ! 93: pkt->pkt_offset -= sizeof(iphdr_t); ! 94: pkt->pkt_len += sizeof(iphdr_t); ! 95: ip = (iphdr_t *) pkt->pkt_offset; ! 96: ip->ip_vhl = (IP_VERSION << 4) | (sizeof(*ip) >> 2); ! 97: ip->ip_tos = 0; ! 98: ip->ip_len = htons(pkt->pkt_len); ! 99: ip->ip_id = ipid++; ! 100: ip->ip_off = 0; ! 101: ip->ip_ttl = IP_FRAGTTL; ! 102: ip->ip_p = IP_PROTO_UDP; ! 103: ip->ip_src = ip_myaddr ? ip_myaddr : IP_ANYADDR; ! 104: ip->ip_dst = dst; ! 105: ip->ip_sum = 0; ! 106: ip->ip_sum = InChecksum((char *)ip, sizeof(*ip)); ! 107: #if 0 ! 108: /* DUMP_STRUCT("pkt (after)", pkt, 100); */ ! 109: DUMP_STRUCT("ip", ip, sizeof(iphdr_t)+pkt->pkt_len); ! 110: #endif ! 111: if (ArpResolve(pkt, gateway ? gateway : dst, edst)) { ! 112: EtherSend(pkt, ETHTYPE_IP, edst); ! 113: PktRelease(pkt); ! 114: } ! 115: } ! 116: ! 117: /* ! 118: * States which TFTP can be in ! 119: */ ! 120: enum TftpPacketStatus { ! 121: TFTP_RECD_GOOD_PACKET, ! 122: TFTP_RECD_BAD_PACKET, ! 123: TFTP_RECD_SERVER_ABORT, ! 124: }; ! 125: ! 126: /* ! 127: * Pseudo header to compute UDP checksum ! 128: */ ! 129: struct pseudoheader { ! 130: ipaddr_t ph_src; ! 131: ipaddr_t ph_dst; ! 132: u_char ph_zero; ! 133: u_char ph_prot; ! 134: u_short ph_length; ! 135: }; ! 136: ! 137: /* ! 138: * Determine whether this IP packet is the TFTP data packet ! 139: * we were expecting. When a broadcast TFTP request was made ! 140: * we'll set the TFTP server address as well. ! 141: */ ! 142: static enum TftpPacketStatus ! 143: TftpDigestPacket(packet_t *pkt, char *rbuf, u_long *rlen) { ! 144: iphdr_t *ip; ! 145: udphdr_t *up; ! 146: tftphdr_t *tp; ! 147: struct pseudoheader ph; ! 148: u_short oldsum, sum; ! 149: u_short udplength; ! 150: ! 151: /* check for minimum size tftp packet */ ! 152: if (pkt->pkt_len < (sizeof(ethhdr_t) + sizeof(iphdr_t) + ! 153: sizeof(udphdr_t) + sizeof(tftphdr_t))) { ! 154: #if 0 ! 155: printe("TftpDigestPacket: bad packet size %d\n", pkt->pkt_len); ! 156: #endif ! 157: return TFTP_RECD_BAD_PACKET; ! 158: } ! 159: ! 160: /* IP related checks */ ! 161: ip = (iphdr_t *) (pkt->pkt_offset + sizeof(ethhdr_t)); ! 162: if (tftp_server != IP_BCASTADDR && ip->ip_src != tftp_server) { ! 163: #if 0 ! 164: printe("TftpDigestPacket: incorrect ip source address 0x%x\n", ip->ip_src); ! 165: #endif ! 166: return TFTP_RECD_BAD_PACKET; ! 167: } ! 168: if (ntohs(ip->ip_len) < ! 169: sizeof(iphdr_t) + sizeof(udphdr_t) + sizeof(tftphdr_t)) { ! 170: #if 0 ! 171: printe("TftpDigestPacket: bad ip length %d\n", ip->ip_len); ! 172: #endif ! 173: return TFTP_RECD_BAD_PACKET; ! 174: } ! 175: if (ip->ip_p != IP_PROTO_UDP) { ! 176: #if 0 ! 177: printe("TftpDigestPacket: wrong ip protocol type 0x%x\n", ip->ip_p); ! 178: #endif ! 179: return TFTP_RECD_BAD_PACKET; ! 180: } ! 181: if (ip_myaddr && ip->ip_dst != ip_myaddr) { ! 182: #if 0 ! 183: printe("TftpDigestPacket: incorrect ip destination address %x\n", ip->ip_dst); ! 184: #endif ! 185: return TFTP_RECD_BAD_PACKET; ! 186: } ! 187: ! 188: /* UDP related checks */ ! 189: up = (udphdr_t *) ((char *)ip + sizeof(iphdr_t)); ! 190: if (block && up->uh_sport != stid) { ! 191: #if 0 ! 192: printe("TftpDigestPacket: wrong udp source port 0x%x\n", up->uh_sport); ! 193: #endif ! 194: return TFTP_RECD_BAD_PACKET; ! 195: } ! 196: *rlen = ntohs(up->uh_len) - sizeof(udphdr_t) - sizeof(tftphdr_t); ! 197: if (up->uh_dport != ctid) { ! 198: #if 0 ! 199: printe("TftpDigestPacket: wrong udp destination port 0x%x\n", up->uh_dport); ! 200: #endif ! 201: return TFTP_RECD_BAD_PACKET; ! 202: } ! 203: ! 204: /* compute UDP checksum if any */ ! 205: oldsum = up->uh_sum; ! 206: if (oldsum) { ! 207: udplength = ntohs(up->uh_len); ! 208: /* ! 209: * zero the byte past the last data byte because the ! 210: * checksum will be over an even number of bytes. ! 211: */ ! 212: if (udplength & 01) ! 213: ((char *)up)[udplength] = '\0'; ! 214: ! 215: /* set up the pseudo-header */ ! 216: ph.ph_src = ip->ip_src; ! 217: ph.ph_dst = ip->ip_dst; ! 218: ph.ph_zero = 0; ! 219: ph.ph_prot = ip->ip_p; ! 220: ph.ph_length = htons(udplength); ! 221: ! 222: up->uh_sum = ~InChecksum((char *)&ph, sizeof(ph)); ! 223: sum = InChecksum((char *)up, (u_long)((udplength + 1) & ~1)); ! 224: up->uh_sum = oldsum; /* put original back */ ! 225: if (oldsum == (u_short) -1) ! 226: oldsum = 0; ! 227: if (sum != oldsum) { ! 228: #if 0 ! 229: printe("TftpDigestPacket: Bad checksum %x != %x, length %d from ", ! 230: sum, oldsum, udplength); ! 231: IpPrintAddr(ip->ip_src); ! 232: printe("\n"); ! 233: #endif ! 234: return TFTP_RECD_BAD_PACKET; ! 235: } ! 236: } ! 237: ! 238: /* TFTP related checks */ ! 239: tp = (tftphdr_t *) ((char *)up + sizeof(udphdr_t)); ! 240: switch (ntohs(tp->th_op)) { ! 241: case TFTP_ERROR: ! 242: printf("Diagnostic from server: error #%d, %s\n", ! 243: ntohs(tp->th_code), &tp->th_msg); ! 244: return TFTP_RECD_SERVER_ABORT; ! 245: case TFTP_DATA: ! 246: break; ! 247: default: ! 248: #if 0 ! 249: printe("TftpDigestPacket: incorrect tftp packet type 0x%x\n", tp->th_op); ! 250: #endif ! 251: return TFTP_RECD_BAD_PACKET; ! 252: } ! 253: ! 254: /* reject old packets */ ! 255: if (ntohs(tp->th_block) != block + 1) { ! 256: #if 0 ! 257: printe("TftpDigestPacket: bad block no. %d\n", tp->th_block); ! 258: #endif ! 259: return TFTP_RECD_BAD_PACKET; ! 260: } ! 261: ! 262: /* some TFTP related check */ ! 263: if (block == 0) { ! 264: stid = up->uh_sport; ! 265: /* in case of a broadcast, remember server address */ ! 266: if (tftp_server == IP_BCASTADDR) { ! 267: tftp_server = ip->ip_src; ! 268: #if 0 ! 269: printe("Found TFTP server at "); ! 270: IpPrintAddr(tftp_server); ! 271: printe("\n"); ! 272: #endif ! 273: } ! 274: } ! 275: if (stid != up->uh_sport) { ! 276: #if 0 ! 277: printe("TftpDigestPacket: incorrect udp source port 0x%x\n", up->uh_sport); ! 278: #endif ! 279: return TFTP_RECD_BAD_PACKET; ! 280: } ! 281: ! 282: bcopy(&tp->th_data, rbuf, *rlen); ! 283: ! 284: /* advance to next block */ ! 285: block++; ! 286: return TFTP_RECD_GOOD_PACKET; ! 287: } ! 288: ! 289: enum TftpStatus { ! 290: TFTP_SUCCESS, ! 291: TFTP_FAILURE, ! 292: }; ! 293: ! 294: static enum TftpStatus ! 295: Tftp(char *rbuf, u_long *rlen) { ! 296: u_long time, current, timeout; ! 297: int retry, quit; ! 298: enum TftpStatus rc = TFTP_FAILURE; ! 299: ! 300: *rlen = 0; ! 301: timeout = 4; /* four seconds */ ! 302: for (retry=0, quit=0; ++retry < NRETRIES && !quit; ) { ! 303: /* ! 304: * Send out a TFTP request. On the first block (actually ! 305: * zero) we send out a read request. Every other block we ! 306: * just acknowledge. ! 307: */ ! 308: packet_t *pkt; ! 309: ethhdr_t *ep; ! 310: udphdr_t *up; ! 311: tftphdr_t *tp; ! 312: #if TRACE > 0 ! 313: printe("Tftp: block %d, try #%d\n", block, retry); ! 314: #endif ! 315: pkt = PktAlloc(sizeof(ethhdr_t) + sizeof(iphdr_t)); ! 316: up = (udphdr_t *) pkt->pkt_offset; ! 317: tp = (tftphdr_t *) (pkt->pkt_offset + sizeof(udphdr_t)); ! 318: if (block == 0) { /* <RRQ> | <filename> | 0 | "octet" | 0 */ ! 319: char *cp, *p; ! 320: ! 321: tp->th_op = htons(TFTP_RRQ); ! 322: cp = tp->th_stuff; ! 323: for (p = tftp_file_name; *p; ) ! 324: *cp++ = *p++; ! 325: *cp++ = '\0'; ! 326: *cp++ = 'o'; ! 327: *cp++ = 'c'; ! 328: *cp++ = 't'; ! 329: *cp++ = 'e'; ! 330: *cp++ = 't'; ! 331: *cp++ = '\0'; ! 332: pkt->pkt_len = sizeof(udphdr_t) + (cp - (char *)tp); ! 333: } else { /* else <ACK> | <block> */ ! 334: tp->th_op = htons(TFTP_ACK); ! 335: tp->th_block = htons(block); ! 336: #if 0 ! 337: printe("ack block %x %x\n", tp->th_block, block); ! 338: #endif ! 339: pkt->pkt_len = sizeof(udphdr_t) + sizeof(tftphdr_t); ! 340: } ! 341: up->uh_sport = ctid; ! 342: up->uh_dport = stid; ! 343: up->uh_sum = 0; ! 344: up->uh_len = htons(pkt->pkt_len); ! 345: #if 0 ! 346: DUMP_STRUCT("tftphdr_t", tp, sizeof(tftphdr_t)); ! 347: DUMP_STRUCT("udphdr_t", up, sizeof(udphdr_t)); ! 348: printe("Tftp: "); ! 349: #endif ! 350: IpSend(pkt, tftp_server, tftp_gateway); ! 351: ! 352: /* ! 353: * Receive TFTP data or ARP packets ! 354: */ ! 355: time = timer() + timeout; ! 356: do { ! 357: pkt = EtherReceive(); ! 358: if (pkt) { ! 359: static int spin = 0; ! 360: ep = (ethhdr_t *) pkt->pkt_offset; ! 361: #if 0 ! 362: DUMP_STRUCT("ethhdr_t", ep, sizeof(ethhdr_t)); ! 363: #endif ! 364: switch (ntohs(ep->eth_proto)) { ! 365: case ETHTYPE_ARP: ! 366: ArpInput(pkt); ! 367: break; ! 368: case ETHTYPE_IP: ! 369: switch (TftpDigestPacket(pkt, rbuf, rlen)) { ! 370: case TFTP_RECD_GOOD_PACKET: ! 371: if (block % 8 == 0) ! 372: printf("%c\b", "-\\|/"[spin++ % 4]); ! 373: #if 0 ! 374: DUMP_STRUCT("good tftp packet", pkt, 100); ! 375: printe("TBD - copy tftp packet #%d, len %d to buffer\n", block, *rlen); ! 376: #endif ! 377: rc = TFTP_SUCCESS; ! 378: quit = 1; ! 379: break; ! 380: case TFTP_RECD_SERVER_ABORT: ! 381: TftpFail(tftp_server, ip_myaddr, tftp_file_name, "aborted by server"); ! 382: ! 383: rc = TFTP_FAILURE; ! 384: quit = 1; ! 385: break; ! 386: default: ! 387: /* for anything else, retry */ ! 388: #if 0 ! 389: printe("Tftp: bogus IP packet rec'd, still waiting\n"); ! 390: #endif ! 391: break; ! 392: } ! 393: break; ! 394: default: ! 395: #if 0 ! 396: printe("Tftp: undesired ethernet packet (type 0x%x) rec'd, still waiting\n", ! 397: ep->eth_proto); ! 398: #endif ! 399: break; ! 400: } ! 401: PktRelease(pkt); ! 402: } ! 403: current = timer(); ! 404: HandleKbdAttn(); ! 405: } while (current < time && !quit); ! 406: ! 407: #if 0 ! 408: /* TBD - move */ ! 409: eth_reset(); ! 410: #endif ! 411: ! 412: if (current >= time) ! 413: timeout <<= 1; ! 414: } ! 415: ! 416: if (retry > NRETRIES) { ! 417: TftpFail(tftp_server, ip_myaddr, tftp_file_name, "timed Out"); ! 418: } ! 419: return rc; ! 420: } ! 421: ! 422: static int tftp_at_eof = 1; ! 423: static u_long tftp_unread_bytes_in_buffer = 0; ! 424: ! 425: void ! 426: SetTftpParms(ipaddr_t server, ipaddr_t gateway, char *file_name) { ! 427: block = 0; ! 428: strncpy(tftp_file_name, file_name, MAX_FILE_NAME_LEN); ! 429: tftp_server = server; ! 430: tftp_at_eof = 0; ! 431: tftp_unread_bytes_in_buffer = 0; ! 432: stid = htons(IP_PORT_TFTP); ! 433: ctid = htons(rand()); ! 434: printf("Attempting to tftp file '%s'", tftp_file_name); ! 435: if (tftp_server != IP_BCASTADDR) { ! 436: printf(" from server "); ! 437: IpPrintAddr(tftp_server); ! 438: } else ! 439: printf(" using IP broadcast"); ! 440: tftp_gateway = gateway; ! 441: if (tftp_gateway) { ! 442: printf(" using gateway "); ! 443: IpPrintAddr(tftp_gateway); ! 444: } ! 445: printf("\n"); ! 446: } ! 447: ! 448: u_long ! 449: Read(void *result, u_long n_req) { ! 450: static u_long bufp = 0; ! 451: static char buf[PKT_DATASIZE]; ! 452: u_long length; ! 453: u_long n_recd = 0; ! 454: while (n_req && !tftp_at_eof) { ! 455: if (tftp_unread_bytes_in_buffer) { ! 456: *((char *)result)++ = buf[bufp++]; ! 457: n_req--; ! 458: n_recd++; ! 459: tftp_unread_bytes_in_buffer--; ! 460: } else { ! 461: switch (Tftp(buf, &length)) { ! 462: case TFTP_SUCCESS: ! 463: tftp_unread_bytes_in_buffer = length; ! 464: bufp = 0; ! 465: if (length < SEGSIZE) ! 466: tftp_at_eof = 1; ! 467: break; ! 468: default: ! 469: /* anything else should cause this to abend */ ! 470: tftp_unread_bytes_in_buffer = 0; ! 471: tftp_at_eof = 1; ! 472: break; ! 473: } ! 474: } ! 475: } ! 476: return n_recd; ! 477: } ! 478: ! 479: u_long ! 480: PhysRead(u_long addr, u_long n_req) { ! 481: u_long n_recd = 0; ! 482: while (n_req) { ! 483: char buf[512]; ! 484: u_long nd = n_req<sizeof(buf) ? n_req : sizeof(buf); ! 485: u_long nr = Read(buf, nd); ! 486: if (nr == 0) { ! 487: /* problem, incomplete read */ ! 488: break; ! 489: } ! 490: PhysBcopy(LA(buf), addr, nr); ! 491: n_req -= nr; ! 492: n_recd += nr; ! 493: addr += nr; ! 494: } ! 495: return n_recd; ! 496: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.