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

Cache object: 8a515c11545a90b27b3d40926f08a27f


[ 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.