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

Cache object: e8299d4051f640a7a754d7557d695150


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