[BACK]Return to nfs_bootdhcp.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / sys / nfs

Annotation of src/sys/nfs/nfs_bootdhcp.c, Revision 1.15

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

CVSweb <webmaster@jp.NetBSD.org>