Index: head/stand/libsa/arp.c =================================================================== --- head/stand/libsa/arp.c (revision 329338) +++ head/stand/libsa/arp.c (revision 329339) @@ -1,305 +1,305 @@ /* $NetBSD: arp.c,v 1.18 1997/07/07 15:52:49 drochner Exp $ */ /* * Copyright (c) 1992 Regents of the University of California. * All rights reserved. * * This software was developed by the Computer Systems Engineering group * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and * contributed to Berkeley. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#) Header: arp.c,v 1.5 93/07/15 05:52:26 leres Exp (LBL) */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include "stand.h" #include "net.h" /* Cache stuff */ #define ARP_NUM 8 /* need at most 3 arp entries */ struct arp_list { struct in_addr addr; u_char ea[6]; } arp_list[ARP_NUM] = { /* XXX - net order `INADDR_BROADCAST' must be a constant */ { {0xffffffff}, BA } }; int arp_num = 1; /* Local forwards */ static ssize_t arpsend(struct iodesc *, void *, size_t); -static ssize_t arprecv(struct iodesc *, void **, void **, time_t); +static ssize_t arprecv(struct iodesc *, void **, void **, time_t, void *); /* Broadcast an ARP packet, asking who has addr on interface d */ u_char * arpwhohas(struct iodesc *d, struct in_addr addr) { int i; struct ether_arp *ah; struct arp_list *al; void *pkt; struct { struct ether_header eh; struct { struct ether_arp arp; u_char pad[18]; /* 60 - sizeof(...) */ } data; } wbuf; /* Try for cached answer first */ for (i = 0, al = arp_list; i < arp_num; ++i, ++al) if (addr.s_addr == al->addr.s_addr) return (al->ea); /* Don't overflow cache */ if (arp_num > ARP_NUM - 1) { arp_num = 1; /* recycle */ printf("arpwhohas: overflowed arp_list!\n"); } #ifdef ARP_DEBUG if (debug) printf("arpwhohas: send request for %s\n", inet_ntoa(addr)); #endif bzero((char*)&wbuf.data, sizeof(wbuf.data)); ah = &wbuf.data.arp; ah->arp_hrd = htons(ARPHRD_ETHER); ah->arp_pro = htons(ETHERTYPE_IP); ah->arp_hln = sizeof(ah->arp_sha); /* hardware address length */ ah->arp_pln = sizeof(ah->arp_spa); /* protocol address length */ ah->arp_op = htons(ARPOP_REQUEST); MACPY(d->myea, ah->arp_sha); bcopy(&d->myip, ah->arp_spa, sizeof(ah->arp_spa)); /* Leave zeros in arp_tha */ bcopy(&addr, ah->arp_tpa, sizeof(ah->arp_tpa)); /* Store ip address in cache (incomplete entry). */ al->addr = addr; pkt = NULL; ah = NULL; i = sendrecv(d, arpsend, &wbuf.data, sizeof(wbuf.data), - arprecv, &pkt, (void **)&ah); + arprecv, &pkt, (void **)&ah, NULL); if (i == -1) { panic("arp: no response for %s\n", inet_ntoa(addr)); } /* Store ethernet address in cache */ #ifdef ARP_DEBUG if (debug) { struct ether_header *eh; eh = (struct ether_header *)((uintptr_t)pkt + ETHER_ALIGN); printf("arp: response from %s\n", ether_sprintf(eh->ether_shost)); printf("arp: cacheing %s --> %s\n", inet_ntoa(addr), ether_sprintf(ah->arp_sha)); } #endif MACPY(ah->arp_sha, al->ea); ++arp_num; free(pkt); return (al->ea); } static ssize_t arpsend(struct iodesc *d, void *pkt, size_t len) { #ifdef ARP_DEBUG if (debug) printf("arpsend: called\n"); #endif return (sendether(d, pkt, len, bcea, ETHERTYPE_ARP)); } /* * Returns 0 if this is the packet we're waiting for * else -1 (and errno == 0) */ static ssize_t -arprecv(struct iodesc *d, void **pkt, void **payload, time_t tleft) +arprecv(struct iodesc *d, void **pkt, void **payload, time_t tleft, void *extra) { ssize_t n; struct ether_arp *ah; u_int16_t etype; /* host order */ void *ptr; #ifdef ARP_DEBUG if (debug) printf("arprecv: "); #endif ptr = NULL; n = readether(d, &ptr, (void **)&ah, tleft, &etype); errno = 0; /* XXX */ if (n == -1 || n < sizeof(struct ether_arp)) { #ifdef ARP_DEBUG if (debug) printf("bad len=%d\n", n); #endif free(ptr); return (-1); } if (etype != ETHERTYPE_ARP) { #ifdef ARP_DEBUG if (debug) printf("not arp type=%d\n", etype); #endif free(ptr); return (-1); } /* Ethernet address now checked in readether() */ if (ah->arp_hrd != htons(ARPHRD_ETHER) || ah->arp_pro != htons(ETHERTYPE_IP) || ah->arp_hln != sizeof(ah->arp_sha) || ah->arp_pln != sizeof(ah->arp_spa) ) { #ifdef ARP_DEBUG if (debug) printf("bad hrd/pro/hln/pln\n"); #endif free(ptr); return (-1); } if (ah->arp_op == htons(ARPOP_REQUEST)) { #ifdef ARP_DEBUG if (debug) printf("is request\n"); #endif arp_reply(d, ah); free(ptr); return (-1); } if (ah->arp_op != htons(ARPOP_REPLY)) { #ifdef ARP_DEBUG if (debug) printf("not ARP reply\n"); #endif free(ptr); return (-1); } /* Is the reply from the source we want? */ if (bcmp(&arp_list[arp_num].addr, ah->arp_spa, sizeof(ah->arp_spa))) { #ifdef ARP_DEBUG if (debug) printf("unwanted address\n"); #endif free(ptr); return (-1); } /* We don't care who the reply was sent to. */ /* We have our answer. */ #ifdef ARP_DEBUG if (debug) printf("got it\n"); #endif *pkt = ptr; *payload = ah; return (n); } /* * Convert an ARP request into a reply and send it. * Notes: Re-uses buffer. Pad to length = 46. */ void arp_reply(struct iodesc *d, void *pkt) { struct ether_arp *arp = pkt; if (arp->arp_hrd != htons(ARPHRD_ETHER) || arp->arp_pro != htons(ETHERTYPE_IP) || arp->arp_hln != sizeof(arp->arp_sha) || arp->arp_pln != sizeof(arp->arp_spa) ) { #ifdef ARP_DEBUG if (debug) printf("arp_reply: bad hrd/pro/hln/pln\n"); #endif return; } if (arp->arp_op != htons(ARPOP_REQUEST)) { #ifdef ARP_DEBUG if (debug) printf("arp_reply: not request!\n"); #endif return; } /* If we are not the target, ignore the request. */ if (bcmp(arp->arp_tpa, &d->myip, sizeof(arp->arp_tpa))) return; #ifdef ARP_DEBUG if (debug) { printf("arp_reply: to %s\n", ether_sprintf(arp->arp_sha)); } #endif arp->arp_op = htons(ARPOP_REPLY); /* source becomes target */ bcopy(arp->arp_sha, arp->arp_tha, sizeof(arp->arp_tha)); bcopy(arp->arp_spa, arp->arp_tpa, sizeof(arp->arp_tpa)); /* here becomes source */ bcopy(d->myea, arp->arp_sha, sizeof(arp->arp_sha)); bcopy(&d->myip, arp->arp_spa, sizeof(arp->arp_spa)); /* * No need to get fancy here. If the send fails, the * requestor will just ask again. */ (void) sendether(d, pkt, sizeof(*arp) + 18, arp->arp_tha, ETHERTYPE_ARP); } Index: head/stand/libsa/bootp.c =================================================================== --- head/stand/libsa/bootp.c (revision 329338) +++ head/stand/libsa/bootp.c (revision 329339) @@ -1,780 +1,781 @@ /* $NetBSD: bootp.c,v 1.14 1998/02/16 11:10:54 drochner Exp $ */ /* * Copyright (c) 1992 Regents of the University of California. * All rights reserved. * * This software was developed by the Computer Systems Engineering group * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and * contributed to Berkeley. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#) Header: bootp.c,v 1.4 93/09/11 03:13:51 leres Exp (LBL) */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #define BOOTP_DEBUGxx #define SUPPORT_DHCP #define DHCP_ENV_NOVENDOR 1 /* do not parse vendor options */ #define DHCP_ENV_PXE 10 /* assume pxe vendor options */ #define DHCP_ENV_FREEBSD 11 /* assume freebsd vendor options */ /* set DHCP_ENV to one of the values above to export dhcp options to kenv */ #define DHCP_ENV DHCP_ENV_NO_VENDOR #include "stand.h" #include "net.h" #include "netif.h" #include "bootp.h" struct in_addr servip; static time_t bot; static char vm_rfc1048[4] = VM_RFC1048; #ifdef BOOTP_VEND_CMU static char vm_cmu[4] = VM_CMU; #endif /* Local forwards */ static ssize_t bootpsend(struct iodesc *, void *, size_t); -static ssize_t bootprecv(struct iodesc *, void **, void **, time_t); +static ssize_t bootprecv(struct iodesc *, void **, void **, time_t, void *); static int vend_rfc1048(u_char *, u_int); #ifdef BOOTP_VEND_CMU static void vend_cmu(u_char *); #endif #ifdef DHCP_ENV /* export the dhcp response to kenv */ struct dhcp_opt; static void setenv_(u_char *cp, u_char *ep, struct dhcp_opt *opts); #else #define setenv_(a, b, c) #endif #ifdef SUPPORT_DHCP static char expected_dhcpmsgtype = -1, dhcp_ok; struct in_addr dhcp_serverip; #endif struct bootp *bootp_response; size_t bootp_response_size; static void bootp_fill_request(unsigned char *bp_vend) { /* * We are booting from PXE, we want to send the string * 'PXEClient' to the DHCP server so you have the option of * only responding to PXE aware dhcp requests. */ bp_vend[0] = TAG_CLASSID; bp_vend[1] = 9; bcopy("PXEClient", &bp_vend[2], 9); bp_vend[11] = TAG_USER_CLASS; /* len of each user class + number of user class */ bp_vend[12] = 8; /* len of the first user class */ bp_vend[13] = 7; bcopy("FreeBSD", &bp_vend[14], 7); bp_vend[21] = TAG_PARAM_REQ; bp_vend[22] = 7; bp_vend[23] = TAG_ROOTPATH; bp_vend[24] = TAG_HOSTNAME; bp_vend[25] = TAG_SWAPSERVER; bp_vend[26] = TAG_GATEWAY; bp_vend[27] = TAG_SUBNET_MASK; bp_vend[28] = TAG_INTF_MTU; bp_vend[29] = TAG_SERVERID; bp_vend[30] = TAG_END; } /* Fetch required bootp infomation */ void bootp(int sock) { void *pkt; struct iodesc *d; struct bootp *bp; struct { u_char header[HEADER_SIZE]; struct bootp wbootp; } wbuf; struct bootp *rbootp; #ifdef BOOTP_DEBUG if (debug) printf("bootp: socket=%d\n", sock); #endif if (!bot) bot = getsecs(); if (!(d = socktodesc(sock))) { printf("bootp: bad socket. %d\n", sock); return; } #ifdef BOOTP_DEBUG if (debug) printf("bootp: d=%lx\n", (long)d); #endif bp = &wbuf.wbootp; bzero(bp, sizeof(*bp)); bp->bp_op = BOOTREQUEST; bp->bp_htype = 1; /* 10Mb Ethernet (48 bits) */ bp->bp_hlen = 6; bp->bp_xid = htonl(d->xid); MACPY(d->myea, bp->bp_chaddr); strncpy(bp->bp_file, bootfile, sizeof(bp->bp_file)); bcopy(vm_rfc1048, bp->bp_vend, sizeof(vm_rfc1048)); #ifdef SUPPORT_DHCP bp->bp_vend[4] = TAG_DHCP_MSGTYPE; bp->bp_vend[5] = 1; bp->bp_vend[6] = DHCPDISCOVER; bootp_fill_request(&bp->bp_vend[7]); #else bp->bp_vend[4] = TAG_END; #endif d->myip.s_addr = INADDR_ANY; d->myport = htons(IPPORT_BOOTPC); d->destip.s_addr = INADDR_BROADCAST; d->destport = htons(IPPORT_BOOTPS); #ifdef SUPPORT_DHCP expected_dhcpmsgtype = DHCPOFFER; dhcp_ok = 0; #endif if(sendrecv(d, bootpsend, bp, sizeof(*bp), - bootprecv, &pkt, (void **)&rbootp) == -1) { + bootprecv, &pkt, (void **)&rbootp, NULL) == -1) { printf("bootp: no reply\n"); return; } #ifdef SUPPORT_DHCP if(dhcp_ok) { u_int32_t leasetime; bp->bp_vend[6] = DHCPREQUEST; bp->bp_vend[7] = TAG_REQ_ADDR; bp->bp_vend[8] = 4; bcopy(&rbootp->bp_yiaddr, &bp->bp_vend[9], 4); bp->bp_vend[13] = TAG_SERVERID; bp->bp_vend[14] = 4; bcopy(&dhcp_serverip.s_addr, &bp->bp_vend[15], 4); bp->bp_vend[19] = TAG_LEASETIME; bp->bp_vend[20] = 4; leasetime = htonl(300); bcopy(&leasetime, &bp->bp_vend[21], 4); bootp_fill_request(&bp->bp_vend[25]); expected_dhcpmsgtype = DHCPACK; free(pkt); if(sendrecv(d, bootpsend, bp, sizeof(*bp), - bootprecv, &pkt, (void **)&rbootp) == -1) { + bootprecv, &pkt, (void **)&rbootp, NULL) == -1) { printf("DHCPREQUEST failed\n"); return; } } #endif myip = d->myip = rbootp->bp_yiaddr; servip = rbootp->bp_siaddr; if (rootip.s_addr == INADDR_ANY) rootip = servip; bcopy(rbootp->bp_file, bootfile, sizeof(bootfile)); bootfile[sizeof(bootfile) - 1] = '\0'; if (!netmask) { if (IN_CLASSA(ntohl(myip.s_addr))) netmask = htonl(IN_CLASSA_NET); else if (IN_CLASSB(ntohl(myip.s_addr))) netmask = htonl(IN_CLASSB_NET); else netmask = htonl(IN_CLASSC_NET); #ifdef BOOTP_DEBUG if (debug) printf("'native netmask' is %s\n", intoa(netmask)); #endif } #ifdef BOOTP_DEBUG if (debug) printf("mask: %s\n", intoa(netmask)); #endif /* We need a gateway if root is on a different net */ if (!SAMENET(myip, rootip, netmask)) { #ifdef BOOTP_DEBUG if (debug) printf("need gateway for root ip\n"); #endif } /* Toss gateway if on a different net */ if (!SAMENET(myip, gateip, netmask)) { #ifdef BOOTP_DEBUG if (debug) printf("gateway ip (%s) bad\n", inet_ntoa(gateip)); #endif gateip.s_addr = 0; } /* Bump xid so next request will be unique. */ ++d->xid; free(pkt); } /* Transmit a bootp request */ static ssize_t bootpsend(struct iodesc *d, void *pkt, size_t len) { struct bootp *bp; #ifdef BOOTP_DEBUG if (debug) printf("bootpsend: d=%lx called.\n", (long)d); #endif bp = pkt; bp->bp_secs = htons((u_short)(getsecs() - bot)); #ifdef BOOTP_DEBUG if (debug) printf("bootpsend: calling sendudp\n"); #endif return (sendudp(d, pkt, len)); } static ssize_t -bootprecv(struct iodesc *d, void **pkt, void **payload, time_t tleft) +bootprecv(struct iodesc *d, void **pkt, void **payload, time_t tleft, + void *extra) { ssize_t n; struct bootp *bp; void *ptr; #ifdef BOOTP_DEBUG if (debug) printf("bootp_recvoffer: called\n"); #endif ptr = NULL; n = readudp(d, &ptr, (void **)&bp, tleft); if (n == -1 || n < sizeof(struct bootp) - BOOTP_VENDSIZE) goto bad; #ifdef BOOTP_DEBUG if (debug) printf("bootprecv: checked. bp = %p, n = %zd\n", bp, n); #endif if (bp->bp_xid != htonl(d->xid)) { #ifdef BOOTP_DEBUG if (debug) { printf("bootprecv: expected xid 0x%lx, got 0x%x\n", d->xid, ntohl(bp->bp_xid)); } #endif goto bad; } #ifdef BOOTP_DEBUG if (debug) printf("bootprecv: got one!\n"); #endif /* Suck out vendor info */ if (bcmp(vm_rfc1048, bp->bp_vend, sizeof(vm_rfc1048)) == 0) { int vsize = n - offsetof(struct bootp, bp_vend); if (vend_rfc1048(bp->bp_vend, vsize) != 0) goto bad; /* Save copy of bootp reply or DHCP ACK message */ if (bp->bp_op == BOOTREPLY && ((dhcp_ok == 1 && expected_dhcpmsgtype == DHCPACK) || dhcp_ok == 0)) { free(bootp_response); bootp_response = malloc(n); if (bootp_response != NULL) { bootp_response_size = n; bcopy(bp, bootp_response, bootp_response_size); } } } #ifdef BOOTP_VEND_CMU else if (bcmp(vm_cmu, bp->bp_vend, sizeof(vm_cmu)) == 0) vend_cmu(bp->bp_vend); #endif else printf("bootprecv: unknown vendor 0x%lx\n", (long)bp->bp_vend); *pkt = ptr; *payload = bp; return (n); bad: free(ptr); errno = 0; return (-1); } static int vend_rfc1048(u_char *cp, u_int len) { u_char *ep; int size; u_char tag; const char *val; #ifdef BOOTP_DEBUG if (debug) printf("vend_rfc1048 bootp info. len=%d\n", len); #endif ep = cp + len; /* Step over magic cookie */ cp += sizeof(int); setenv_(cp, ep, NULL); while (cp < ep) { tag = *cp++; size = *cp++; if (tag == TAG_END) break; if (tag == TAG_SUBNET_MASK) { bcopy(cp, &netmask, sizeof(netmask)); } if (tag == TAG_GATEWAY) { bcopy(cp, &gateip.s_addr, sizeof(gateip.s_addr)); } if (tag == TAG_SWAPSERVER) { /* let it override bp_siaddr */ bcopy(cp, &rootip.s_addr, sizeof(rootip.s_addr)); } if (tag == TAG_ROOTPATH) { if ((val = getenv("dhcp.root-path")) == NULL) val = (const char *)cp; strlcpy(rootpath, val, sizeof(rootpath)); } if (tag == TAG_HOSTNAME) { if ((val = getenv("dhcp.host-name")) == NULL) val = (const char *)cp; strlcpy(hostname, val, sizeof(hostname)); } if (tag == TAG_INTF_MTU) { intf_mtu = 0; if ((val = getenv("dhcp.interface-mtu")) != NULL) { unsigned long tmp; char *end; errno = 0; /* * Do not allow MTU to exceed max IPv4 packet * size, max value of 16-bit word. */ tmp = strtoul(val, &end, 0); if (errno != 0 || *val == '\0' || *end != '\0' || tmp > USHRT_MAX) { printf("%s: bad value: \"%s\", " "ignoring\n", "dhcp.interface-mtu", val); } else { intf_mtu = (u_int)tmp; } } if (intf_mtu <= 0) intf_mtu = be16dec(cp); } #ifdef SUPPORT_DHCP if (tag == TAG_DHCP_MSGTYPE) { if(*cp != expected_dhcpmsgtype) return(-1); dhcp_ok = 1; } if (tag == TAG_SERVERID) { bcopy(cp, &dhcp_serverip.s_addr, sizeof(dhcp_serverip.s_addr)); } #endif cp += size; } return(0); } #ifdef BOOTP_VEND_CMU static void vend_cmu(u_char *cp) { struct cmu_vend *vp; #ifdef BOOTP_DEBUG if (debug) printf("vend_cmu bootp info.\n"); #endif vp = (struct cmu_vend *)cp; if (vp->v_smask.s_addr != 0) { netmask = vp->v_smask.s_addr; } if (vp->v_dgate.s_addr != 0) { gateip = vp->v_dgate; } } #endif #ifdef DHCP_ENV /* * Parse DHCP options and store them into kenv variables. * Original code from Danny Braniss, modifications by Luigi Rizzo. * * The parser is driven by tables which specify the type and name of * each dhcp option and how it appears in kenv. * The first entry in the list contains the prefix used to set the kenv * name (including the . if needed), the last entry must have a 0 tag. * Entries do not need to be sorted though it helps for readability. * * Certain vendor-specific tables can be enabled according to DHCP_ENV. * Set it to 0 if you don't want any. */ enum opt_fmt { __NONE = 0, __8 = 1, __16 = 2, __32 = 4, /* Unsigned fields, value=size */ __IP, /* IPv4 address */ __TXT, /* C string */ __BYTES, /* byte sequence, printed %02x */ __INDIR, /* name=value */ __ILIST, /* name=value;name=value ... */ __VE, /* vendor specific, recurse */ }; struct dhcp_opt { uint8_t tag; uint8_t fmt; const char *desc; }; static struct dhcp_opt vndr_opt[] = { /* Vendor Specific Options */ #if DHCP_ENV == DHCP_ENV_FREEBSD /* FreeBSD table in the original code */ {0, 0, "FreeBSD"}, /* prefix */ {1, __TXT, "kernel"}, {2, __TXT, "kernelname"}, {3, __TXT, "kernel_options"}, {4, __IP, "usr-ip"}, {5, __TXT, "conf-path"}, {6, __TXT, "rc.conf0"}, {7, __TXT, "rc.conf1"}, {8, __TXT, "rc.conf2"}, {9, __TXT, "rc.conf3"}, {10, __TXT, "rc.conf4"}, {11, __TXT, "rc.conf5"}, {12, __TXT, "rc.conf6"}, {13, __TXT, "rc.conf7"}, {14, __TXT, "rc.conf8"}, {15, __TXT, "rc.conf9"}, {20, __TXT, "boot.nfsroot.options"}, {245, __INDIR, ""}, {246, __INDIR, ""}, {247, __INDIR, ""}, {248, __INDIR, ""}, {249, __INDIR, ""}, {250, __INDIR, ""}, {251, __INDIR, ""}, {252, __INDIR, ""}, {253, __INDIR, ""}, {254, __INDIR, ""}, #elif DHCP_ENV == DHCP_ENV_PXE /* some pxe options, RFC4578 */ {0, 0, "pxe"}, /* prefix */ {93, __16, "system-architecture"}, {94, __BYTES, "network-interface"}, {97, __BYTES, "machine-identifier"}, #else /* default (empty) table */ {0, 0, "dhcp.vendor."}, /* prefix */ #endif {0, __TXT, "%soption-%d"} }; static struct dhcp_opt dhcp_opt[] = { /* DHCP Option names, formats and codes, from RFC2132. */ {0, 0, "dhcp."}, // prefix {1, __IP, "subnet-mask"}, {2, __32, "time-offset"}, /* this is signed */ {3, __IP, "routers"}, {4, __IP, "time-servers"}, {5, __IP, "ien116-name-servers"}, {6, __IP, "domain-name-servers"}, {7, __IP, "log-servers"}, {8, __IP, "cookie-servers"}, {9, __IP, "lpr-servers"}, {10, __IP, "impress-servers"}, {11, __IP, "resource-location-servers"}, {12, __TXT, "host-name"}, {13, __16, "boot-size"}, {14, __TXT, "merit-dump"}, {15, __TXT, "domain-name"}, {16, __IP, "swap-server"}, {17, __TXT, "root-path"}, {18, __TXT, "extensions-path"}, {19, __8, "ip-forwarding"}, {20, __8, "non-local-source-routing"}, {21, __IP, "policy-filter"}, {22, __16, "max-dgram-reassembly"}, {23, __8, "default-ip-ttl"}, {24, __32, "path-mtu-aging-timeout"}, {25, __16, "path-mtu-plateau-table"}, {26, __16, "interface-mtu"}, {27, __8, "all-subnets-local"}, {28, __IP, "broadcast-address"}, {29, __8, "perform-mask-discovery"}, {30, __8, "mask-supplier"}, {31, __8, "perform-router-discovery"}, {32, __IP, "router-solicitation-address"}, {33, __IP, "static-routes"}, {34, __8, "trailer-encapsulation"}, {35, __32, "arp-cache-timeout"}, {36, __8, "ieee802-3-encapsulation"}, {37, __8, "default-tcp-ttl"}, {38, __32, "tcp-keepalive-interval"}, {39, __8, "tcp-keepalive-garbage"}, {40, __TXT, "nis-domain"}, {41, __IP, "nis-servers"}, {42, __IP, "ntp-servers"}, {43, __VE, "vendor-encapsulated-options"}, {44, __IP, "netbios-name-servers"}, {45, __IP, "netbios-dd-server"}, {46, __8, "netbios-node-type"}, {47, __TXT, "netbios-scope"}, {48, __IP, "x-font-servers"}, {49, __IP, "x-display-managers"}, {50, __IP, "dhcp-requested-address"}, {51, __32, "dhcp-lease-time"}, {52, __8, "dhcp-option-overload"}, {53, __8, "dhcp-message-type"}, {54, __IP, "dhcp-server-identifier"}, {55, __8, "dhcp-parameter-request-list"}, {56, __TXT, "dhcp-message"}, {57, __16, "dhcp-max-message-size"}, {58, __32, "dhcp-renewal-time"}, {59, __32, "dhcp-rebinding-time"}, {60, __TXT, "vendor-class-identifier"}, {61, __TXT, "dhcp-client-identifier"}, {64, __TXT, "nisplus-domain"}, {65, __IP, "nisplus-servers"}, {66, __TXT, "tftp-server-name"}, {67, __TXT, "bootfile-name"}, {68, __IP, "mobile-ip-home-agent"}, {69, __IP, "smtp-server"}, {70, __IP, "pop-server"}, {71, __IP, "nntp-server"}, {72, __IP, "www-server"}, {73, __IP, "finger-server"}, {74, __IP, "irc-server"}, {75, __IP, "streettalk-server"}, {76, __IP, "streettalk-directory-assistance-server"}, {77, __TXT, "user-class"}, {85, __IP, "nds-servers"}, {86, __TXT, "nds-tree-name"}, {87, __TXT, "nds-context"}, {210, __TXT, "authenticate"}, /* use the following entries for arbitrary variables */ {246, __ILIST, ""}, {247, __ILIST, ""}, {248, __ILIST, ""}, {249, __ILIST, ""}, {250, __INDIR, ""}, {251, __INDIR, ""}, {252, __INDIR, ""}, {253, __INDIR, ""}, {254, __INDIR, ""}, {0, __TXT, "%soption-%d"} }; /* * parse a dhcp response, set environment variables translating options * names and values according to the tables above. Also set dhcp.tags * to the list of selected tags. */ static void setenv_(u_char *cp, u_char *ep, struct dhcp_opt *opts) { u_char *ncp; u_char tag; char tags[512], *tp; /* the list of tags */ #define FLD_SEP ',' /* separator in list of elements */ ncp = cp; tp = tags; if (opts == NULL) opts = dhcp_opt; while (ncp < ep) { unsigned int size; /* option size */ char *vp, *endv, buf[256]; /* the value buffer */ struct dhcp_opt *op; tag = *ncp++; /* extract tag and size */ size = *ncp++; cp = ncp; /* current payload */ ncp += size; /* point to the next option */ if (tag == TAG_END) break; if (tag == 0) continue; for (op = opts+1; op->tag && op->tag != tag; op++) ; /* if not found we end up on the default entry */ /* * Copy data into the buffer. libstand does not have snprintf so we * need to be careful with sprintf(). With strings, the source is * always <256 char so shorter than the buffer so we are safe; with * other arguments, the longest string is inet_ntoa which is 16 bytes * so we make sure to have always enough room in the string before * trying an sprint. */ vp = buf; *vp = '\0'; endv = buf + sizeof(buf) - 1 - 16; /* last valid write position */ switch(op->fmt) { case __NONE: break; /* should not happen */ case __VE: /* recurse, vendor specific */ setenv_(cp, cp+size, vndr_opt); break; case __IP: /* ip address */ for (; size > 0 && vp < endv; size -= 4, cp += 4) { struct in_addr in_ip; /* ip addresses */ if (vp != buf) *vp++ = FLD_SEP; bcopy(cp, &in_ip.s_addr, sizeof(in_ip.s_addr)); sprintf(vp, "%s", inet_ntoa(in_ip)); vp += strlen(vp); } break; case __BYTES: /* opaque byte string */ for (; size > 0 && vp < endv; size -= 1, cp += 1) { sprintf(vp, "%02x", *cp); vp += strlen(vp); } break; case __TXT: bcopy(cp, buf, size); /* cannot overflow */ buf[size] = 0; break; case __32: case __16: case __8: /* op->fmt is also the length of each field */ for (; size > 0 && vp < endv; size -= op->fmt, cp += op->fmt) { uint32_t v; if (op->fmt == __32) v = (cp[0]<<24) + (cp[1]<<16) + (cp[2]<<8) + cp[3]; else if (op->fmt == __16) v = (cp[0]<<8) + cp[1]; else v = cp[0]; if (vp != buf) *vp++ = FLD_SEP; sprintf(vp, "%u", v); vp += strlen(vp); } break; case __INDIR: /* name=value */ case __ILIST: /* name=value;name=value... */ bcopy(cp, buf, size); /* cannot overflow */ buf[size] = '\0'; for (endv = buf; endv; endv = vp) { u_char *s = NULL; /* semicolon ? */ /* skip leading whitespace */ while (*endv && strchr(" \t\n\r", *endv)) endv++; vp = strchr(endv, '='); /* find name=value separator */ if (!vp) break; *vp++ = 0; if (op->fmt == __ILIST && (s = strchr(vp, ';'))) *s++ = '\0'; setenv(endv, vp, 1); vp = s; /* prepare for next round */ } buf[0] = '\0'; /* option already done */ } if (tp - tags < sizeof(tags) - 5) { /* add tag to the list */ if (tp != tags) *tp++ = FLD_SEP; sprintf(tp, "%d", tag); tp += strlen(tp); } if (buf[0]) { char env[128]; /* the string name */ if (op->tag == 0) sprintf(env, op->desc, opts[0].desc, tag); else sprintf(env, "%s%s", opts[0].desc, op->desc); /* * Do not replace existing values in the environment, so that * locally-obtained values can override server-provided values. */ setenv(env, buf, 0); } } if (tp != tags) { char env[128]; /* the string name */ sprintf(env, "%stags", opts[0].desc); setenv(env, tags, 1); } } #endif /* additional dhcp */ Index: head/stand/libsa/net.c =================================================================== --- head/stand/libsa/net.c (revision 329338) +++ head/stand/libsa/net.c (revision 329339) @@ -1,283 +1,283 @@ /* $NetBSD: net.c,v 1.20 1997/12/26 22:41:30 scottr Exp $ */ /* * Copyright (c) 1992 Regents of the University of California. * All rights reserved. * * This software was developed by the Computer Systems Engineering group * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and * contributed to Berkeley. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#) Header: net.c,v 1.9 93/08/06 19:32:15 leres Exp (LBL) */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include "stand.h" #include "net.h" /* * Send a packet and wait for a reply, with exponential backoff. * * The send routine must return the actual number of bytes written, * or -1 on error. * * The receive routine can indicate success by returning the number of * bytes read; it can return 0 to indicate EOF; it can return -1 with a * non-zero errno to indicate failure; finally, it can return -1 with a * zero errno to indicate it isn't done yet. */ ssize_t sendrecv(struct iodesc *d, ssize_t (*sproc)(struct iodesc *, void *, size_t), void *sbuf, size_t ssize, - ssize_t (*rproc)(struct iodesc *, void **, void **, time_t), - void **pkt, void **payload) + ssize_t (*rproc)(struct iodesc *, void **, void **, time_t, void *), + void **pkt, void **payload, void *recv_extra) { ssize_t cc; time_t t, tmo, tlast; long tleft; #ifdef NET_DEBUG if (debug) printf("sendrecv: called\n"); #endif tmo = MINTMO; tlast = 0; tleft = 0; t = getsecs(); for (;;) { if (tleft <= 0) { if (tmo >= MAXTMO) { errno = ETIMEDOUT; return -1; } cc = (*sproc)(d, sbuf, ssize); if (cc != -1 && cc < ssize) panic("sendrecv: short write! (%zd < %zd)", cc, ssize); tleft = tmo; tmo += MINTMO; if (tmo > MAXTMO) tmo = MAXTMO; if (cc == -1) { /* Error on transmit; wait before retrying */ while ((getsecs() - t) < tmo) ; tleft = 0; continue; } tlast = t; } /* Try to get a packet and process it. */ - cc = (*rproc)(d, pkt, payload, tleft); + cc = (*rproc)(d, pkt, payload, tleft, recv_extra); /* Return on data, EOF or real error. */ if (cc != -1 || (errno != 0 && errno != ETIMEDOUT)) return (cc); /* Timed out or didn't get the packet we're waiting for */ t = getsecs(); tleft -= t - tlast; tlast = t; } } /* * Like inet_addr() in the C library, but we only accept base-10. * Return values are in network order. */ n_long inet_addr(char *cp) { u_long val; int n; char c; u_int parts[4]; u_int *pp = parts; for (;;) { /* * Collect number up to ``.''. * Values are specified as for C: * 0x=hex, 0=octal, other=decimal. */ val = 0; while ((c = *cp) != '\0') { if (c >= '0' && c <= '9') { val = (val * 10) + (c - '0'); cp++; continue; } break; } if (*cp == '.') { /* * Internet format: * a.b.c.d * a.b.c (with c treated as 16-bits) * a.b (with b treated as 24 bits) */ if (pp >= parts + 3 || val > 0xff) goto bad; *pp++ = val, cp++; } else break; } /* * Check for trailing characters. */ if (*cp != '\0') goto bad; /* * Concoct the address according to * the number of parts specified. */ n = pp - parts + 1; switch (n) { case 1: /* a -- 32 bits */ break; case 2: /* a.b -- 8.24 bits */ if (val > 0xffffff) goto bad; val |= parts[0] << 24; break; case 3: /* a.b.c -- 8.8.16 bits */ if (val > 0xffff) goto bad; val |= (parts[0] << 24) | (parts[1] << 16); break; case 4: /* a.b.c.d -- 8.8.8.8 bits */ if (val > 0xff) goto bad; val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8); break; } return (htonl(val)); bad: return (htonl(INADDR_NONE)); } char * inet_ntoa(struct in_addr ia) { return (intoa(ia.s_addr)); } /* Similar to inet_ntoa() */ char * intoa(n_long addr) { char *cp; u_int byte; int n; static char buf[17]; /* strlen(".255.255.255.255") + 1 */ addr = ntohl(addr); cp = &buf[sizeof buf]; *--cp = '\0'; n = 4; do { byte = addr & 0xff; *--cp = byte % 10 + '0'; byte /= 10; if (byte > 0) { *--cp = byte % 10 + '0'; byte /= 10; if (byte > 0) *--cp = byte + '0'; } *--cp = '.'; addr >>= 8; } while (--n > 0); return (cp+1); } static char * number(char *s, int *n) { for (*n = 0; isdigit(*s); s++) *n = (*n * 10) + *s - '0'; return s; } n_long ip_convertaddr(char *p) { #define IP_ANYADDR 0 n_long addr = 0, n; if (p == (char *)0 || *p == '\0') return IP_ANYADDR; p = number(p, &n); addr |= (n << 24) & 0xff000000; if (*p == '\0' || *p++ != '.') return IP_ANYADDR; p = number(p, &n); addr |= (n << 16) & 0xff0000; if (*p == '\0' || *p++ != '.') return IP_ANYADDR; p = number(p, &n); addr |= (n << 8) & 0xff00; if (*p == '\0' || *p++ != '.') return IP_ANYADDR; p = number(p, &n); addr |= n & 0xff; if (*p != '\0') return IP_ANYADDR; return htonl(addr); } Index: head/stand/libsa/net.h =================================================================== --- head/stand/libsa/net.h (revision 329338) +++ head/stand/libsa/net.h (revision 329339) @@ -1,131 +1,132 @@ /* $NetBSD: net.h,v 1.10 1995/10/20 00:46:30 cgd Exp $ */ /* * Copyright (c) 1993 Adam Glass * Copyright (c) 1992 Regents of the University of California. * All rights reserved. * * This software was developed by the Computer Systems Engineering group * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and * contributed to Berkeley. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _STAND_NET_H #define _STAND_NET_H #ifndef _KERNEL /* XXX - see */ #undef __IPADDR #define __IPADDR(x) htonl((u_int32_t)(x)) #endif #include "iodesc.h" #define BA { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } enum net_proto { NET_NONE, NET_NFS, NET_TFTP }; /* Returns true if n_long's on the same net */ #define SAMENET(a1, a2, m) ((a1.s_addr & m) == (a2.s_addr & m)) #define MACPY(s, d) bcopy((char *)s, (char *)d, 6) #define MAXTMO 120 /* seconds */ #define MINTMO 2 /* seconds */ #define FNAME_SIZE 128 #define IFNAME_SIZE 16 #define RECV_SIZE 1536 /* XXX delete this */ /* * How much room to leave for headers: * 14: struct ether_header * 20: struct ip * 8: struct udphdr * That's 42 but let's pad it out to 48 bytes. */ #define ETHER_SIZE 14 #define HEADER_SIZE 48 extern u_char bcea[6]; extern char rootpath[FNAME_SIZE]; extern char bootfile[FNAME_SIZE]; extern char hostname[FNAME_SIZE]; extern int hostnamelen; extern char domainname[FNAME_SIZE]; extern int domainnamelen; extern int netproto; extern char ifname[IFNAME_SIZE]; /* All of these are in network order. */ extern struct in_addr myip; extern struct in_addr rootip; extern struct in_addr swapip; extern struct in_addr gateip; extern struct in_addr nameip; extern n_long netmask; extern u_int intf_mtu; extern int debug; /* defined in the machdep sources */ extern struct iodesc sockets[SOPEN_MAX]; /* ARP/RevARP functions: */ u_char *arpwhohas(struct iodesc *, struct in_addr); void arp_reply(struct iodesc *, void *); int rarp_getipaddress(int); /* Link functions: */ ssize_t sendether(struct iodesc *d, void *pkt, size_t len, u_char *dea, int etype); ssize_t readether(struct iodesc *, void **, void **, time_t, uint16_t *); ssize_t sendip(struct iodesc *, void *, size_t, uint8_t); ssize_t readip(struct iodesc *, void **, void **, time_t, uint8_t); ssize_t sendudp(struct iodesc *, void *, size_t); ssize_t readudp(struct iodesc *, void **, void **, time_t); ssize_t sendrecv(struct iodesc *, ssize_t (*)(struct iodesc *, void *, size_t), void *, size_t, - ssize_t (*)(struct iodesc *, void **, void **, time_t), - void **, void **); + ssize_t (*)(struct iodesc *, void **, void **, time_t, + void *), + void **, void **, void *); /* bootp/DHCP */ void bootp(int); /* Utilities: */ char *ether_sprintf(u_char *); int in_cksum(void *, int); char *inet_ntoa(struct in_addr); char *intoa(n_long); /* similar to inet_ntoa */ n_long inet_addr(char *); #endif /* ! _STAND_NET_H */ Index: head/stand/libsa/rarp.c =================================================================== --- head/stand/libsa/rarp.c (revision 329338) +++ head/stand/libsa/rarp.c (revision 329339) @@ -1,218 +1,219 @@ /* $NetBSD: rarp.c,v 1.16 1997/07/07 15:52:52 drochner Exp $ */ /* * Copyright (c) 1992 Regents of the University of California. * All rights reserved. * * This software was developed by the Computer Systems Engineering group * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and * contributed to Berkeley. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#) Header: arp.c,v 1.5 93/07/15 05:52:26 leres Exp (LBL) */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include "stand.h" #include "net.h" #include "netif.h" static ssize_t rarpsend(struct iodesc *, void *, size_t); -static ssize_t rarprecv(struct iodesc *, void **, void **, time_t); +static ssize_t rarprecv(struct iodesc *, void **, void **, time_t, void *); /* * Ethernet (Reverse) Address Resolution Protocol (see RFC 903, and 826). */ int rarp_getipaddress(int sock) { struct iodesc *d; struct ether_arp *ap; void *pkt; struct { u_char header[ETHER_SIZE]; struct { struct ether_arp arp; u_char pad[18]; /* 60 - sizeof(arp) */ } data; } wbuf; #ifdef RARP_DEBUG if (debug) printf("rarp: socket=%d\n", sock); #endif if (!(d = socktodesc(sock))) { printf("rarp: bad socket. %d\n", sock); return (-1); } #ifdef RARP_DEBUG if (debug) printf("rarp: d=%x\n", (u_int)d); #endif bzero((char*)&wbuf.data, sizeof(wbuf.data)); ap = &wbuf.data.arp; ap->arp_hrd = htons(ARPHRD_ETHER); ap->arp_pro = htons(ETHERTYPE_IP); ap->arp_hln = sizeof(ap->arp_sha); /* hardware address length */ ap->arp_pln = sizeof(ap->arp_spa); /* protocol address length */ ap->arp_op = htons(ARPOP_REVREQUEST); bcopy(d->myea, ap->arp_sha, 6); bcopy(d->myea, ap->arp_tha, 6); pkt = NULL; if (sendrecv(d, rarpsend, &wbuf.data, sizeof(wbuf.data), - rarprecv, &pkt, (void *)&ap) < 0) { + rarprecv, &pkt, (void *)&ap, NULL) < 0) { printf("No response for RARP request\n"); return (-1); } bcopy(ap->arp_tpa, (char *)&myip, sizeof(myip)); #if 0 /* XXX - Can NOT assume this is our root server! */ bcopy(ap->arp_spa, (char *)&rootip, sizeof(rootip)); #endif free(pkt); /* Compute our "natural" netmask. */ if (IN_CLASSA(myip.s_addr)) netmask = IN_CLASSA_NET; else if (IN_CLASSB(myip.s_addr)) netmask = IN_CLASSB_NET; else netmask = IN_CLASSC_NET; d->myip = myip; return (0); } /* * Broadcast a RARP request (i.e. who knows who I am) */ static ssize_t rarpsend(struct iodesc *d, void *pkt, size_t len) { #ifdef RARP_DEBUG if (debug) printf("rarpsend: called\n"); #endif return (sendether(d, pkt, len, bcea, ETHERTYPE_REVARP)); } /* * Returns 0 if this is the packet we're waiting for * else -1 (and errno == 0) */ static ssize_t -rarprecv(struct iodesc *d, void **pkt, void **payload, time_t tleft) +rarprecv(struct iodesc *d, void **pkt, void **payload, time_t tleft, + void *extra) { ssize_t n; struct ether_arp *ap; void *ptr = NULL; uint16_t etype; /* host order */ #ifdef RARP_DEBUG if (debug) printf("rarprecv: "); #endif n = readether(d, &ptr, (void **)&ap, tleft, &etype); errno = 0; /* XXX */ if (n == -1 || n < sizeof(struct ether_arp)) { #ifdef RARP_DEBUG if (debug) printf("bad len=%d\n", n); #endif free(ptr); return (-1); } if (etype != ETHERTYPE_REVARP) { #ifdef RARP_DEBUG if (debug) printf("bad type=0x%x\n", etype); #endif free(ptr); return (-1); } if (ap->arp_hrd != htons(ARPHRD_ETHER) || ap->arp_pro != htons(ETHERTYPE_IP) || ap->arp_hln != sizeof(ap->arp_sha) || ap->arp_pln != sizeof(ap->arp_spa) ) { #ifdef RARP_DEBUG if (debug) printf("bad hrd/pro/hln/pln\n"); #endif free(ptr); return (-1); } if (ap->arp_op != htons(ARPOP_REVREPLY)) { #ifdef RARP_DEBUG if (debug) printf("bad op=0x%x\n", ntohs(ap->arp_op)); #endif free(ptr); return (-1); } /* Is the reply for our Ethernet address? */ if (bcmp(ap->arp_tha, d->myea, 6)) { #ifdef RARP_DEBUG if (debug) printf("unwanted address\n"); #endif free(ptr); return (-1); } /* We have our answer. */ #ifdef RARP_DEBUG if (debug) printf("got it\n"); #endif *pkt = ptr; *payload = ap; return (n); } Index: head/stand/libsa/rpc.c =================================================================== --- head/stand/libsa/rpc.c (revision 329338) +++ head/stand/libsa/rpc.c (revision 329339) @@ -1,433 +1,433 @@ /* $NetBSD: rpc.c,v 1.18 1998/01/23 19:27:45 thorpej Exp $ */ /* * Copyright (c) 1992 Regents of the University of California. * All rights reserved. * * This software was developed by the Computer Systems Engineering group * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and * contributed to Berkeley. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#) Header: rpc.c,v 1.12 93/09/28 08:31:56 leres Exp (LBL) */ #include __FBSDID("$FreeBSD$"); /* * RPC functions used by NFS and bootparams. * Note that bootparams requires the ability to find out the * address of the server from which its response has come. * This is supported by keeping the IP/UDP headers in the * buffer space provided by the caller. (See rpc_fromaddr) */ #include #include #include #include #include #include "rpcv2.h" #include "stand.h" #include "net.h" #include "netif.h" #include "rpc.h" struct auth_info { int32_t authtype; /* auth type */ u_int32_t authlen; /* auth length */ }; struct auth_unix { int32_t ua_time; int32_t ua_hostname; /* null */ int32_t ua_uid; int32_t ua_gid; int32_t ua_gidlist; /* null */ }; struct rpc_call { u_int32_t rp_xid; /* request transaction id */ int32_t rp_direction; /* call direction (0) */ u_int32_t rp_rpcvers; /* rpc version (2) */ u_int32_t rp_prog; /* program */ u_int32_t rp_vers; /* version */ u_int32_t rp_proc; /* procedure */ }; struct rpc_reply { u_int32_t rp_xid; /* request transaction id */ int32_t rp_direction; /* call direction (1) */ int32_t rp_astatus; /* accept status (0: accepted) */ union { u_int32_t rpu_errno; struct { struct auth_info rok_auth; u_int32_t rok_status; } rpu_rok; } rp_u; }; /* Local forwards */ -static ssize_t recvrpc(struct iodesc *, void **, void **, time_t); +static ssize_t recvrpc(struct iodesc *, void **, void **, time_t, void *); static int rpc_getport(struct iodesc *, n_long, n_long); int rpc_xid; int rpc_port = 0x400; /* predecrement */ /* * Make a rpc call; return length of answer * Note: Caller must leave room for headers. */ ssize_t rpc_call(struct iodesc *d, n_long prog, n_long vers, n_long proc, void *sdata, size_t slen, void **rdata, void **pkt) { ssize_t cc, rsize; struct auth_info *auth; struct rpc_call *call; struct rpc_reply *reply; char *send_head, *send_tail; void *ptr; n_long x; int port; /* host order */ #ifdef RPC_DEBUG if (debug) printf("rpc_call: prog=0x%x vers=%d proc=%d\n", prog, vers, proc); #endif port = rpc_getport(d, prog, vers); if (port == -1) return (-1); d->destport = htons(port); /* * Prepend authorization stuff and headers. * Note, must prepend things in reverse order. */ send_head = sdata; send_tail = (char *)sdata + slen; /* Auth verifier is always auth_null */ send_head -= sizeof(*auth); auth = (struct auth_info *)send_head; auth->authtype = htonl(RPCAUTH_NULL); auth->authlen = 0; /* Auth credentials: always auth unix (as root) */ send_head -= sizeof(struct auth_unix); bzero(send_head, sizeof(struct auth_unix)); send_head -= sizeof(*auth); auth = (struct auth_info *)send_head; auth->authtype = htonl(RPCAUTH_UNIX); auth->authlen = htonl(sizeof(struct auth_unix)); /* RPC call structure. */ send_head -= sizeof(*call); call = (struct rpc_call *)send_head; rpc_xid++; call->rp_xid = htonl(rpc_xid); call->rp_direction = htonl(RPC_CALL); call->rp_rpcvers = htonl(RPC_VER2); call->rp_prog = htonl(prog); call->rp_vers = htonl(vers); call->rp_proc = htonl(proc); ptr = NULL; cc = sendrecv(d, sendudp, send_head, send_tail - send_head, - recvrpc, &ptr, (void **)&reply); + recvrpc, &ptr, (void **)&reply, NULL); #ifdef RPC_DEBUG if (debug) printf("callrpc: cc=%zd\n", cc); #endif if (cc == -1) return (-1); if (cc <= sizeof(*reply)) { errno = EBADRPC; free(ptr); return (-1); } /* * Check the RPC reply status. * The xid, dir, astatus were already checked. */ auth = &reply->rp_u.rpu_rok.rok_auth; x = ntohl(auth->authlen); if (x != 0) { #ifdef RPC_DEBUG if (debug) printf("callrpc: reply auth != NULL\n"); #endif errno = EBADRPC; free(ptr); return (-1); } x = ntohl(reply->rp_u.rpu_rok.rok_status); if (x != 0) { printf("callrpc: error = %ld\n", (long)x); errno = EBADRPC; free(ptr); return (-1); } rsize = cc - sizeof(*reply); *rdata = (void *)((uintptr_t)reply + sizeof(*reply)); *pkt = ptr; return (rsize); } /* * Returns true if packet is the one we're waiting for. * This just checks the XID, direction, acceptance. * Remaining checks are done by callrpc */ static ssize_t -recvrpc(struct iodesc *d, void **pkt, void **payload, time_t tleft) +recvrpc(struct iodesc *d, void **pkt, void **payload, time_t tleft, void *extra) { void *ptr; struct rpc_reply *reply; ssize_t n; int x; errno = 0; #ifdef RPC_DEBUG if (debug) printf("recvrpc: called\n"); #endif ptr = NULL; n = readudp(d, &ptr, (void **)&reply, tleft); if (n <= (4 * 4)) { free(ptr); return (-1); } x = ntohl(reply->rp_xid); if (x != rpc_xid) { #ifdef RPC_DEBUG if (debug) printf("recvrpc: rp_xid %d != xid %d\n", x, rpc_xid); #endif free(ptr); return (-1); } x = ntohl(reply->rp_direction); if (x != RPC_REPLY) { #ifdef RPC_DEBUG if (debug) printf("recvrpc: rp_direction %d != REPLY\n", x); #endif free(ptr); return (-1); } x = ntohl(reply->rp_astatus); if (x != RPC_MSGACCEPTED) { errno = ntohl(reply->rp_u.rpu_errno); printf("recvrpc: reject, astat=%d, errno=%d\n", x, errno); free(ptr); return (-1); } *pkt = ptr; *payload = reply; /* Return data count (thus indicating success) */ return (n); } /* * Given a pointer to a reply just received, * dig out the IP address/port from the headers. */ void rpc_fromaddr(void *pkt, struct in_addr *addr, u_short *port) { struct hackhdr { /* Tail of IP header: just IP addresses */ n_long ip_src; n_long ip_dst; /* UDP header: */ u_int16_t uh_sport; /* source port */ u_int16_t uh_dport; /* destination port */ int16_t uh_ulen; /* udp length */ u_int16_t uh_sum; /* udp checksum */ /* RPC reply header: */ struct rpc_reply rpc; } *hhdr; hhdr = ((struct hackhdr *)pkt) - 1; addr->s_addr = hhdr->ip_src; *port = hhdr->uh_sport; } /* * RPC Portmapper cache */ #define PMAP_NUM 8 /* need at most 5 pmap entries */ int rpc_pmap_num; struct pmap_list { struct in_addr addr; /* server, net order */ u_int prog; /* host order */ u_int vers; /* host order */ int port; /* host order */ } rpc_pmap_list[PMAP_NUM]; /* * return port number in host order, or -1. * arguments are: * addr .. server, net order. * prog .. host order. * vers .. host order. */ int rpc_pmap_getcache(struct in_addr addr, u_int prog, u_int vers) { struct pmap_list *pl; for (pl = rpc_pmap_list; pl < &rpc_pmap_list[rpc_pmap_num]; pl++) { if (pl->addr.s_addr == addr.s_addr && pl->prog == prog && pl->vers == vers ) { return (pl->port); } } return (-1); } /* * arguments are: * addr .. server, net order. * prog .. host order. * vers .. host order. * port .. host order. */ void rpc_pmap_putcache(struct in_addr addr, u_int prog, u_int vers, int port) { struct pmap_list *pl; /* Don't overflow cache... */ if (rpc_pmap_num >= PMAP_NUM) { /* ... just re-use the last entry. */ rpc_pmap_num = PMAP_NUM - 1; #ifdef RPC_DEBUG printf("rpc_pmap_putcache: cache overflow\n"); #endif } pl = &rpc_pmap_list[rpc_pmap_num]; rpc_pmap_num++; /* Cache answer */ pl->addr = addr; pl->prog = prog; pl->vers = vers; pl->port = port; } /* * Request a port number from the port mapper. * Returns the port in host order. * prog and vers are host order. */ int rpc_getport(struct iodesc *d, n_long prog, n_long vers) { struct args { n_long prog; /* call program */ n_long vers; /* call version */ n_long proto; /* call protocol */ n_long port; /* call port (unused) */ } *args; struct res { n_long port; } *res; struct { n_long h[RPC_HEADER_WORDS]; struct args d; } sdata; void *pkt; ssize_t cc; int port; #ifdef RPC_DEBUG if (debug) printf("%s: prog=0x%x vers=%d\n", __func__, prog, vers); #endif /* This one is fixed forever. */ if (prog == PMAPPROG) { port = PMAPPORT; goto out; } /* Try for cached answer first */ port = rpc_pmap_getcache(d->destip, prog, vers); if (port != -1) goto out; args = &sdata.d; args->prog = htonl(prog); args->vers = htonl(vers); args->proto = htonl(IPPROTO_UDP); args->port = 0; pkt = NULL; cc = rpc_call(d, PMAPPROG, PMAPVERS, PMAPPROC_GETPORT, args, sizeof(*args), (void **)&res, &pkt); if (cc < sizeof(*res)) { printf("getport: %s", strerror(errno)); errno = EBADRPC; free(pkt); return (-1); } port = (int)ntohl(res->port); free(pkt); rpc_pmap_putcache(d->destip, prog, vers, port); out: #ifdef RPC_DEBUG if (debug) printf("%s: port=%u\n", __func__, port); #endif return (port); } Index: head/stand/libsa/tftp.c =================================================================== --- head/stand/libsa/tftp.c (revision 329338) +++ head/stand/libsa/tftp.c (revision 329339) @@ -1,791 +1,739 @@ /* $NetBSD: tftp.c,v 1.4 1997/09/17 16:57:07 drochner Exp $ */ /* * Copyright (c) 1996 * Matthias Drochner. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed for the NetBSD Project * by Matthias Drochner. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); /* * Simple TFTP implementation for libsa. * Assumes: * - socket descriptor (int) at open_file->f_devdata * - server host IP in global servip * Restrictions: * - read only * - lseek only with SEEK_SET or SEEK_CUR * - no big time differences between transfers ( #include #include #include #include #include #include #include "stand.h" #include "net.h" #include "netif.h" #include "tftp.h" struct tftp_handle; +struct tftprecv_extra; +static ssize_t recvtftp(struct iodesc *d, void **pkt, void **payload, + time_t tleft, void *recv_extra); static int tftp_open(const char *path, struct open_file *f); static int tftp_close(struct open_file *f); static int tftp_parse_oack(struct tftp_handle *h, char *buf, size_t len); static int tftp_read(struct open_file *f, void *buf, size_t size, size_t *resid); static int tftp_write(struct open_file *f, void *buf, size_t size, size_t *resid); static off_t tftp_seek(struct open_file *f, off_t offset, int where); static int tftp_set_blksize(struct tftp_handle *h, const char *str); static int tftp_stat(struct open_file *f, struct stat *sb); -static ssize_t sendrecv_tftp(struct tftp_handle *h, - ssize_t (*sproc)(struct iodesc *, void *, size_t), - void *sbuf, size_t ssize, - ssize_t (*rproc)(struct tftp_handle *h, void **, void **, time_t, unsigned short *), - void **, void **, unsigned short *rtype); struct fs_ops tftp_fsops = { "tftp", tftp_open, tftp_close, tftp_read, tftp_write, tftp_seek, tftp_stat, null_readdir }; extern struct in_addr servip; static int tftpport = 2000; static int is_open = 0; /* * The legacy TFTP_BLKSIZE value was SEGSIZE(512). * TFTP_REQUESTED_BLKSIZE of 1428 is (Ethernet MTU, less the TFTP, UDP and * IP header lengths). */ #define TFTP_REQUESTED_BLKSIZE 1428 /* * Choose a blksize big enough so we can test with Ethernet * Jumbo frames in the future. */ #define TFTP_MAX_BLKSIZE 9008 struct tftp_handle { struct iodesc *iodesc; int currblock; /* contents of lastdata */ int islastblock; /* flag */ int validsize; int off; char *path; /* saved for re-requests */ unsigned int tftp_blksize; unsigned long tftp_tsize; void *pkt; struct tftphdr *tftp_hdr; }; +struct tftprecv_extra { + struct tftp_handle *tftp_handle; + unsigned short rtype; /* Received type */ +}; + #define TFTP_MAX_ERRCODE EOPTNEG static const int tftperrors[TFTP_MAX_ERRCODE + 1] = { 0, /* ??? */ ENOENT, EPERM, ENOSPC, EINVAL, /* ??? */ EINVAL, /* ??? */ EEXIST, EINVAL, /* ??? */ EINVAL, /* Option negotiation failed. */ }; static int tftp_getnextblock(struct tftp_handle *h); /* send error message back. */ static void tftp_senderr(struct tftp_handle *h, u_short errcode, const char *msg) { struct { u_char header[HEADER_SIZE]; struct tftphdr t; u_char space[63]; /* +1 from t */ } __packed __aligned(4) wbuf; char *wtail; int len; len = strlen(msg); if (len > sizeof(wbuf.space)) len = sizeof(wbuf.space); wbuf.t.th_opcode = htons((u_short) ERROR); wbuf.t.th_code = htons(errcode); wtail = wbuf.t.th_msg; bcopy(msg, wtail, len); wtail[len] = '\0'; wtail += len + 1; sendudp(h->iodesc, &wbuf.t, wtail - (char *) &wbuf.t); } static void tftp_sendack(struct tftp_handle *h) { struct { u_char header[HEADER_SIZE]; struct tftphdr t; } __packed __aligned(4) wbuf; char *wtail; wbuf.t.th_opcode = htons((u_short) ACK); wtail = (char *) &wbuf.t.th_block; wbuf.t.th_block = htons((u_short) h->currblock); wtail += 2; sendudp(h->iodesc, &wbuf.t, wtail - (char *) &wbuf.t); } static ssize_t -recvtftp(struct tftp_handle *h, void **pkt, void **payload, time_t tleft, - unsigned short *rtype) +recvtftp(struct iodesc *d, void **pkt, void **payload, time_t tleft, + void *recv_extra) { - struct iodesc *d = h->iodesc; + struct tftprecv_extra *extra; + struct tftp_handle *h; struct tftphdr *t; + unsigned short *rtype; void *ptr = NULL; ssize_t len; errno = 0; + extra = (struct tftprecv_extra *)recv_extra; + h = extra->tftp_handle; len = readudp(d, &ptr, (void **)&t, tleft); if (len < 4) { free(ptr); return (-1); } - *rtype = ntohs(t->th_opcode); + extra->rtype = ntohs(t->th_opcode); switch (ntohs(t->th_opcode)) { case DATA: { int got; if (htons(t->th_block) != (u_short) d->xid) { /* * Expected block? */ free(ptr); return (-1); } if (d->xid == 1) { /* * First data packet from new port. */ struct udphdr *uh; uh = (struct udphdr *) t - 1; d->destport = uh->uh_sport; } /* else check uh_sport has not changed??? */ got = len - (t->th_data - (char *)t); *pkt = ptr; *payload = t; return (got); } case ERROR: if ((unsigned) ntohs(t->th_code) > TFTP_MAX_ERRCODE) { printf("illegal tftp error %d\n", ntohs(t->th_code)); errno = EIO; } else { #ifdef TFTP_DEBUG printf("tftp-error %d\n", ntohs(t->th_code)); #endif errno = tftperrors[ntohs(t->th_code)]; } free(ptr); return (-1); case OACK: { struct udphdr *uh; int tftp_oack_len; /* * Unexpected OACK. TFTP transfer already in progress. * Drop the pkt. */ if (d->xid != 1) { free(ptr); return (-1); } /* * Remember which port this OACK came from, because we need * to send the ACK or errors back to it. */ uh = (struct udphdr *) t - 1; d->destport = uh->uh_sport; /* Parse options ACK-ed by the server. */ tftp_oack_len = len - sizeof(t->th_opcode); if (tftp_parse_oack(h, t->th_u.tu_stuff, tftp_oack_len) != 0) { tftp_senderr(h, EOPTNEG, "Malformed OACK"); errno = EIO; free(ptr); return (-1); } *pkt = ptr; *payload = t; return (0); } default: #ifdef TFTP_DEBUG printf("tftp type %d not handled\n", ntohs(t->th_opcode)); #endif free(ptr); return (-1); } } /* send request, expect first block (or error) */ static int tftp_makereq(struct tftp_handle *h) { struct { u_char header[HEADER_SIZE]; struct tftphdr t; u_char space[FNAME_SIZE + 6]; } __packed __aligned(4) wbuf; + struct tftprecv_extra recv_extra; char *wtail; int l; ssize_t res; void *pkt; struct tftphdr *t; char *tftp_blksize = NULL; int blksize_l; - unsigned short rtype = 0; /* * Allow overriding default TFTP block size by setting * a tftp.blksize environment variable. */ if ((tftp_blksize = getenv("tftp.blksize")) != NULL) { tftp_set_blksize(h, tftp_blksize); } wbuf.t.th_opcode = htons((u_short) RRQ); wtail = wbuf.t.th_stuff; l = strlen(h->path); #ifdef TFTP_PREPEND_PATH if (l > FNAME_SIZE - (sizeof(TFTP_PREPEND_PATH) - 1)) return (ENAMETOOLONG); bcopy(TFTP_PREPEND_PATH, wtail, sizeof(TFTP_PREPEND_PATH) - 1); wtail += sizeof(TFTP_PREPEND_PATH) - 1; #else if (l > FNAME_SIZE) return (ENAMETOOLONG); #endif bcopy(h->path, wtail, l + 1); wtail += l + 1; bcopy("octet", wtail, 6); wtail += 6; bcopy("blksize", wtail, 8); wtail += 8; blksize_l = sprintf(wtail, "%d", h->tftp_blksize); wtail += blksize_l + 1; bcopy("tsize", wtail, 6); wtail += 6; bcopy("0", wtail, 2); wtail += 2; /* h->iodesc->myport = htons(--tftpport); */ h->iodesc->myport = htons(tftpport + (getsecs() & 0x3ff)); h->iodesc->destport = htons(IPPORT_TFTP); h->iodesc->xid = 1; /* expected block */ h->currblock = 0; h->islastblock = 0; h->validsize = 0; pkt = NULL; - res = sendrecv_tftp(h, &sendudp, &wbuf.t, wtail - (char *) &wbuf.t, - &recvtftp, &pkt, (void **)&t, &rtype); + recv_extra.tftp_handle = h; + res = sendrecv(h->iodesc, &sendudp, &wbuf.t, wtail - (char *) &wbuf.t, + (void *)&recvtftp, &pkt, (void **)&t, &recv_extra); if (res == -1) { free(pkt); return (errno); } free(h->pkt); h->pkt = pkt; h->tftp_hdr = t; - if (rtype == OACK) + if (recv_extra.rtype == OACK) return (tftp_getnextblock(h)); /* Server ignored our blksize request, revert to TFTP default. */ h->tftp_blksize = SEGSIZE; - switch (rtype) { + switch (recv_extra.rtype) { case DATA: { h->currblock = 1; h->validsize = res; h->islastblock = 0; if (res < h->tftp_blksize) { h->islastblock = 1; /* very short file */ tftp_sendack(h); } return (0); } case ERROR: default: return (errno); } } /* ack block, expect next */ static int tftp_getnextblock(struct tftp_handle *h) { struct { u_char header[HEADER_SIZE]; struct tftphdr t; } __packed __aligned(4) wbuf; + struct tftprecv_extra recv_extra; char *wtail; int res; void *pkt; struct tftphdr *t; - unsigned short rtype = 0; wbuf.t.th_opcode = htons((u_short) ACK); wtail = (char *) &wbuf.t.th_block; wbuf.t.th_block = htons((u_short) h->currblock); wtail += 2; h->iodesc->xid = h->currblock + 1; /* expected block */ pkt = NULL; - res = sendrecv_tftp(h, &sendudp, &wbuf.t, wtail - (char *) &wbuf.t, - &recvtftp, &pkt, (void **)&t, &rtype); + recv_extra.tftp_handle = h; + res = sendrecv(h->iodesc, &sendudp, &wbuf.t, wtail - (char *) &wbuf.t, + (void *)&recvtftp, &pkt, (void **)&t, &recv_extra); if (res == -1) { /* 0 is OK! */ free(pkt); return (errno); } free(h->pkt); h->pkt = pkt; h->tftp_hdr = t; h->currblock++; h->validsize = res; if (res < h->tftp_blksize) h->islastblock = 1; /* EOF */ if (h->islastblock == 1) { /* Send an ACK for the last block */ wbuf.t.th_block = htons((u_short) h->currblock); sendudp(h->iodesc, &wbuf.t, wtail - (char *)&wbuf.t); } return (0); } static int tftp_open(const char *path, struct open_file *f) { struct tftp_handle *tftpfile; struct iodesc *io; int res; size_t pathsize; const char *extraslash; if (netproto != NET_TFTP) return (EINVAL); if (f->f_dev->dv_type != DEVT_NET) return (EINVAL); if (is_open) return (EBUSY); tftpfile = (struct tftp_handle *) malloc(sizeof(*tftpfile)); if (!tftpfile) return (ENOMEM); memset(tftpfile, 0, sizeof(*tftpfile)); tftpfile->tftp_blksize = TFTP_REQUESTED_BLKSIZE; tftpfile->iodesc = io = socktodesc(*(int *) (f->f_devdata)); if (io == NULL) return (EINVAL); io->destip = servip; tftpfile->off = 0; pathsize = (strlen(rootpath) + 1 + strlen(path) + 1) * sizeof(char); tftpfile->path = malloc(pathsize); if (tftpfile->path == NULL) { free(tftpfile); return(ENOMEM); } if (rootpath[strlen(rootpath) - 1] == '/' || path[0] == '/') extraslash = ""; else extraslash = "/"; res = snprintf(tftpfile->path, pathsize, "%s%s%s", rootpath, extraslash, path); if (res < 0 || res > pathsize) { free(tftpfile->path); free(tftpfile); return(ENOMEM); } res = tftp_makereq(tftpfile); if (res) { free(tftpfile->path); free(tftpfile->pkt); free(tftpfile); return (res); } f->f_fsdata = (void *) tftpfile; is_open = 1; return (0); } static int tftp_read(struct open_file *f, void *addr, size_t size, size_t *resid /* out */) { struct tftp_handle *tftpfile; tftpfile = (struct tftp_handle *) f->f_fsdata; while (size > 0) { int needblock, count; twiddle(32); needblock = tftpfile->off / tftpfile->tftp_blksize + 1; if (tftpfile->currblock > needblock) { /* seek backwards */ tftp_senderr(tftpfile, 0, "No error: read aborted"); tftp_makereq(tftpfile); /* no error check, it worked * for open */ } while (tftpfile->currblock < needblock) { int res; res = tftp_getnextblock(tftpfile); if (res) { /* no answer */ #ifdef TFTP_DEBUG printf("tftp: read error\n"); #endif return (res); } if (tftpfile->islastblock) break; } if (tftpfile->currblock == needblock) { int offinblock, inbuffer; offinblock = tftpfile->off % tftpfile->tftp_blksize; inbuffer = tftpfile->validsize - offinblock; if (inbuffer < 0) { #ifdef TFTP_DEBUG printf("tftp: invalid offset %d\n", tftpfile->off); #endif return (EINVAL); } count = (size < inbuffer ? size : inbuffer); bcopy(tftpfile->tftp_hdr->th_data + offinblock, addr, count); addr = (char *)addr + count; tftpfile->off += count; size -= count; if ((tftpfile->islastblock) && (count == inbuffer)) break; /* EOF */ } else { #ifdef TFTP_DEBUG printf("tftp: block %d not found\n", needblock); #endif return (EINVAL); } } if (resid) *resid = size; return (0); } static int tftp_close(struct open_file *f) { struct tftp_handle *tftpfile; tftpfile = (struct tftp_handle *) f->f_fsdata; /* let it time out ... */ if (tftpfile) { free(tftpfile->path); free(tftpfile->pkt); free(tftpfile); } is_open = 0; return (0); } static int tftp_write(struct open_file *f __unused, void *start __unused, size_t size __unused, size_t *resid __unused /* out */) { return (EROFS); } static int tftp_stat(struct open_file *f, struct stat *sb) { struct tftp_handle *tftpfile; tftpfile = (struct tftp_handle *) f->f_fsdata; sb->st_mode = 0444 | S_IFREG; sb->st_nlink = 1; sb->st_uid = 0; sb->st_gid = 0; sb->st_size = (off_t) tftpfile->tftp_tsize; return (0); } static off_t tftp_seek(struct open_file *f, off_t offset, int where) { struct tftp_handle *tftpfile; tftpfile = (struct tftp_handle *) f->f_fsdata; switch (where) { case SEEK_SET: tftpfile->off = offset; break; case SEEK_CUR: tftpfile->off += offset; break; default: errno = EOFFSET; return (-1); } return (tftpfile->off); -} - -static ssize_t -sendrecv_tftp(struct tftp_handle *h, - ssize_t (*sproc)(struct iodesc *, void *, size_t), - void *sbuf, size_t ssize, - ssize_t (*rproc)(struct tftp_handle *, void **, void **, time_t, - unsigned short *), - void **pkt, void **payload, unsigned short *rtype) -{ - struct iodesc *d = h->iodesc; - ssize_t cc; - time_t t, t1, tleft; - -#ifdef TFTP_DEBUG - if (debug) - printf("sendrecv: called\n"); -#endif - - tleft = MINTMO; - t = t1 = getsecs(); - for (;;) { - if ((getsecs() - t) > MAXTMO) { - errno = ETIMEDOUT; - return -1; - } - - cc = (*sproc)(d, sbuf, ssize); - if (cc != -1 && cc < ssize) - panic("sendrecv: short write! (%zd < %zu)", - cc, ssize); - - if (cc == -1) { - /* Error on transmit; wait before retrying */ - while ((getsecs() - t1) < tleft); - t1 = getsecs(); - continue; - } - - t = t1 = getsecs(); -recvnext: - if ((getsecs() - t) > MAXTMO) { - errno = ETIMEDOUT; - return -1; - } - /* Try to get a packet and process it. */ - cc = (*rproc)(h, pkt, payload, tleft, rtype); - /* Return on data, EOF or real error. */ - if (cc != -1 || (errno != 0 && errno != ETIMEDOUT)) - return (cc); - if ((getsecs() - t1) < tleft) { - goto recvnext; - } - - /* Timed out or didn't get the packet we're waiting for */ - tleft += MINTMO; - if (tleft > (2 * MINTMO)) { - tleft = (2 * MINTMO); - } - t1 = getsecs(); - } } static int tftp_set_blksize(struct tftp_handle *h, const char *str) { char *endptr; int new_blksize; int ret = 0; if (h == NULL || str == NULL) return (ret); new_blksize = (unsigned int)strtol(str, &endptr, 0); /* * Only accept blksize value if it is numeric. * RFC2348 specifies that acceptable values are 8-65464. * Let's choose a limit less than MAXRSPACE. */ if (*endptr == '\0' && new_blksize >= 8 && new_blksize <= TFTP_MAX_BLKSIZE) { h->tftp_blksize = new_blksize; ret = 1; } return (ret); } /* * In RFC2347, the TFTP Option Acknowledgement package (OACK) * is used to acknowledge a client's option negotiation request. * The format of an OACK packet is: * +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+ * | opc | opt1 | 0 | value1 | 0 | optN | 0 | valueN | 0 | * +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+ * * opc * The opcode field contains a 6, for Option Acknowledgment. * * opt1 * The first option acknowledgment, copied from the original * request. * * value1 * The acknowledged value associated with the first option. If * and how this value may differ from the original request is * detailed in the specification for the option. * * optN, valueN * The final option/value acknowledgment pair. */ static int tftp_parse_oack(struct tftp_handle *h, char *buf, size_t len) { /* * We parse the OACK strings into an array * of name-value pairs. */ char *tftp_options[128] = { 0 }; char *val = buf; int i = 0; int option_idx = 0; int blksize_is_set = 0; int tsize = 0; unsigned int orig_blksize; while (option_idx < 128 && i < len) { if (buf[i] == '\0') { if (&buf[i] > val) { tftp_options[option_idx] = val; val = &buf[i] + 1; ++option_idx; } } ++i; } /* Save the block size we requested for sanity check later. */ orig_blksize = h->tftp_blksize; /* * Parse individual TFTP options. * * "blksize" is specified in RFC2348. * * "tsize" is specified in RFC2349. */ for (i = 0; i < option_idx; i += 2) { if (strcasecmp(tftp_options[i], "blksize") == 0) { if (i + 1 < option_idx) blksize_is_set = tftp_set_blksize(h, tftp_options[i + 1]); } else if (strcasecmp(tftp_options[i], "tsize") == 0) { if (i + 1 < option_idx) tsize = strtol(tftp_options[i + 1], (char **)NULL, 10); if (tsize != 0) h->tftp_tsize = tsize; } else { /* Do not allow any options we did not expect to be ACKed. */ printf("unexpected tftp option '%s'\n", tftp_options[i]); return (-1); } } if (!blksize_is_set) { /* * If TFTP blksize was not set, try defaulting * to the legacy TFTP blksize of SEGSIZE(512) */ h->tftp_blksize = SEGSIZE; } else if (h->tftp_blksize > orig_blksize) { /* * Server should not be proposing block sizes that * exceed what we said we can handle. */ printf("unexpected blksize %u\n", h->tftp_blksize); return (-1); } #ifdef TFTP_DEBUG printf("tftp_blksize: %u\n", h->tftp_blksize); printf("tftp_tsize: %lu\n", h->tftp_tsize); #endif return 0; }