bootp.c revision 92913
138451Smsmith/*	$NetBSD: bootp.c,v 1.14 1998/02/16 11:10:54 drochner Exp $	*/
238451Smsmith
338451Smsmith/*
438451Smsmith * Copyright (c) 1992 Regents of the University of California.
538451Smsmith * All rights reserved.
638451Smsmith *
738451Smsmith * This software was developed by the Computer Systems Engineering group
838451Smsmith * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
938451Smsmith * contributed to Berkeley.
1038451Smsmith *
1138451Smsmith * Redistribution and use in source and binary forms, with or without
1238451Smsmith * modification, are permitted provided that the following conditions
1338451Smsmith * are met:
1438451Smsmith * 1. Redistributions of source code must retain the above copyright
1538451Smsmith *    notice, this list of conditions and the following disclaimer.
1638451Smsmith * 2. Redistributions in binary form must reproduce the above copyright
1738451Smsmith *    notice, this list of conditions and the following disclaimer in the
1838451Smsmith *    documentation and/or other materials provided with the distribution.
1938451Smsmith * 3. All advertising materials mentioning features or use of this software
2038451Smsmith *    must display the following acknowledgement:
2138451Smsmith *	This product includes software developed by the University of
2238451Smsmith *	California, Lawrence Berkeley Laboratory and its contributors.
2338451Smsmith * 4. Neither the name of the University nor the names of its contributors
2438451Smsmith *    may be used to endorse or promote products derived from this software
2538451Smsmith *    without specific prior written permission.
2638451Smsmith *
2738451Smsmith * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2838451Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2938451Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
3038451Smsmith * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
3138451Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
3238451Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
3338451Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3438451Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3538451Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3638451Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3738451Smsmith * SUCH DAMAGE.
3838451Smsmith *
3938451Smsmith * @(#) Header: bootp.c,v 1.4 93/09/11 03:13:51 leres Exp  (LBL)
4038451Smsmith */
4138451Smsmith
4284221Sdillon#include <sys/cdefs.h>
4384221Sdillon__FBSDID("$FreeBSD: head/lib/libstand/bootp.c 92913 2002-03-21 23:39:28Z obrien $");
4484221Sdillon
4538451Smsmith#include <sys/types.h>
4638451Smsmith#include <netinet/in.h>
4738451Smsmith#include <netinet/in_systm.h>
4838451Smsmith
4938451Smsmith#include <string.h>
5038451Smsmith
5138451Smsmith#define BOOTP_DEBUGxx
5238451Smsmith#define SUPPORT_DHCP
5338451Smsmith
5438451Smsmith#include "stand.h"
5538451Smsmith#include "net.h"
5638451Smsmith#include "netif.h"
5738451Smsmith#include "bootp.h"
5838451Smsmith
5938451Smsmith
6038451Smsmithstruct in_addr servip;
6138451Smsmith
6238451Smsmithstatic n_long	nmask, smask;
6338451Smsmith
6438451Smsmithstatic time_t	bot;
6538451Smsmith
6638451Smsmithstatic	char vm_rfc1048[4] = VM_RFC1048;
6738451Smsmith#ifdef BOOTP_VEND_CMU
6838451Smsmithstatic	char vm_cmu[4] = VM_CMU;
6938451Smsmith#endif
7038451Smsmith
7138451Smsmith/* Local forwards */
7238451Smsmithstatic	ssize_t bootpsend(struct iodesc *, void *, size_t);
7338451Smsmithstatic	ssize_t bootprecv(struct iodesc *, void *, size_t, time_t);
7438451Smsmithstatic	int vend_rfc1048(u_char *, u_int);
7538451Smsmith#ifdef BOOTP_VEND_CMU
7638451Smsmithstatic	void vend_cmu(u_char *);
7738451Smsmith#endif
7838451Smsmith
7938451Smsmith#ifdef SUPPORT_DHCP
8038451Smsmithstatic char expected_dhcpmsgtype = -1, dhcp_ok;
8138451Smsmithstruct in_addr dhcp_serverip;
8238451Smsmith#endif
8338451Smsmith
8438451Smsmith/* Fetch required bootp infomation */
8538451Smsmithvoid
8664527Spsbootp(sock, flag)
8738451Smsmith	int sock;
8864527Sps	int flag;
8938451Smsmith{
9038451Smsmith	struct iodesc *d;
9192913Sobrien	struct bootp *bp;
9238451Smsmith	struct {
9338451Smsmith		u_char header[HEADER_SIZE];
9438451Smsmith		struct bootp wbootp;
9538451Smsmith	} wbuf;
9638451Smsmith	struct {
9738451Smsmith		u_char header[HEADER_SIZE];
9838451Smsmith		struct bootp rbootp;
9938451Smsmith	} rbuf;
10038451Smsmith
10138451Smsmith#ifdef BOOTP_DEBUG
10238451Smsmith 	if (debug)
10338451Smsmith		printf("bootp: socket=%d\n", sock);
10438451Smsmith#endif
10538451Smsmith	if (!bot)
10638451Smsmith		bot = getsecs();
10738451Smsmith
10838451Smsmith	if (!(d = socktodesc(sock))) {
10938451Smsmith		printf("bootp: bad socket. %d\n", sock);
11038451Smsmith		return;
11138451Smsmith	}
11238451Smsmith#ifdef BOOTP_DEBUG
11338451Smsmith 	if (debug)
11438451Smsmith		printf("bootp: d=%lx\n", (long)d);
11538451Smsmith#endif
11638451Smsmith
11738451Smsmith	bp = &wbuf.wbootp;
11838451Smsmith	bzero(bp, sizeof(*bp));
11938451Smsmith
12038451Smsmith	bp->bp_op = BOOTREQUEST;
12138451Smsmith	bp->bp_htype = 1;		/* 10Mb Ethernet (48 bits) */
12238451Smsmith	bp->bp_hlen = 6;
12338451Smsmith	bp->bp_xid = htonl(d->xid);
12438451Smsmith	MACPY(d->myea, bp->bp_chaddr);
12538451Smsmith	strncpy(bp->bp_file, bootfile, sizeof(bp->bp_file));
12638451Smsmith	bcopy(vm_rfc1048, bp->bp_vend, sizeof(vm_rfc1048));
12738451Smsmith#ifdef SUPPORT_DHCP
12838451Smsmith	bp->bp_vend[4] = TAG_DHCP_MSGTYPE;
12938451Smsmith	bp->bp_vend[5] = 1;
13038451Smsmith	bp->bp_vend[6] = DHCPDISCOVER;
13164527Sps
13264527Sps	/*
13364527Sps	 * If we are booting from PXE, we want to send the string
13464527Sps	 * 'PXEClient' to the DHCP server so you have the option of
13564527Sps	 * only responding to PXE aware dhcp requests.
13664527Sps	 */
13764527Sps	if (flag & BOOTP_PXE) {
13864527Sps		bp->bp_vend[7] = TAG_CLASSID;
13964527Sps		bp->bp_vend[8] = 9;
14064527Sps		bcopy("PXEClient", &bp->bp_vend[9], 9);
14164527Sps		bp->bp_vend[18] = TAG_END;
14264527Sps	} else
14364527Sps		bp->bp_vend[7] = TAG_END;
14438451Smsmith#else
14538451Smsmith	bp->bp_vend[4] = TAG_END;
14638451Smsmith#endif
14738451Smsmith
14838451Smsmith	d->myip.s_addr = INADDR_ANY;
14938451Smsmith	d->myport = htons(IPPORT_BOOTPC);
15038451Smsmith	d->destip.s_addr = INADDR_BROADCAST;
15138451Smsmith	d->destport = htons(IPPORT_BOOTPS);
15238451Smsmith
15338451Smsmith#ifdef SUPPORT_DHCP
15438451Smsmith	expected_dhcpmsgtype = DHCPOFFER;
15538451Smsmith	dhcp_ok = 0;
15638451Smsmith#endif
15738451Smsmith
15838451Smsmith	if(sendrecv(d,
15938451Smsmith		    bootpsend, bp, sizeof(*bp),
16038451Smsmith		    bootprecv, &rbuf.rbootp, sizeof(rbuf.rbootp))
16138451Smsmith	   == -1) {
16238451Smsmith	    printf("bootp: no reply\n");
16338451Smsmith	    return;
16438451Smsmith	}
16538451Smsmith
16638451Smsmith#ifdef SUPPORT_DHCP
16738451Smsmith	if(dhcp_ok) {
16838451Smsmith		u_int32_t leasetime;
16938451Smsmith		bp->bp_vend[6] = DHCPREQUEST;
17038451Smsmith		bp->bp_vend[7] = TAG_REQ_ADDR;
17138451Smsmith		bp->bp_vend[8] = 4;
17238451Smsmith		bcopy(&rbuf.rbootp.bp_yiaddr, &bp->bp_vend[9], 4);
17338451Smsmith		bp->bp_vend[13] = TAG_SERVERID;
17438451Smsmith		bp->bp_vend[14] = 4;
17538451Smsmith		bcopy(&dhcp_serverip.s_addr, &bp->bp_vend[15], 4);
17638451Smsmith		bp->bp_vend[19] = TAG_LEASETIME;
17738451Smsmith		bp->bp_vend[20] = 4;
17838451Smsmith		leasetime = htonl(300);
17938451Smsmith		bcopy(&leasetime, &bp->bp_vend[21], 4);
18064527Sps		if (flag & BOOTP_PXE) {
18164527Sps			bp->bp_vend[25] = TAG_CLASSID;
18264527Sps			bp->bp_vend[26] = 9;
18364527Sps			bcopy("PXEClient", &bp->bp_vend[27], 9);
18464527Sps			bp->bp_vend[36] = TAG_END;
18564527Sps		} else
18664527Sps			bp->bp_vend[25] = TAG_END;
18738451Smsmith
18838451Smsmith		expected_dhcpmsgtype = DHCPACK;
18938451Smsmith
19038451Smsmith		if(sendrecv(d,
19138451Smsmith			    bootpsend, bp, sizeof(*bp),
19238451Smsmith			    bootprecv, &rbuf.rbootp, sizeof(rbuf.rbootp))
19338451Smsmith		   == -1) {
19438451Smsmith			printf("DHCPREQUEST failed\n");
19538451Smsmith			return;
19638451Smsmith		}
19738451Smsmith	}
19838451Smsmith#endif
19938451Smsmith
20038451Smsmith	myip = d->myip = rbuf.rbootp.bp_yiaddr;
20138451Smsmith	servip = rbuf.rbootp.bp_siaddr;
20238451Smsmith	if(rootip.s_addr == INADDR_ANY) rootip = servip;
20338451Smsmith	bcopy(rbuf.rbootp.bp_file, bootfile, sizeof(bootfile));
20438451Smsmith	bootfile[sizeof(bootfile) - 1] = '\0';
20538451Smsmith
20666134Sps	if (IN_CLASSA(ntohl(myip.s_addr)))
20738451Smsmith		nmask = htonl(IN_CLASSA_NET);
20866134Sps	else if (IN_CLASSB(ntohl(myip.s_addr)))
20938451Smsmith		nmask = htonl(IN_CLASSB_NET);
21038451Smsmith	else
21138451Smsmith		nmask = htonl(IN_CLASSC_NET);
21238451Smsmith#ifdef BOOTP_DEBUG
21338451Smsmith	if (debug)
21438451Smsmith		printf("'native netmask' is %s\n", intoa(nmask));
21538451Smsmith#endif
21638451Smsmith
21738451Smsmith	/* Check subnet mask against net mask; toss if bogus */
21838451Smsmith	if ((nmask & smask) != nmask) {
21938451Smsmith#ifdef BOOTP_DEBUG
22038451Smsmith		if (debug)
22138451Smsmith			printf("subnet mask (%s) bad\n", intoa(smask));
22238451Smsmith#endif
22338451Smsmith		smask = 0;
22438451Smsmith	}
22538451Smsmith
22638451Smsmith	/* Get subnet (or natural net) mask */
22738451Smsmith	netmask = nmask;
22838451Smsmith	if (smask)
22938451Smsmith		netmask = smask;
23038451Smsmith#ifdef BOOTP_DEBUG
23138451Smsmith	if (debug)
23238451Smsmith		printf("mask: %s\n", intoa(netmask));
23338451Smsmith#endif
23438451Smsmith
23538451Smsmith	/* We need a gateway if root is on a different net */
23638451Smsmith	if (!SAMENET(myip, rootip, netmask)) {
23738451Smsmith#ifdef BOOTP_DEBUG
23838451Smsmith		if (debug)
23938451Smsmith			printf("need gateway for root ip\n");
24038451Smsmith#endif
24138451Smsmith	}
24238451Smsmith
24338451Smsmith	/* Toss gateway if on a different net */
24438451Smsmith	if (!SAMENET(myip, gateip, netmask)) {
24538451Smsmith#ifdef BOOTP_DEBUG
24638451Smsmith		if (debug)
24738451Smsmith			printf("gateway ip (%s) bad\n", inet_ntoa(gateip));
24838451Smsmith#endif
24938451Smsmith		gateip.s_addr = 0;
25038451Smsmith	}
25138451Smsmith
25238451Smsmith	/* Bump xid so next request will be unique. */
25338451Smsmith	++d->xid;
25438451Smsmith}
25538451Smsmith
25638451Smsmith/* Transmit a bootp request */
25738451Smsmithstatic ssize_t
25838451Smsmithbootpsend(d, pkt, len)
25992913Sobrien	struct iodesc *d;
26092913Sobrien	void *pkt;
26192913Sobrien	size_t len;
26238451Smsmith{
26392913Sobrien	struct bootp *bp;
26438451Smsmith
26538451Smsmith#ifdef BOOTP_DEBUG
26638451Smsmith	if (debug)
26738451Smsmith		printf("bootpsend: d=%lx called.\n", (long)d);
26838451Smsmith#endif
26938451Smsmith
27038451Smsmith	bp = pkt;
27138451Smsmith	bp->bp_secs = htons((u_short)(getsecs() - bot));
27238451Smsmith
27338451Smsmith#ifdef BOOTP_DEBUG
27438451Smsmith	if (debug)
27538451Smsmith		printf("bootpsend: calling sendudp\n");
27638451Smsmith#endif
27738451Smsmith
27838451Smsmith	return (sendudp(d, pkt, len));
27938451Smsmith}
28038451Smsmith
28138451Smsmithstatic ssize_t
28238451Smsmithbootprecv(d, pkt, len, tleft)
28392913Sobrienstruct iodesc *d;
28492913Sobrienvoid *pkt;
28592913Sobriensize_t len;
28638451Smsmithtime_t tleft;
28738451Smsmith{
28892913Sobrien	ssize_t n;
28992913Sobrien	struct bootp *bp;
29038451Smsmith
29138451Smsmith#ifdef BOOTP_DEBUGx
29238451Smsmith	if (debug)
29338451Smsmith		printf("bootp_recvoffer: called\n");
29438451Smsmith#endif
29538451Smsmith
29638451Smsmith	n = readudp(d, pkt, len, tleft);
29738451Smsmith	if (n == -1 || n < sizeof(struct bootp) - BOOTP_VENDSIZE)
29838451Smsmith		goto bad;
29938451Smsmith
30038451Smsmith	bp = (struct bootp *)pkt;
30138451Smsmith
30238451Smsmith#ifdef BOOTP_DEBUG
30338451Smsmith	if (debug)
30438451Smsmith		printf("bootprecv: checked.  bp = 0x%lx, n = %d\n",
30538451Smsmith		    (long)bp, (int)n);
30638451Smsmith#endif
30738451Smsmith	if (bp->bp_xid != htonl(d->xid)) {
30838451Smsmith#ifdef BOOTP_DEBUG
30938451Smsmith		if (debug) {
31038451Smsmith			printf("bootprecv: expected xid 0x%lx, got 0x%x\n",
31138451Smsmith			    d->xid, ntohl(bp->bp_xid));
31238451Smsmith		}
31338451Smsmith#endif
31438451Smsmith		goto bad;
31538451Smsmith	}
31638451Smsmith
31738451Smsmith#ifdef BOOTP_DEBUG
31838451Smsmith	if (debug)
31938451Smsmith		printf("bootprecv: got one!\n");
32038451Smsmith#endif
32138451Smsmith
32238451Smsmith	/* Suck out vendor info */
32338451Smsmith	if (bcmp(vm_rfc1048, bp->bp_vend, sizeof(vm_rfc1048)) == 0) {
32438451Smsmith		if(vend_rfc1048(bp->bp_vend, sizeof(bp->bp_vend)) != 0)
32538451Smsmith		    goto bad;
32638451Smsmith	}
32738451Smsmith#ifdef BOOTP_VEND_CMU
32838451Smsmith	else if (bcmp(vm_cmu, bp->bp_vend, sizeof(vm_cmu)) == 0)
32938451Smsmith		vend_cmu(bp->bp_vend);
33038451Smsmith#endif
33138451Smsmith	else
33238451Smsmith		printf("bootprecv: unknown vendor 0x%lx\n", (long)bp->bp_vend);
33338451Smsmith
33438451Smsmith	return(n);
33538451Smsmithbad:
33638451Smsmith	errno = 0;
33738451Smsmith	return (-1);
33838451Smsmith}
33938451Smsmith
34038451Smsmithstatic int
34138451Smsmithvend_rfc1048(cp, len)
34292913Sobrien	u_char *cp;
34338451Smsmith	u_int len;
34438451Smsmith{
34592913Sobrien	u_char *ep;
34692913Sobrien	int size;
34792913Sobrien	u_char tag;
34838451Smsmith
34938451Smsmith#ifdef BOOTP_DEBUG
35038451Smsmith	if (debug)
35138451Smsmith		printf("vend_rfc1048 bootp info. len=%d\n", len);
35238451Smsmith#endif
35338451Smsmith	ep = cp + len;
35438451Smsmith
35538451Smsmith	/* Step over magic cookie */
35638451Smsmith	cp += sizeof(int);
35738451Smsmith
35838451Smsmith	while (cp < ep) {
35938451Smsmith		tag = *cp++;
36038451Smsmith		size = *cp++;
36138451Smsmith		if (tag == TAG_END)
36238451Smsmith			break;
36338451Smsmith
36438451Smsmith		if (tag == TAG_SUBNET_MASK) {
36538451Smsmith			bcopy(cp, &smask, sizeof(smask));
36638451Smsmith		}
36738451Smsmith		if (tag == TAG_GATEWAY) {
36838451Smsmith			bcopy(cp, &gateip.s_addr, sizeof(gateip.s_addr));
36938451Smsmith		}
37038451Smsmith		if (tag == TAG_SWAPSERVER) {
37138451Smsmith			/* let it override bp_siaddr */
37238451Smsmith			bcopy(cp, &rootip.s_addr, sizeof(swapip.s_addr));
37338451Smsmith		}
37438451Smsmith		if (tag == TAG_ROOTPATH) {
37538451Smsmith			strncpy(rootpath, (char *)cp, sizeof(rootpath));
37638451Smsmith			rootpath[size] = '\0';
37738451Smsmith		}
37838451Smsmith		if (tag == TAG_HOSTNAME) {
37938451Smsmith			strncpy(hostname, (char *)cp, sizeof(hostname));
38038451Smsmith			hostname[size] = '\0';
38138451Smsmith		}
38238451Smsmith#ifdef SUPPORT_DHCP
38338451Smsmith		if (tag == TAG_DHCP_MSGTYPE) {
38438451Smsmith			if(*cp != expected_dhcpmsgtype)
38538451Smsmith			    return(-1);
38638451Smsmith			dhcp_ok = 1;
38738451Smsmith		}
38838451Smsmith		if (tag == TAG_SERVERID) {
38938451Smsmith			bcopy(cp, &dhcp_serverip.s_addr,
39038451Smsmith			      sizeof(dhcp_serverip.s_addr));
39138451Smsmith		}
39238451Smsmith#endif
39338451Smsmith		cp += size;
39438451Smsmith	}
39538451Smsmith	return(0);
39638451Smsmith}
39738451Smsmith
39838451Smsmith#ifdef BOOTP_VEND_CMU
39938451Smsmithstatic void
40038451Smsmithvend_cmu(cp)
40138451Smsmith	u_char *cp;
40238451Smsmith{
40392913Sobrien	struct cmu_vend *vp;
40438451Smsmith
40538451Smsmith#ifdef BOOTP_DEBUG
40638451Smsmith	if (debug)
40738451Smsmith		printf("vend_cmu bootp info.\n");
40838451Smsmith#endif
40938451Smsmith	vp = (struct cmu_vend *)cp;
41038451Smsmith
41138451Smsmith	if (vp->v_smask.s_addr != 0) {
41238451Smsmith		smask = vp->v_smask.s_addr;
41338451Smsmith	}
41438451Smsmith	if (vp->v_dgate.s_addr != 0) {
41538451Smsmith		gateip = vp->v_dgate;
41638451Smsmith	}
41738451Smsmith}
41838451Smsmith#endif
419