The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/nfs/nfs_bootdhcp.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*      $NetBSD: nfs_bootdhcp.c,v 1.32.2.1 2007/05/13 10:30:54 jdc Exp $        */
    2 
    3 /*-
    4  * Copyright (c) 1995, 1997 The NetBSD Foundation, Inc.
    5  * All rights reserved.
    6  *
    7  * This code is derived from software contributed to The NetBSD Foundation
    8  * by Adam Glass and Gordon W. Ross.
    9  *
   10  * Redistribution and use in source and binary forms, with or without
   11  * modification, are permitted provided that the following conditions
   12  * are met:
   13  * 1. Redistributions of source code must retain the above copyright
   14  *    notice, this list of conditions and the following disclaimer.
   15  * 2. Redistributions in binary form must reproduce the above copyright
   16  *    notice, this list of conditions and the following disclaimer in the
   17  *    documentation and/or other materials provided with the distribution.
   18  * 3. All advertising materials mentioning features or use of this software
   19  *    must display the following acknowledgement:
   20  *      This product includes software developed by the NetBSD
   21  *      Foundation, Inc. and its contributors.
   22  * 4. Neither the name of The NetBSD Foundation nor the names of its
   23  *    contributors may be used to endorse or promote products derived
   24  *    from this software without specific prior written permission.
   25  *
   26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
   27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
   28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
   29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
   30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   36  * POSSIBILITY OF SUCH DAMAGE.
   37  */
   38 
   39 /*
   40  * Support for NFS diskless booting with BOOTP (RFC951, RFC1048)
   41  *
   42  * History:
   43  *
   44  * Tor Egge developed the initial version of this code based on
   45  * the Sun RPC/bootparam sources nfs_boot.c and krpc_subr.c and
   46  * submitted that work to NetBSD as bugreport "kern/2351" on
   47  * 29 Apr 1996.
   48  *
   49  * Gordon Ross reorganized Tor's version into this form and
   50  * integrated it into the NetBSD sources during Aug 1997.
   51  */
   52 
   53 #include <sys/cdefs.h>
   54 __KERNEL_RCSID(0, "$NetBSD: nfs_bootdhcp.c,v 1.32.2.1 2007/05/13 10:30:54 jdc Exp $");
   55 
   56 #include "opt_nfs_boot.h"
   57 #include "opt_tftproot.h"
   58 
   59 #include <sys/param.h>
   60 #include <sys/systm.h>
   61 #include <sys/kernel.h>
   62 #include <sys/device.h>
   63 #include <sys/ioctl.h>
   64 #include <sys/proc.h>
   65 #include <sys/mount.h>
   66 #include <sys/mbuf.h>
   67 #include <sys/reboot.h>
   68 #include <sys/socket.h>
   69 #include <sys/socketvar.h>
   70 
   71 #include <net/if.h>
   72 #include <net/if_types.h>
   73 #include <net/if_arp.h>         /* ARPHRD_ETHER, etc. */
   74 #include <net/if_dl.h>
   75 #include <net/if_ether.h>
   76 #include <net/route.h>
   77 
   78 #include <netinet/in.h>
   79 #include <netinet/if_inarp.h>
   80 
   81 #include <nfs/rpcv2.h>
   82 
   83 #include <nfs/nfsproto.h>
   84 #include <nfs/nfs.h>
   85 #include <nfs/nfsmount.h>
   86 #include <nfs/nfsdiskless.h>
   87 
   88 /*
   89  * There are two implementations of NFS diskless boot.
   90  * This implementation uses BOOTP (RFC951, RFC1048), and
   91  * the other uses Sun RPC/bootparams (nfs_bootparam.c).
   92  *
   93  * This method gets everything it needs with one BOOTP
   94  * request and reply.  Note that this actually uses only
   95  * the old BOOTP functionality subset of DHCP.  It is not
   96  * clear that DHCP provides any advantage over BOOTP for
   97  * diskless boot.  DHCP allows the server to assign an IP
   98  * address without any a-priori knowledge of the client,
   99  * but we require that the server has a-priori knowledge
  100  * of the client so it can export our (unique) NFS root.
  101  * Given that the server needs a-priori knowledge about
  102  * the client anyway, it might as well assign a fixed IP
  103  * address for the client and support BOOTP.
  104  *
  105  * On the other hand, disk-FULL clients may use DHCP, but
  106  * in that case the DHCP client should be user-mode code,
  107  * and has no bearing on the code below. -gwr
  108  */
  109 
  110 /* Begin stuff from bootp.h */
  111 /* Definitions from RFC951 */
  112 #define BP_CHADDR_LEN    16
  113 #define BP_SNAME_LEN     64
  114 #define BP_FILE_LEN     128
  115 #define BP_VEND_LEN      64
  116 struct bootp {
  117         u_int8_t        bp_op;          /* packet opcode type */
  118         u_int8_t        bp_htype;       /* hardware addr type */
  119         u_int8_t        bp_hlen;        /* hardware addr length */
  120         u_int8_t        bp_hops;        /* gateway hops */
  121         u_int32_t       bp_xid;         /* transaction ID */
  122         u_int16_t       bp_secs;        /* seconds since boot began */
  123         u_int16_t       bp_flags;       /* RFC1532 broadcast, etc. */
  124         struct in_addr  bp_ciaddr;      /* client IP address */
  125         struct in_addr  bp_yiaddr;      /* 'your' IP address */
  126         struct in_addr  bp_siaddr;      /* server IP address */
  127         struct in_addr  bp_giaddr;      /* gateway IP address */
  128         u_int8_t bp_chaddr[BP_CHADDR_LEN]; /* client hardware address */
  129         char    bp_sname[BP_SNAME_LEN]; /* server host name */
  130         char    bp_file[BP_FILE_LEN];   /* boot file name */
  131         u_int8_t bp_vend[BP_VEND_LEN];  /* RFC1048 options */
  132         /*
  133          * Note that BOOTP packets are allowed to be longer
  134          * (see RFC 1532 sect. 2.1) and common practice is to
  135          * allow the option data in bp_vend to extend into the
  136          * additional space provided in longer packets.
  137          */
  138 };
  139 
  140 #define IPPORT_BOOTPS 67
  141 #define IPPORT_BOOTPC 68
  142 
  143 #define BOOTREQUEST             1
  144 #define BOOTREPLY               2
  145 
  146 /*
  147  * Is this available from the sockaddr_dl somehow?
  148  * Perhaps (struct arphdr)->ar_hrd = ARPHRD_ETHER?
  149  * The interface has ->if_type but not the ARP fmt.
  150  */
  151 #define HTYPE_ETHERNET          1
  152 #define HTYPE_IEEE802           6
  153 
  154 /*
  155  * Vendor magic cookie (v_magic) for RFC1048
  156  */
  157 static const u_int8_t vm_rfc1048[4] = { 99, 130, 83, 99 };
  158 
  159 /*
  160  * Tag values used to specify what information is being supplied in
  161  * the vendor (options) data area of the packet.
  162  */
  163 /* RFC 1048 */
  164 #define TAG_END                 ((unsigned char) 255)
  165 #define TAG_PAD                 ((unsigned char)   0)
  166 #define TAG_SUBNET_MASK         ((unsigned char)   1)
  167 #define TAG_TIME_OFFSET         ((unsigned char)   2)
  168 #define TAG_GATEWAY             ((unsigned char)   3)
  169 #define TAG_TIME_SERVER         ((unsigned char)   4)
  170 #define TAG_NAME_SERVER         ((unsigned char)   5)
  171 #define TAG_DOMAIN_SERVER       ((unsigned char)   6)
  172 #define TAG_LOG_SERVER          ((unsigned char)   7)
  173 #define TAG_COOKIE_SERVER       ((unsigned char)   8)
  174 #define TAG_LPR_SERVER          ((unsigned char)   9)
  175 #define TAG_IMPRESS_SERVER      ((unsigned char)  10)
  176 #define TAG_RLP_SERVER          ((unsigned char)  11)
  177 #define TAG_HOST_NAME           ((unsigned char)  12)
  178 #define TAG_BOOT_SIZE           ((unsigned char)  13)
  179 /* RFC 1395 */
  180 #define TAG_DUMP_FILE           ((unsigned char)  14)
  181 #define TAG_DOMAIN_NAME         ((unsigned char)  15)
  182 #define TAG_SWAP_SERVER         ((unsigned char)  16)
  183 #define TAG_ROOT_PATH           ((unsigned char)  17)
  184 /* End of stuff from bootp.h */
  185 
  186 #ifdef NFS_BOOT_DHCP
  187 #define TAG_REQ_ADDR            ((unsigned char)  50)
  188 #define TAG_LEASETIME           ((unsigned char)  51)
  189 #define TAG_OVERLOAD            ((unsigned char)  52)
  190 #define TAG_DHCP_MSGTYPE        ((unsigned char)  53)
  191 #define TAG_SERVERID            ((unsigned char)  54)
  192 #define TAG_PARAM_REQ           ((unsigned char)  55)
  193 #define TAG_MSG                 ((unsigned char)  56)
  194 #define TAG_MAXSIZE             ((unsigned char)  57)
  195 #define TAG_T1                  ((unsigned char)  58)
  196 #define TAG_T2                  ((unsigned char)  59)
  197 #define TAG_CLASSID             ((unsigned char)  60)
  198 #define TAG_CLIENTID            ((unsigned char)  61)
  199 #endif
  200 
  201 #ifdef NFS_BOOT_DHCP
  202 #define DHCPDISCOVER 1
  203 #define DHCPOFFER 2
  204 #define DHCPREQUEST 3
  205 #define DHCPDECLINE 4
  206 #define DHCPACK 5
  207 #define DHCPNAK 6
  208 #define DHCPRELEASE 7
  209 #endif
  210 
  211 #ifdef NFS_BOOT_DHCP
  212 #define BOOTP_SIZE_MAX  (sizeof(struct bootp)+312-64)
  213 #else
  214 /*
  215  * The "extended" size is somewhat arbitrary, but is
  216  * constrained by the maximum message size specified
  217  * by RFC1533 (567 total).  This value increases the
  218  * space for options from 64 bytes to 256 bytes.
  219  */
  220 #define BOOTP_SIZE_MAX  (sizeof(struct bootp)+256-64)
  221 #endif
  222 #define BOOTP_SIZE_MIN  (sizeof(struct bootp))
  223 
  224 /* Convenience macro */
  225 #define INTOHL(ina) ((u_int32_t)ntohl((ina).s_addr))
  226 
  227 static int bootpc_call __P((struct nfs_diskless *, struct lwp *));
  228 static void bootp_extract __P((struct bootp *, int, struct nfs_diskless *));
  229 
  230 #ifdef  DEBUG_NFS_BOOT_DHCP
  231 #define DPRINTF(s)  printf s
  232 #else
  233 #define DPRINTF(s)
  234 #endif
  235 
  236 
  237 /*
  238  * Get our boot parameters using BOOTP.
  239  */
  240 int
  241 nfs_bootdhcp(nd, lwp)
  242         struct nfs_diskless *nd;
  243         struct lwp *lwp;
  244 {
  245         struct ifnet *ifp = nd->nd_ifp;
  246         int error;
  247 
  248         /*
  249          * Do enough of ifconfig(8) so that the chosen interface
  250          * can talk to the servers.  Use address zero for now.
  251          */
  252         error = nfs_boot_setaddress(ifp, lwp, INADDR_ANY, INADDR_ANY,
  253                                     INADDR_BROADCAST);
  254         if (error) {
  255                 printf("nfs_boot: set ifaddr zero, error=%d\n", error);
  256                 return (error);
  257         }
  258 
  259         /* This function call does the real send/recv work. */
  260         error = bootpc_call(nd, lwp);
  261 
  262         /* Get rid of the temporary (zero) IP address. */
  263         (void) nfs_boot_deladdress(ifp, lwp, INADDR_ANY);
  264 
  265         /* NOW we can test the error from bootpc_call. */
  266         if (error)
  267                 goto out;
  268 
  269         /*
  270          * Do ifconfig with our real IP address and mask.
  271          */
  272         error = nfs_boot_setaddress(ifp, lwp, nd->nd_myip.s_addr,
  273                                     nd->nd_mask.s_addr, INADDR_ANY);
  274         if (error) {
  275                 printf("nfs_boot: set ifaddr real, error=%d\n", error);
  276                 goto out;
  277         }
  278 
  279 out:
  280         if (error) {
  281                 (void) nfs_boot_ifupdown(ifp, lwp, 0);
  282                 nfs_boot_flushrt(ifp);
  283         }
  284         return (error);
  285 }
  286 
  287 struct bootpcontext {
  288         int xid;
  289         u_char *haddr;
  290         u_char halen;
  291         struct bootp *replybuf;
  292         int replylen;
  293 #ifdef NFS_BOOT_DHCP
  294         char expected_dhcpmsgtype, dhcp_ok;
  295         struct in_addr dhcp_serverip;
  296 #endif
  297 };
  298 
  299 static int bootpset __P((struct mbuf*, void*, int));
  300 static int bootpcheck __P((struct mbuf*, void*));
  301 
  302 static int
  303 bootpset(struct mbuf *m, void *context, int waited)
  304 {
  305         struct bootp *bootp;
  306 
  307         /* we know it's contigous (in 1 mbuf cluster) */
  308         bootp = mtod(m, struct bootp*);
  309 
  310         bootp->bp_secs = htons(waited);
  311 
  312         return (0);
  313 }
  314 
  315 static int
  316 bootpcheck(m, context)
  317         struct mbuf *m;
  318         void *context;
  319 {
  320         struct bootp *bootp;
  321         struct bootpcontext *bpc = context;
  322         u_int tag, len;
  323         u_char *p, *limit;
  324 
  325         /*
  326          * Is this a valid reply?
  327          */
  328         if (m->m_pkthdr.len < BOOTP_SIZE_MIN) {
  329                 DPRINTF(("bootpcheck: short packet %d < %d\n", m->m_pkthdr.len,
  330                     BOOTP_SIZE_MIN));
  331                 return (-1);
  332         }
  333         if (m->m_pkthdr.len > BOOTP_SIZE_MAX) {
  334                 DPRINTF(("bootpcheck: long packet %d > %d\n", m->m_pkthdr.len,
  335                     BOOTP_SIZE_MAX));
  336                 return (-1);
  337         }
  338 
  339         /*
  340          * don't make first checks more expensive than necessary
  341          */
  342         if (m->m_len < offsetof(struct bootp, bp_sname)) {
  343                 m = m_pullup(m, offsetof(struct bootp, bp_sname));
  344                 if (m == NULL) {
  345                         DPRINTF(("bootpcheck: m_pullup failed\n"));
  346                         return (-1);
  347                 }
  348         }
  349         bootp = mtod(m, struct bootp*);
  350 
  351         if (bootp->bp_op != BOOTREPLY) {
  352                 DPRINTF(("bootpcheck: op %d is not reply\n", bootp->bp_op));
  353                 return (-1);
  354         }
  355         if (bootp->bp_hlen != bpc->halen) {
  356                 DPRINTF(("bootpcheck: hlen %d != %d\n", bootp->bp_hlen,
  357                     bpc->halen));
  358                 return (-1);
  359         }
  360         if (memcmp(bootp->bp_chaddr, bpc->haddr, bpc->halen)) {
  361 #ifdef DEBUG_NFS_BOOT_DHCP
  362                 char bp_chaddr[3 * bpc->halen], haddr[3 * bpc->halen];
  363 #endif
  364                 DPRINTF(("bootpcheck: incorrect hwaddr %s != %s\n",
  365                     ether_snprintf(bp_chaddr, sizeof(bp_chaddr),
  366                     bootp->bp_chaddr),
  367                     ether_snprintf(haddr, sizeof(haddr), bpc->haddr)));
  368                 return (-1);
  369         }
  370         if (bootp->bp_xid != bpc->xid) {
  371                 DPRINTF(("bootpcheck: xid %d != %d\n", bootp->bp_xid,
  372                     bpc->xid));
  373                 return (-1);
  374         }
  375 
  376         /*
  377          * OK, it's worth to look deeper.
  378          * We copy the mbuf into a flat buffer here because
  379          * m_pullup() is a bit limited for this purpose
  380          * (doesn't allocate a cluster if necessary).
  381          */
  382         bpc->replylen = m->m_pkthdr.len;
  383         m_copydata(m, 0, bpc->replylen, (caddr_t)bpc->replybuf);
  384         bootp = bpc->replybuf;
  385 
  386         /*
  387          * Check if the IP address we get looks correct.
  388          * (DHCP servers can send junk to unknown clients.)
  389          * XXX more checks might be needed
  390          */
  391         if (bootp->bp_yiaddr.s_addr == INADDR_ANY ||
  392             bootp->bp_yiaddr.s_addr == INADDR_BROADCAST) {
  393                 printf("nfs_boot: wrong IP addr %s",
  394                        inet_ntoa(bootp->bp_yiaddr));
  395                 goto warn;
  396         }
  397 
  398         /*
  399          * Check the vendor data.
  400          */
  401         if (memcmp(bootp->bp_vend, vm_rfc1048, 4)) {
  402                 printf("nfs_boot: reply missing options");
  403                 goto warn;
  404         }
  405         p = &bootp->bp_vend[4];
  406         limit = ((char*)bootp) + bpc->replylen;
  407         while (p < limit) {
  408                 tag = *p++;
  409                 if (tag == TAG_END)
  410                         break;
  411                 if (tag == TAG_PAD)
  412                         continue;
  413                 len = *p++;
  414                 if ((p + len) > limit) {
  415                         printf("nfs_boot: option %d too long", tag);
  416                         goto warn;
  417                 }
  418                 switch (tag) {
  419 #ifdef NFS_BOOT_DHCP
  420                 case TAG_DHCP_MSGTYPE:
  421                         if (*p != bpc->expected_dhcpmsgtype)
  422                                 return (-1);
  423                         bpc->dhcp_ok = 1;
  424                         break;
  425                 case TAG_SERVERID:
  426                         memcpy(&bpc->dhcp_serverip.s_addr, p,
  427                               sizeof(bpc->dhcp_serverip.s_addr));
  428                         break;
  429 #endif
  430                 default:
  431                         break;
  432                 }
  433                 p += len;
  434         }
  435         return (0);
  436 
  437 warn:
  438         printf(" (bad reply from %s)\n", inet_ntoa(bootp->bp_siaddr));
  439         return (-1);
  440 }
  441 
  442 static int
  443 bootpc_call(nd, lwp)
  444         struct nfs_diskless *nd;
  445         struct lwp *lwp;
  446 {
  447         struct socket *so;
  448         struct ifnet *ifp = nd->nd_ifp;
  449         static u_int32_t xid = ~0xFF;
  450         struct bootp *bootp;    /* request */
  451         struct mbuf *m, *nam;
  452         struct sockaddr_in *sin;
  453         int error;
  454         u_char *haddr;
  455         u_char hafmt, halen;
  456         struct bootpcontext bpc;
  457 #ifdef NFS_BOOT_DHCP
  458         char vci[64];
  459         int vcilen;
  460 #endif
  461 
  462         error = socreate(AF_INET, &so, SOCK_DGRAM, 0, lwp);
  463         if (error) {
  464                 printf("bootp: socreate, error=%d\n", error);
  465                 return (error);
  466         }
  467 
  468         /*
  469          * Initialize to NULL anything that will hold an allocation,
  470          * and free each at the end if not null.
  471          */
  472         bpc.replybuf = NULL;
  473         m = nam = NULL;
  474 
  475         /* Record our H/W (Ethernet) address. */
  476         {       struct sockaddr_dl *sdl = ifp->if_sadl;
  477                 switch (sdl->sdl_type) {
  478                     case IFT_ISO88025:
  479                         hafmt = HTYPE_IEEE802;
  480                         break;
  481                     case IFT_ETHER:
  482                     case IFT_FDDI:
  483                         hafmt = HTYPE_ETHERNET;
  484                         break;
  485                     default:
  486                         printf("bootp: unsupported interface type %d\n",
  487                                sdl->sdl_type);
  488                         error = EINVAL;
  489                         goto out;
  490                 }
  491                 halen = sdl->sdl_alen;
  492                 haddr = (unsigned char *)LLADDR(sdl);
  493         }
  494 
  495         /*
  496          * Skip the route table when sending on this socket.
  497          * If this is not done, ip_output finds the loopback
  498          * interface (why?) and then fails because broadcast
  499          * is not supported on that interface...
  500          */
  501         {       int32_t *opt;
  502                 m = m_get(M_WAIT, MT_SOOPTS);
  503                 opt = mtod(m, int32_t *);
  504                 m->m_len = sizeof(*opt);
  505                 *opt = 1;
  506                 error = sosetopt(so, SOL_SOCKET, SO_DONTROUTE, m);
  507                 m = NULL;       /* was consumed */
  508         }
  509         if (error) {
  510                 DPRINTF(("bootpc_call: SO_DONTROUTE failed %d\n", error));
  511                 goto out;
  512         }
  513 
  514         /* Enable broadcast. */
  515         if ((error = nfs_boot_enbroadcast(so))) {
  516                 DPRINTF(("bootpc_call: SO_BROADCAST failed %d\n", error));
  517                 goto out;
  518         }
  519 
  520         /*
  521          * Set some TTL so we can boot through routers.
  522          * Real BOOTP forwarding agents don't need this; they obey "bp_hops"
  523          * and set "bp_giaddr", thus rewrite the packet anyway.
  524          * The "helper-address" feature of some popular router vendor seems
  525          * to do simple IP forwarding and drops packets with (ip_ttl == 1).
  526          */
  527         {       u_char *opt;
  528                 m = m_get(M_WAIT, MT_SOOPTS);
  529                 opt = mtod(m, u_char *);
  530                 m->m_len = sizeof(*opt);
  531                 *opt = 7;
  532                 error = sosetopt(so, IPPROTO_IP, IP_MULTICAST_TTL, m);
  533                 m = NULL;       /* was consumed */
  534         }
  535         if (error) {
  536                 DPRINTF(("bootpc_call: IP_MULTICAST_TTL failed %d\n", error));
  537                 goto out;
  538         }
  539 
  540         /* Set the receive timeout for the socket. */
  541         if ((error = nfs_boot_setrecvtimo(so))) {
  542                 DPRINTF(("bootpc_call: SO_RCVTIMEO failed %d\n", error));
  543                 goto out;
  544         }
  545 
  546         /*
  547          * Bind the local endpoint to a bootp client port.
  548          */
  549         if ((error = nfs_boot_sobind_ipport(so, IPPORT_BOOTPC, lwp))) {
  550                 DPRINTF(("bootpc_call: bind failed %d\n", error));
  551                 goto out;
  552         }
  553 
  554         /*
  555          * Setup socket address for the server.
  556          */
  557         nam = m_get(M_WAIT, MT_SONAME);
  558         sin = mtod(nam, struct sockaddr_in *);
  559         sin->sin_len = nam->m_len = sizeof(*sin);
  560         sin->sin_family = AF_INET;
  561         sin->sin_addr.s_addr = INADDR_BROADCAST;
  562         sin->sin_port = htons(IPPORT_BOOTPS);
  563 
  564         /*
  565          * Allocate buffer used for request
  566          */
  567         m = m_gethdr(M_WAIT, MT_DATA);
  568         m_clget(m, M_WAIT);
  569         bootp = mtod(m, struct bootp*);
  570         m->m_pkthdr.len = m->m_len = BOOTP_SIZE_MAX;
  571         m->m_pkthdr.rcvif = NULL;
  572 
  573         /*
  574          * Build the BOOTP reqest message.
  575          * Note: xid is host order! (opaque to server)
  576          */
  577         memset((caddr_t)bootp, 0, BOOTP_SIZE_MAX);
  578         bootp->bp_op    = BOOTREQUEST;
  579         bootp->bp_htype = hafmt;
  580         bootp->bp_hlen  = halen;        /* Hardware address length */
  581         bootp->bp_xid = ++xid;
  582         memcpy(bootp->bp_chaddr, haddr, halen);
  583 #ifdef NFS_BOOT_BOOTP_REQFILE
  584         strncpy(bootp->bp_file, NFS_BOOT_BOOTP_REQFILE, sizeof(bootp->bp_file));
  585 #endif
  586         /* Fill-in the vendor data. */
  587         memcpy(bootp->bp_vend, vm_rfc1048, 4);
  588 #ifdef NFS_BOOT_DHCP
  589         bootp->bp_vend[4] = TAG_DHCP_MSGTYPE;
  590         bootp->bp_vend[5] = 1;
  591         bootp->bp_vend[6] = DHCPDISCOVER;
  592         /*
  593          * Insert a NetBSD Vendor Class Identifier option.
  594          */
  595         snprintf(vci, sizeof(vci), "%s:%s:kernel:%s", ostype, MACHINE,
  596             osrelease);
  597         vcilen = strlen(vci);
  598         bootp->bp_vend[7] = TAG_CLASSID;
  599         bootp->bp_vend[8] = vcilen;
  600         memcpy(&bootp->bp_vend[9], vci, vcilen);
  601         bootp->bp_vend[9 + vcilen] = TAG_END;
  602 #else
  603         bootp->bp_vend[4] = TAG_END;
  604 #endif
  605 
  606         bpc.xid = xid;
  607         bpc.haddr = haddr;
  608         bpc.halen = halen;
  609         bpc.replybuf = malloc(BOOTP_SIZE_MAX, M_DEVBUF, M_WAITOK);
  610         if (bpc.replybuf == NULL)
  611                 panic("nfs_boot: malloc reply buf");
  612 #ifdef NFS_BOOT_DHCP
  613         bpc.expected_dhcpmsgtype = DHCPOFFER;
  614         bpc.dhcp_ok = 0;
  615 #endif
  616 
  617         error = nfs_boot_sendrecv(so, nam, bootpset, m,
  618                                   bootpcheck, 0, 0, &bpc, lwp);
  619         if (error)
  620                 goto out;
  621 
  622 #ifdef NFS_BOOT_DHCP
  623         if (bpc.dhcp_ok) {
  624                 u_int32_t leasetime;
  625                 bootp->bp_vend[6] = DHCPREQUEST;
  626                 bootp->bp_vend[7] = TAG_REQ_ADDR;
  627                 bootp->bp_vend[8] = 4;
  628                 memcpy(&bootp->bp_vend[9], &bpc.replybuf->bp_yiaddr, 4);
  629                 bootp->bp_vend[13] = TAG_SERVERID;
  630                 bootp->bp_vend[14] = 4;
  631                 memcpy(&bootp->bp_vend[15], &bpc.dhcp_serverip.s_addr, 4);
  632                 bootp->bp_vend[19] = TAG_LEASETIME;
  633                 bootp->bp_vend[20] = 4;
  634                 leasetime = htonl(300);
  635                 memcpy(&bootp->bp_vend[21], &leasetime, 4);
  636                 bootp->bp_vend[25] = TAG_CLASSID;
  637                 bootp->bp_vend[26] = vcilen;
  638                 memcpy(&bootp->bp_vend[27], vci, vcilen);
  639                 bootp->bp_vend[27 + vcilen] = TAG_END;
  640 
  641                 bpc.expected_dhcpmsgtype = DHCPACK;
  642 
  643                 error = nfs_boot_sendrecv(so, nam, bootpset, m,
  644                                           bootpcheck, 0, 0, &bpc, lwp);
  645                 if (error)
  646                         goto out;
  647         }
  648 #endif
  649 
  650         /*
  651          * bootpcheck() has copied the receive mbuf into
  652          * the buffer at bpc.replybuf.
  653          */
  654 #ifdef NFS_BOOT_DHCP
  655         printf("nfs_boot: %s next-server: %s\n",
  656                (bpc.dhcp_ok ? "DHCP" : "BOOTP"),
  657 #else
  658         printf("nfs_boot: BOOTP next-server: %s\n",
  659 #endif
  660                inet_ntoa(bpc.replybuf->bp_siaddr));
  661 
  662         bootp_extract(bpc.replybuf, bpc.replylen, nd);
  663 
  664 out:
  665         if (bpc.replybuf)
  666                 free(bpc.replybuf, M_DEVBUF);
  667         if (m)
  668                 m_freem(m);
  669         if (nam)
  670                 m_freem(nam);
  671         soclose(so);
  672         return (error);
  673 }
  674 
  675 static void
  676 bootp_extract(bootp, replylen, nd)
  677         struct bootp *bootp;
  678         int replylen;
  679         struct nfs_diskless *nd;
  680 {
  681         struct sockaddr_in *sin;
  682         struct in_addr netmask;
  683         struct in_addr gateway;
  684         struct in_addr rootserver;
  685         char *myname;   /* my hostname */
  686         char *mydomain; /* my domainname */
  687         char *rootpath;
  688         int mynamelen;
  689         int mydomainlen;
  690         int rootpathlen;
  691         int overloaded;
  692         u_int tag, len;
  693         u_char *p, *limit;
  694 
  695         /* Default these to "unspecified". */
  696         netmask.s_addr = 0;
  697         gateway.s_addr = 0;
  698         mydomain    = myname    = rootpath = NULL;
  699         mydomainlen = mynamelen = rootpathlen = 0;
  700 
  701         /* default root server to bootp next-server */
  702         rootserver = bootp->bp_siaddr;
  703         /* assume that server name field is not overloaded by default */
  704         overloaded = 0;
  705 
  706         p = &bootp->bp_vend[4];
  707         limit = ((char*)bootp) + replylen;
  708         while (p < limit) {
  709                 tag = *p++;
  710                 if (tag == TAG_END)
  711                         break;
  712                 if (tag == TAG_PAD)
  713                         continue;
  714                 len = *p++;
  715 #if 0 /* already done in bootpcheck() */
  716                 if ((p + len) > limit) {
  717                         printf("nfs_boot: option %d too long\n", tag);
  718                         break;
  719                 }
  720 #endif
  721                 switch (tag) {
  722                     case TAG_SUBNET_MASK:
  723                         memcpy(&netmask, p, 4);
  724                         break;
  725                     case TAG_GATEWAY:
  726                         /* Routers */
  727                         memcpy(&gateway, p, 4);
  728                         break;
  729                     case TAG_HOST_NAME:
  730                         if (len >= sizeof(hostname)) {
  731                                 printf("nfs_boot: host name >= %lu bytes",
  732                                        (u_long)sizeof(hostname));
  733                                 break;
  734                         }
  735                         myname = p;
  736                         mynamelen = len;
  737                         break;
  738                     case TAG_DOMAIN_NAME:
  739                         if (len >= sizeof(domainname)) {
  740                                 printf("nfs_boot: domain name >= %lu bytes",
  741                                        (u_long)sizeof(domainname));
  742                                 break;
  743                         }
  744                         mydomain = p;
  745                         mydomainlen = len;
  746                         break;
  747                     case TAG_ROOT_PATH:
  748                         /* Leave some room for the server name. */
  749                         if (len >= (MNAMELEN-10)) {
  750                                 printf("nfs_boot: rootpath >=%d bytes",
  751                                        (MNAMELEN-10));
  752                                 break;
  753                         }
  754                         rootpath = p;
  755                         rootpathlen = len;
  756                         break;
  757                     case TAG_SWAP_SERVER:
  758                         /* override NFS server address */
  759                         memcpy(&rootserver, p, 4);
  760                         break;
  761 #ifdef NFS_BOOT_DHCP
  762                     case TAG_OVERLOAD:
  763                         if (len > 0 && ((*p & 0x02) != 0))
  764                                 /*
  765                                  * The server name field in the dhcp packet
  766                                  * is overloaded and we can't find server
  767                                  * name there.
  768                                  */
  769                                 overloaded = 1;
  770                         break;
  771 #endif
  772                     default:
  773                         break;
  774                 }
  775                 p += len;
  776         }
  777 
  778         /*
  779          * Store and print network config info.
  780          */
  781         if (myname) {
  782                 myname[mynamelen] = '\0';
  783                 strncpy(hostname, myname, sizeof(hostname));
  784                 hostnamelen = mynamelen;
  785                 printf("nfs_boot: my_name=%s\n", hostname);
  786         }
  787         if (mydomain) {
  788                 mydomain[mydomainlen] = '\0';
  789                 strncpy(domainname, mydomain, sizeof(domainname));
  790                 domainnamelen = mydomainlen;
  791                 printf("nfs_boot: my_domain=%s\n", domainname);
  792         }
  793         nd->nd_myip = bootp->bp_yiaddr;
  794         if (nd->nd_myip.s_addr)
  795                 printf("nfs_boot: my_addr=%s\n", inet_ntoa(nd->nd_myip));
  796         nd->nd_mask = netmask;
  797         if (nd->nd_mask.s_addr)
  798                 printf("nfs_boot: my_mask=%s\n", inet_ntoa(nd->nd_mask));
  799         nd->nd_gwip = gateway;
  800         if (nd->nd_gwip.s_addr)
  801                 printf("nfs_boot: gateway=%s\n", inet_ntoa(nd->nd_gwip));
  802 
  803         /*
  804          * Store the information about our NFS root mount.
  805          * The caller will print it, so be silent here.
  806          */
  807         {
  808                 struct nfs_dlmount *ndm = &nd->nd_root;
  809 
  810                 /* Server IP address. */
  811                 sin = (struct sockaddr_in *) &ndm->ndm_saddr;
  812                 memset((caddr_t)sin, 0, sizeof(*sin));
  813                 sin->sin_len = sizeof(*sin);
  814                 sin->sin_family = AF_INET;
  815                 sin->sin_addr = rootserver;
  816                 /* Server name. */
  817                 if (!overloaded && bootp->bp_sname[0] != 0 &&
  818                     !memcmp(&rootserver, &bootp->bp_siaddr,
  819                           sizeof(struct in_addr))) {
  820                         /* standard root server, we have the name */
  821                         strncpy(ndm->ndm_host, bootp->bp_sname, BP_SNAME_LEN-1);
  822                 } else {
  823                         /* Show the server IP address numerically. */
  824                         strncpy(ndm->ndm_host, inet_ntoa(rootserver),
  825                                 BP_SNAME_LEN-1);
  826                 }
  827                 len = strlen(ndm->ndm_host);
  828                 if (rootpath &&
  829                     len + 1 + rootpathlen + 1 <= sizeof(ndm->ndm_host)) {
  830                         ndm->ndm_host[len++] = ':';
  831                         strncpy(ndm->ndm_host + len,
  832                                 rootpath, rootpathlen);
  833                         ndm->ndm_host[len + rootpathlen] = '\0';
  834                 } /* else: upper layer will handle error */
  835         }
  836 
  837 #ifdef TFTPROOT
  838 #if BP_FILE_LEN > MNAMELEN
  839 #define BOOTFILELEN MNAMELEN
  840 #else
  841 #define BOOTFILELEN BP_FILE_LEN
  842 #endif
  843         strncpy(nd->nd_bootfile, bootp->bp_file, BOOTFILELEN);
  844         nd->nd_bootfile[BOOTFILELEN - 1] = '\0';
  845 #undef BOOTFILELEN
  846 #endif /* TFTPROOT */
  847 }

Cache object: a06686c9388bc77d3d843937d0d82c11


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.