Index: head/lib/libstand/bootp.c =================================================================== --- head/lib/libstand/bootp.c (revision 314947) +++ head/lib/libstand/bootp.c (revision 314948) @@ -1,758 +1,769 @@ /* $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 #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 *, size_t, time_t); 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 /* Fetch required bootp infomation */ void bootp(sock, flag) int sock; int flag; { struct iodesc *d; struct bootp *bp; struct { u_char header[HEADER_SIZE]; struct bootp wbootp; } wbuf; struct { u_char header[HEADER_SIZE]; struct bootp rbootp; } rbuf; #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; /* * If 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. */ if (flag & BOOTP_PXE) { bp->bp_vend[7] = TAG_CLASSID; bp->bp_vend[8] = 9; bcopy("PXEClient", &bp->bp_vend[9], 9); bp->bp_vend[18] = TAG_PARAM_REQ; bp->bp_vend[19] = 8; bp->bp_vend[20] = TAG_ROOTPATH; bp->bp_vend[21] = TAG_TFTP_SERVER; bp->bp_vend[22] = TAG_HOSTNAME; bp->bp_vend[23] = TAG_SWAPSERVER; bp->bp_vend[24] = TAG_GATEWAY; bp->bp_vend[25] = TAG_SUBNET_MASK; bp->bp_vend[26] = TAG_INTF_MTU; bp->bp_vend[27] = TAG_SERVERID; bp->bp_vend[28] = TAG_END; } else bp->bp_vend[7] = TAG_END; #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, &rbuf.rbootp, sizeof(rbuf.rbootp)) == -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(&rbuf.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); if (flag & BOOTP_PXE) { bp->bp_vend[25] = TAG_CLASSID; bp->bp_vend[26] = 9; bcopy("PXEClient", &bp->bp_vend[27], 9); bp->bp_vend[36] = TAG_END; } else bp->bp_vend[25] = TAG_END; expected_dhcpmsgtype = DHCPACK; if(sendrecv(d, bootpsend, bp, sizeof(*bp), bootprecv, &rbuf.rbootp, sizeof(rbuf.rbootp)) == -1) { printf("DHCPREQUEST failed\n"); return; } } #endif myip = d->myip = rbuf.rbootp.bp_yiaddr; servip = rbuf.rbootp.bp_siaddr; if(rootip.s_addr == INADDR_ANY) rootip = servip; bcopy(rbuf.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; } /* Transmit a bootp request */ static ssize_t bootpsend(d, pkt, len) 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(d, pkt, len, tleft) struct iodesc *d; void *pkt; size_t len; time_t tleft; { ssize_t n; struct bootp *bp; #ifdef BOOTP_DEBUGx if (debug) printf("bootp_recvoffer: called\n"); #endif n = readudp(d, pkt, len, tleft); if (n == -1 || n < sizeof(struct bootp) - BOOTP_VENDSIZE) goto bad; bp = (struct bootp *)pkt; #ifdef BOOTP_DEBUG if (debug) printf("bootprecv: checked. bp = 0x%lx, n = %d\n", (long)bp, (int)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) { if(vend_rfc1048(bp->bp_vend, sizeof(bp->bp_vend)) != 0) goto bad; } #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); return(n); bad: errno = 0; return (-1); } +int +dhcp_try_rfc1048(u_char *cp, u_int len) +{ + + expected_dhcpmsgtype = DHCPACK; + if (bcmp(vm_rfc1048, cp, sizeof(vm_rfc1048)) == 0) { + return (vend_rfc1048(cp, len)); + } + return (-1); +} + static int vend_rfc1048(cp, len) 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) { if ((val = getenv("dhcp.interface-mtu")) != NULL) { intf_mtu = (u_int)strtoul(val, NULL, 0); } else { 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)); } if (tag == TAG_TFTP_SERVER) { bcopy(cp, &tftpip.s_addr, sizeof(tftpip.s_addr)); } #endif cp += size; } return(0); } #ifdef BOOTP_VEND_CMU static void vend_cmu(cp) 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/lib/libstand/bootp.h =================================================================== --- head/lib/libstand/bootp.h (revision 314947) +++ head/lib/libstand/bootp.h (revision 314948) @@ -1,147 +1,153 @@ /* $NetBSD: bootp.h,v 1.4 1997/09/06 13:55:57 drochner Exp $ */ /* * Bootstrap Protocol (BOOTP). RFC951 and RFC1048. * * This file specifies the "implementation-independent" BOOTP protocol * information which is common to both client and server. * * Copyright 1988 by Carnegie Mellon. * * Permission to use, copy, modify, and distribute this program for any * purpose and without fee is hereby granted, provided that this copyright * and permission notice appear on all copies and supporting documentation, * the name of Carnegie Mellon not be used in advertising or publicity * pertaining to distribution of the program without specific prior * permission, and notice be given in supporting documentation that copying * and distribution is by permission of Carnegie Mellon and Stanford * University. Carnegie Mellon makes no representations about the * suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. * * $FreeBSD$ */ +#ifndef _BOOTP_H_ +#define _BOOTP_H_ struct bootp { unsigned char bp_op; /* packet opcode type */ unsigned char bp_htype; /* hardware addr type */ unsigned char bp_hlen; /* hardware addr length */ unsigned char bp_hops; /* gateway hops */ unsigned int bp_xid; /* transaction ID */ unsigned short bp_secs; /* seconds since boot began */ unsigned short bp_flags; struct in_addr bp_ciaddr; /* client IP address */ struct in_addr bp_yiaddr; /* 'your' IP address */ struct in_addr bp_siaddr; /* server IP address */ struct in_addr bp_giaddr; /* gateway IP address */ unsigned char bp_chaddr[16]; /* client hardware address */ unsigned char bp_sname[64]; /* server host name */ unsigned char bp_file[128]; /* boot file name */ #ifdef SUPPORT_DHCP #define BOOTP_VENDSIZE 312 #else #define BOOTP_VENDSIZE 64 #endif unsigned char bp_vend[BOOTP_VENDSIZE]; /* vendor-specific area */ }; /* * UDP port numbers, server and client. */ #define IPPORT_BOOTPS 67 #define IPPORT_BOOTPC 68 #define BOOTREPLY 2 #define BOOTREQUEST 1 /* * Vendor magic cookie (v_magic) for CMU */ #define VM_CMU "CMU" /* * Vendor magic cookie (v_magic) for RFC1048 */ #define VM_RFC1048 { 99, 130, 83, 99 } /* * RFC1048 tag values used to specify what information is being supplied in * the vendor field of the packet. */ #define TAG_PAD ((unsigned char) 0) #define TAG_SUBNET_MASK ((unsigned char) 1) #define TAG_TIME_OFFSET ((unsigned char) 2) #define TAG_GATEWAY ((unsigned char) 3) #define TAG_TIME_SERVER ((unsigned char) 4) #define TAG_NAME_SERVER ((unsigned char) 5) #define TAG_DOMAIN_SERVER ((unsigned char) 6) #define TAG_LOG_SERVER ((unsigned char) 7) #define TAG_COOKIE_SERVER ((unsigned char) 8) #define TAG_LPR_SERVER ((unsigned char) 9) #define TAG_IMPRESS_SERVER ((unsigned char) 10) #define TAG_RLP_SERVER ((unsigned char) 11) #define TAG_HOSTNAME ((unsigned char) 12) #define TAG_BOOTSIZE ((unsigned char) 13) #define TAG_DUMPFILE ((unsigned char) 14) #define TAG_DOMAINNAME ((unsigned char) 15) #define TAG_SWAPSERVER ((unsigned char) 16) #define TAG_ROOTPATH ((unsigned char) 17) #define TAG_INTF_MTU ((unsigned char) 26) #ifdef SUPPORT_DHCP #define TAG_REQ_ADDR ((unsigned char) 50) #define TAG_LEASETIME ((unsigned char) 51) #define TAG_OVERLOAD ((unsigned char) 52) #define TAG_DHCP_MSGTYPE ((unsigned char) 53) #define TAG_SERVERID ((unsigned char) 54) #define TAG_PARAM_REQ ((unsigned char) 55) #define TAG_MSG ((unsigned char) 56) #define TAG_MAXSIZE ((unsigned char) 57) #define TAG_T1 ((unsigned char) 58) #define TAG_T2 ((unsigned char) 59) #define TAG_CLASSID ((unsigned char) 60) #define TAG_CLIENTID ((unsigned char) 61) #define TAG_TFTP_SERVER ((unsigned char) 150) #endif #define TAG_END ((unsigned char) 255) #ifdef SUPPORT_DHCP #define DHCPDISCOVER 1 #define DHCPOFFER 2 #define DHCPREQUEST 3 #define DHCPDECLINE 4 #define DHCPACK 5 #define DHCPNAK 6 #define DHCPRELEASE 7 #endif /* * bootp flags */ #define BOOTP_NONE 0x0000 /* No flags */ #define BOOTP_PXE 0x0001 /* Booting from PXE. */ /* * "vendor" data permitted for CMU bootp clients. */ struct cmu_vend { unsigned char v_magic[4]; /* magic number */ unsigned int v_flags; /* flags/opcodes, etc. */ struct in_addr v_smask; /* Subnet mask */ struct in_addr v_dgate; /* Default gateway */ struct in_addr v_dns1, v_dns2; /* Domain name servers */ struct in_addr v_ins1, v_ins2; /* IEN-116 name servers */ struct in_addr v_ts1, v_ts2; /* Time servers */ unsigned char v_unused[25]; /* currently unused */ }; /* v_flags values */ #define VF_SMASK 1 /* Subnet mask field contains valid data */ + +int dhcp_try_rfc1048(u_char *cp, u_int len); + +#endif /* _BOOTP_H_ */ Index: head/sys/boot/i386/libi386/pxe.c =================================================================== --- head/sys/boot/i386/libi386/pxe.c (revision 314947) +++ head/sys/boot/i386/libi386/pxe.c (revision 314948) @@ -1,724 +1,749 @@ /*- * Copyright (c) 2000 Alfred Perlstein * Copyright (c) 2000 Paul Saab * Copyright (c) 2000 John Baldwin * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include "btxv86.h" #include "pxe.h" /* * Allocate the PXE buffers statically instead of sticking grimy fingers into * BTX's private data area. The scratch buffer is used to send information to * the PXE BIOS, and the data buffer is used to receive data from the PXE BIOS. */ #define PXE_BUFFER_SIZE 0x2000 #define PXE_TFTP_BUFFER_SIZE 512 static char scratch_buffer[PXE_BUFFER_SIZE]; static char data_buffer[PXE_BUFFER_SIZE]; static pxenv_t *pxenv_p = NULL; /* PXENV+ */ static pxe_t *pxe_p = NULL; /* !PXE */ static BOOTPLAYER bootplayer; /* PXE Cached information. */ static int pxe_debug = 0; static int pxe_sock = -1; static int pxe_opens = 0; void pxe_enable(void *pxeinfo); static void (*pxe_call)(int func); static void pxenv_call(int func); static void bangpxe_call(int func); static int pxe_init(void); static int pxe_strategy(void *devdata, int flag, daddr_t dblk, size_t size, char *buf, size_t *rsize); static int pxe_open(struct open_file *f, ...); static int pxe_close(struct open_file *f); static int pxe_print(int verbose); static void pxe_cleanup(void); static void pxe_setnfshandle(char *rootpath); static void pxe_perror(int error); static int pxe_netif_match(struct netif *nif, void *machdep_hint); static int pxe_netif_probe(struct netif *nif, void *machdep_hint); static void pxe_netif_init(struct iodesc *desc, void *machdep_hint); static int pxe_netif_get(struct iodesc *desc, void *pkt, size_t len, time_t timeout); static int pxe_netif_put(struct iodesc *desc, void *pkt, size_t len); static void pxe_netif_end(struct netif *nif); #ifdef OLD_NFSV2 int nfs_getrootfh(struct iodesc*, char*, u_char*); #else int nfs_getrootfh(struct iodesc*, char*, uint32_t*, u_char*); #endif extern struct netif_stats pxe_st[]; extern u_int16_t __bangpxeseg; extern u_int16_t __bangpxeoff; extern void __bangpxeentry(void); extern u_int16_t __pxenvseg; extern u_int16_t __pxenvoff; extern void __pxenventry(void); +extern struct in_addr servip; struct netif_dif pxe_ifs[] = { /* dif_unit dif_nsel dif_stats dif_private */ {0, 1, &pxe_st[0], 0} }; struct netif_stats pxe_st[NENTS(pxe_ifs)]; struct netif_driver pxenetif = { "pxenet", pxe_netif_match, pxe_netif_probe, pxe_netif_init, pxe_netif_get, pxe_netif_put, pxe_netif_end, pxe_ifs, NENTS(pxe_ifs) }; struct netif_driver *netif_drivers[] = { &pxenetif, NULL }; struct devsw pxedisk = { "pxe", DEVT_NET, pxe_init, pxe_strategy, pxe_open, pxe_close, noioctl, pxe_print, pxe_cleanup }; /* * This function is called by the loader to enable PXE support if we * are booted by PXE. The passed in pointer is a pointer to the PXENV+ * structure. */ void pxe_enable(void *pxeinfo) { pxenv_p = (pxenv_t *)pxeinfo; pxe_p = (pxe_t *)PTOV(pxenv_p->PXEPtr.segment * 16 + pxenv_p->PXEPtr.offset); pxe_call = NULL; } /* * return true if pxe structures are found/initialized, * also figures out our IP information via the pxe cached info struct */ static int pxe_init(void) { t_PXENV_GET_CACHED_INFO *gci_p; int counter; uint8_t checksum; uint8_t *checkptr; if (pxenv_p == NULL) return (0); /* look for "PXENV+" */ if (bcmp((void *)pxenv_p->Signature, S_SIZE("PXENV+"))) { pxenv_p = NULL; return (0); } /* make sure the size is something we can handle */ if (pxenv_p->Length > sizeof(*pxenv_p)) { printf("PXENV+ structure too large, ignoring\n"); pxenv_p = NULL; return (0); } /* * do byte checksum: * add up each byte in the structure, the total should be 0 */ checksum = 0; checkptr = (uint8_t *) pxenv_p; for (counter = 0; counter < pxenv_p->Length; counter++) checksum += *checkptr++; if (checksum != 0) { printf("PXENV+ structure failed checksum, ignoring\n"); pxenv_p = NULL; return (0); } /* * PXENV+ passed, so use that if !PXE is not available or * the checksum fails. */ pxe_call = pxenv_call; if (pxenv_p->Version >= 0x0200) { for (;;) { if (bcmp((void *)pxe_p->Signature, S_SIZE("!PXE"))) { pxe_p = NULL; break; } checksum = 0; checkptr = (uint8_t *)pxe_p; for (counter = 0; counter < pxe_p->StructLength; counter++) checksum += *checkptr++; if (checksum != 0) { pxe_p = NULL; break; } pxe_call = bangpxe_call; break; } } printf("\nPXE version %d.%d, real mode entry point ", (uint8_t) (pxenv_p->Version >> 8), (uint8_t) (pxenv_p->Version & 0xFF)); if (pxe_call == bangpxe_call) printf("@%04x:%04x\n", pxe_p->EntryPointSP.segment, pxe_p->EntryPointSP.offset); else printf("@%04x:%04x\n", pxenv_p->RMEntry.segment, pxenv_p->RMEntry.offset); gci_p = (t_PXENV_GET_CACHED_INFO *) scratch_buffer; bzero(gci_p, sizeof(*gci_p)); gci_p->PacketType = PXENV_PACKET_TYPE_BINL_REPLY; pxe_call(PXENV_GET_CACHED_INFO); if (gci_p->Status != 0) { pxe_perror(gci_p->Status); pxe_p = NULL; return (0); } bcopy(PTOV((gci_p->Buffer.segment << 4) + gci_p->Buffer.offset), &bootplayer, gci_p->BufferSize); return (1); } static int pxe_strategy(void *devdata, int flag, daddr_t dblk, size_t size, char *buf, size_t *rsize) { return (EIO); } static int pxe_open(struct open_file *f, ...) { va_list args; char *devname; /* Device part of file name (or NULL). */ char temp[FNAME_SIZE]; int error = 0; int i; va_start(args, f); devname = va_arg(args, char*); va_end(args); /* On first open, do netif open, mount, etc. */ if (pxe_opens == 0) { /* Find network interface. */ if (pxe_sock < 0) { pxe_sock = netif_open(devname); if (pxe_sock < 0) { printf("pxe_open: netif_open() failed\n"); return (ENXIO); } if (pxe_debug) printf("pxe_open: netif_open() succeeded\n"); + + if (socktodesc(pxe_sock) == NULL) { + printf("pxe_open: bad socket %d\n", pxe_sock); + return (ENXIO); + } + } if (rootip.s_addr == 0) { /* - * Do a bootp/dhcp request to find out where our + * Try to extract the RFC1048 data from PXE. + * If fail do a bootp/dhcp request to find out where our * NFS/TFTP server is. Even if we dont get back * the proper information, fall back to the server * which brought us to life and a default rootpath. */ - bootp(pxe_sock, BOOTP_PXE); + + if (dhcp_try_rfc1048(bootplayer.vendor.d, BOOTP_DHCPVEND) < 0) { + if (pxe_debug) + printf("pxe_open: no RFC1048 data in PXE Cache\n"); + bootp(pxe_sock, BOOTP_PXE); + } else if (pxe_debug) { + printf("pxe_open: loaded RFC1048 data from PXE Cache\n"); + } + if (rootip.s_addr == 0) rootip.s_addr = bootplayer.sip; + if (gateip.s_addr == 0) + gateip.s_addr = bootplayer.gip; + if (myip.s_addr == 0) + myip.s_addr = bootplayer.yip; + if (servip.s_addr == 0) + servip = rootip; netproto = NET_NFS; if (tftpip.s_addr != 0) { netproto = NET_TFTP; rootip.s_addr = tftpip.s_addr; } if (netproto == NET_NFS && !rootpath[0]) strcpy(rootpath, PXENFSROOTPATH); for (i = 0; rootpath[i] != '\0' && i < FNAME_SIZE; i++) if (rootpath[i] == ':') break; if (i && i != FNAME_SIZE && rootpath[i] == ':') { rootpath[i++] = '\0'; if (inet_addr(&rootpath[0]) != INADDR_NONE) rootip.s_addr = inet_addr(&rootpath[0]); bcopy(&rootpath[i], &temp[0], strlen(&rootpath[i]) + 1); bcopy(&temp[0], &rootpath[0], strlen(&rootpath[i]) + 1); } setenv("boot.netif.ip", inet_ntoa(myip), 1); setenv("boot.netif.netmask", intoa(netmask), 1); setenv("boot.netif.gateway", inet_ntoa(gateip), 1); setenv("boot.netif.server", inet_ntoa(rootip), 1); if (bootplayer.Hardware == ETHER_TYPE) { sprintf(temp, "%6D", bootplayer.CAddr, ":"); setenv("boot.netif.hwaddr", temp, 1); } if (intf_mtu != 0) { char mtu[16]; sprintf(mtu, "%u", intf_mtu); setenv("boot.netif.mtu", mtu, 1); } printf("pxe_open: server addr: %s\n", inet_ntoa(rootip)); printf("pxe_open: server path: %s\n", rootpath); printf("pxe_open: gateway ip: %s\n", inet_ntoa(gateip)); + printf("pxe_open: my ip: %s\n", inet_ntoa(myip)); + printf("pxe_open: netmask: %s\n", intoa(netmask)); + printf("pxe_open: servip: %s\n", inet_ntoa(servip)); if (netproto == NET_TFTP) { setenv("boot.tftproot.server", inet_ntoa(rootip), 1); setenv("boot.tftproot.path", rootpath, 1); } else if (netproto == NET_NFS) { setenv("boot.nfsroot.server", inet_ntoa(rootip), 1); setenv("boot.nfsroot.path", rootpath, 1); } setenv("dhcp.host-name", hostname, 1); setenv("pxeboot.ip", inet_ntoa(myip), 1); if (bootplayer.Hardware == ETHER_TYPE) { sprintf(temp, "%6D", bootplayer.CAddr, ":"); setenv("pxeboot.hwaddr", temp, 1); } } } pxe_opens++; f->f_devdata = &pxe_sock; return (error); } static int pxe_close(struct open_file *f) { #ifdef PXE_DEBUG if (pxe_debug) printf("pxe_close: opens=%d\n", pxe_opens); #endif /* On last close, do netif close, etc. */ f->f_devdata = NULL; /* Extra close call? */ if (pxe_opens <= 0) return (0); pxe_opens--; /* Not last close? */ if (pxe_opens > 0) return (0); if (netproto == NET_NFS) { /* get an NFS filehandle for our root filesystem */ pxe_setnfshandle(rootpath); } if (pxe_sock >= 0) { #ifdef PXE_DEBUG if (pxe_debug) printf("pxe_close: calling netif_close()\n"); #endif netif_close(pxe_sock); pxe_sock = -1; } return (0); } static int pxe_print(int verbose) { char line[255]; if (pxe_call == NULL) return (0); printf("%s devices:", pxedisk.dv_name); if (pager_output("\n") != 0) return (1); if (verbose) { snprintf(line, sizeof(line), " pxe0: %s:%s\n", inet_ntoa(rootip), rootpath); } else { snprintf(line, sizeof(line), " pxe0:\n"); } return (pager_output(line)); } static void pxe_cleanup(void) { #ifdef PXE_DEBUG t_PXENV_UNLOAD_STACK *unload_stack_p = (t_PXENV_UNLOAD_STACK *)scratch_buffer; t_PXENV_UNDI_SHUTDOWN *undi_shutdown_p = (t_PXENV_UNDI_SHUTDOWN *)scratch_buffer; #endif if (pxe_call == NULL) return; pxe_call(PXENV_UNDI_SHUTDOWN); #ifdef PXE_DEBUG if (pxe_debug && undi_shutdown_p->Status != 0) printf("pxe_cleanup: UNDI_SHUTDOWN failed %x\n", undi_shutdown_p->Status); #endif pxe_call(PXENV_UNLOAD_STACK); #ifdef PXE_DEBUG if (pxe_debug && unload_stack_p->Status != 0) printf("pxe_cleanup: UNLOAD_STACK failed %x\n", unload_stack_p->Status); #endif } void pxe_perror(int err) { return; } /* * Reach inside the libstand NFS code and dig out an NFS handle * for the root filesystem. */ #ifdef OLD_NFSV2 struct nfs_iodesc { struct iodesc *iodesc; off_t off; u_char fh[NFS_FHSIZE]; /* structure truncated here */ }; extern struct nfs_iodesc nfs_root_node; extern int rpc_port; static void pxe_rpcmountcall() { struct iodesc *d; int error; if (!(d = socktodesc(pxe_sock))) return; d->myport = htons(--rpc_port); d->destip = rootip; if ((error = nfs_getrootfh(d, rootpath, nfs_root_node.fh)) != 0) printf("NFS MOUNT RPC error: %d\n", error); nfs_root_node.iodesc = d; } static void pxe_setnfshandle(char *rootpath) { int i; u_char *fh; char buf[2 * NFS_FHSIZE + 3], *cp; /* * If NFS files were never opened, we need to do mount call * ourselves. Use nfs_root_node.iodesc as flag indicating * previous NFS usage. */ if (nfs_root_node.iodesc == NULL) pxe_rpcmountcall(); fh = &nfs_root_node.fh[0]; buf[0] = 'X'; cp = &buf[1]; for (i = 0; i < NFS_FHSIZE; i++, cp += 2) sprintf(cp, "%02x", fh[i]); sprintf(cp, "X"); setenv("boot.nfsroot.nfshandle", buf, 1); } #else /* !OLD_NFSV2 */ #define NFS_V3MAXFHSIZE 64 struct nfs_iodesc { struct iodesc *iodesc; off_t off; uint32_t fhsize; u_char fh[NFS_V3MAXFHSIZE]; /* structure truncated */ }; extern struct nfs_iodesc nfs_root_node; extern int rpc_port; static void pxe_rpcmountcall() { struct iodesc *d; int error; if (!(d = socktodesc(pxe_sock))) return; d->myport = htons(--rpc_port); d->destip = rootip; if ((error = nfs_getrootfh(d, rootpath, &nfs_root_node.fhsize, nfs_root_node.fh)) != 0) { printf("NFS MOUNT RPC error: %d\n", error); nfs_root_node.fhsize = 0; } nfs_root_node.iodesc = d; } static void pxe_setnfshandle(char *rootpath) { int i; u_char *fh; char buf[2 * NFS_V3MAXFHSIZE + 3], *cp; /* * If NFS files were never opened, we need to do mount call * ourselves. Use nfs_root_node.iodesc as flag indicating * previous NFS usage. */ if (nfs_root_node.iodesc == NULL) pxe_rpcmountcall(); fh = &nfs_root_node.fh[0]; buf[0] = 'X'; cp = &buf[1]; for (i = 0; i < nfs_root_node.fhsize; i++, cp += 2) sprintf(cp, "%02x", fh[i]); sprintf(cp, "X"); setenv("boot.nfsroot.nfshandle", buf, 1); sprintf(buf, "%d", nfs_root_node.fhsize); setenv("boot.nfsroot.nfshandlelen", buf, 1); } #endif /* OLD_NFSV2 */ void pxenv_call(int func) { #ifdef PXE_DEBUG if (pxe_debug) printf("pxenv_call %x\n", func); #endif bzero(&v86, sizeof(v86)); bzero(data_buffer, sizeof(data_buffer)); __pxenvseg = pxenv_p->RMEntry.segment; __pxenvoff = pxenv_p->RMEntry.offset; v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS; v86.es = VTOPSEG(scratch_buffer); v86.edi = VTOPOFF(scratch_buffer); v86.addr = (VTOPSEG(__pxenventry) << 16) | VTOPOFF(__pxenventry); v86.ebx = func; v86int(); v86.ctl = V86_FLAGS; } void bangpxe_call(int func) { #ifdef PXE_DEBUG if (pxe_debug) printf("bangpxe_call %x\n", func); #endif bzero(&v86, sizeof(v86)); bzero(data_buffer, sizeof(data_buffer)); __bangpxeseg = pxe_p->EntryPointSP.segment; __bangpxeoff = pxe_p->EntryPointSP.offset; v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS; v86.edx = VTOPSEG(scratch_buffer); v86.eax = VTOPOFF(scratch_buffer); v86.addr = (VTOPSEG(__bangpxeentry) << 16) | VTOPOFF(__bangpxeentry); v86.ebx = func; v86int(); v86.ctl = V86_FLAGS; } time_t getsecs(void) { time_t n = 0; time(&n); return n; } static int pxe_netif_match(struct netif *nif, void *machdep_hint) { return 1; } static int pxe_netif_probe(struct netif *nif, void *machdep_hint) { t_PXENV_UDP_OPEN *udpopen_p = (t_PXENV_UDP_OPEN *)scratch_buffer; if (pxe_call == NULL) return -1; bzero(udpopen_p, sizeof(*udpopen_p)); udpopen_p->src_ip = bootplayer.yip; pxe_call(PXENV_UDP_OPEN); if (udpopen_p->status != 0) { printf("pxe_netif_probe: failed %x\n", udpopen_p->status); return -1; } return 0; } static void pxe_netif_end(struct netif *nif) { t_PXENV_UDP_CLOSE *udpclose_p = (t_PXENV_UDP_CLOSE *)scratch_buffer; bzero(udpclose_p, sizeof(*udpclose_p)); pxe_call(PXENV_UDP_CLOSE); if (udpclose_p->status != 0) printf("pxe_end failed %x\n", udpclose_p->status); } static void pxe_netif_init(struct iodesc *desc, void *machdep_hint) { int i; for (i = 0; i < 6; ++i) desc->myea[i] = bootplayer.CAddr[i]; desc->xid = bootplayer.ident; } static int pxe_netif_get(struct iodesc *desc, void *pkt, size_t len, time_t timeout) { return len; } static int pxe_netif_put(struct iodesc *desc, void *pkt, size_t len) { return len; } ssize_t sendudp(struct iodesc *h, void *pkt, size_t len) { t_PXENV_UDP_WRITE *udpwrite_p = (t_PXENV_UDP_WRITE *)scratch_buffer; bzero(udpwrite_p, sizeof(*udpwrite_p)); udpwrite_p->ip = h->destip.s_addr; udpwrite_p->dst_port = h->destport; udpwrite_p->src_port = h->myport; udpwrite_p->buffer_size = len; udpwrite_p->buffer.segment = VTOPSEG(pkt); udpwrite_p->buffer.offset = VTOPOFF(pkt); if (netmask == 0 || SAMENET(myip, h->destip, netmask)) udpwrite_p->gw = 0; else udpwrite_p->gw = gateip.s_addr; pxe_call(PXENV_UDP_WRITE); #if 0 /* XXX - I dont know why we need this. */ delay(1000); #endif if (udpwrite_p->status != 0) { /* XXX: This happens a lot. It shouldn't. */ if (udpwrite_p->status != 1) printf("sendudp failed %x\n", udpwrite_p->status); return -1; } return len; } ssize_t readudp(struct iodesc *h, void *pkt, size_t len, time_t timeout) { t_PXENV_UDP_READ *udpread_p = (t_PXENV_UDP_READ *)scratch_buffer; struct udphdr *uh = NULL; uh = (struct udphdr *) pkt - 1; bzero(udpread_p, sizeof(*udpread_p)); udpread_p->dest_ip = h->myip.s_addr; udpread_p->d_port = h->myport; udpread_p->buffer_size = len; udpread_p->buffer.segment = VTOPSEG(data_buffer); udpread_p->buffer.offset = VTOPOFF(data_buffer); pxe_call(PXENV_UDP_READ); #if 0 /* XXX - I dont know why we need this. */ delay(1000); #endif if (udpread_p->status != 0) { /* XXX: This happens a lot. It shouldn't. */ if (udpread_p->status != 1) printf("readudp failed %x\n", udpread_p->status); return -1; } bcopy(data_buffer, pkt, udpread_p->buffer_size); uh->uh_sport = udpread_p->s_port; return udpread_p->buffer_size; }