summaryrefslogtreecommitdiffstats
path: root/sys/nfs/bootp_subr.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/nfs/bootp_subr.c')
-rw-r--r--sys/nfs/bootp_subr.c273
1 files changed, 240 insertions, 33 deletions
diff --git a/sys/nfs/bootp_subr.c b/sys/nfs/bootp_subr.c
index 9101ba7..72fc393 100644
--- a/sys/nfs/bootp_subr.c
+++ b/sys/nfs/bootp_subr.c
@@ -126,10 +126,15 @@ struct bootpc_ifcontext {
enum {
IF_BOOTP_UNRESOLVED,
IF_BOOTP_RESOLVED,
+ IF_BOOTP_FAILED,
+ IF_DHCP_UNRESOLVED,
IF_DHCP_OFFERED,
IF_DHCP_RESOLVED,
- IF_BOOTP_FAILED
+ IF_DHCP_FAILED,
} state;
+ int dhcpquerytype; /* dhcp type sent */
+ struct in_addr dhcpserver;
+ int gotdhcpserver;
};
#define TAG_MAXLEN 1024
@@ -191,6 +196,17 @@ struct bootpc_globalcontext {
#define TAG_ROOTOPTS 130
#define TAG_SWAPOPTS 131
+#define TAG_DHCP_MSGTYPE 53
+#define TAG_DHCP_REQ_ADDR 50
+#define TAG_DHCP_SERVERID 54
+#define TAG_DHCP_LEASETIME 51
+
+#define DHCP_NOMSG 0
+#define DHCP_DISCOVER 1
+#define DHCP_OFFER 2
+#define DHCP_REQUEST 3
+#define DHCP_ACK 5
+
extern int nfs_diskless_valid;
extern struct nfsv3_diskless nfsv3_diskless;
@@ -249,6 +265,10 @@ static void bootpc_decode_reply(struct nfsv3_diskless *nd,
static int bootpc_received(struct bootpc_globalcontext *gctx,
struct bootpc_ifcontext *ifctx);
+static __inline int bootpc_ifctx_isresolved(struct bootpc_ifcontext *ifctx);
+static __inline int bootpc_ifctx_isunresolved(struct bootpc_ifcontext *ifctx);
+static __inline int bootpc_ifctx_isfailed(struct bootpc_ifcontext *ifctx);
+
void bootpc_init(void);
/*
@@ -418,15 +438,52 @@ allocifctx(struct bootpc_globalcontext *gctx)
bzero(ifctx, sizeof(*ifctx));
ifctx->xid = gctx->xid;
+#ifdef BOOTP_NO_DHCP
+ ifctx->state = IF_BOOTP_UNRESOLVED;
+#else
+ ifctx->state = IF_DHCP_UNRESOLVED;
+#endif
gctx->xid += 0x100;
return ifctx;
}
+static __inline int
+bootpc_ifctx_isresolved(struct bootpc_ifcontext *ifctx)
+{
+ if (ifctx->state == IF_BOOTP_RESOLVED ||
+ ifctx->state == IF_DHCP_RESOLVED)
+ return 1;
+ return 0;
+}
+
+
+static __inline int
+bootpc_ifctx_isunresolved(struct bootpc_ifcontext *ifctx)
+{
+ if (ifctx->state == IF_BOOTP_UNRESOLVED ||
+ ifctx->state == IF_DHCP_UNRESOLVED)
+ return 1;
+ return 0;
+}
+
+
+static __inline int
+bootpc_ifctx_isfailed(struct bootpc_ifcontext *ifctx)
+{
+ if (ifctx->state == IF_BOOTP_FAILED ||
+ ifctx->state == IF_DHCP_FAILED)
+ return 1;
+ return 0;
+}
+
+
static int
bootpc_received(struct bootpc_globalcontext *gctx,
struct bootpc_ifcontext *ifctx)
{
+ unsigned char dhcpreplytype;
+ char *p;
/*
* Need timeout for fallback to less
* desirable alternative.
@@ -442,9 +499,35 @@ bootpc_received(struct bootpc_globalcontext *gctx,
if (gctx->tmptag.badopt != 0)
return 0;
+ p = bootpc_tag(&gctx->tmptag, &gctx->reply,
+ gctx->replylen, TAG_DHCP_MSGTYPE);
+ if (p != NULL)
+ dhcpreplytype = *p;
+ else
+ dhcpreplytype = DHCP_NOMSG;
+
+ switch (ifctx->dhcpquerytype) {
+ case DHCP_DISCOVER:
+ if (dhcpreplytype != DHCP_OFFER /* Normal DHCP offer */
+#ifndef BOOTP_FORCE_DHCP
+ && dhcpreplytype != DHCP_NOMSG /* Fallback to BOOTP */
+#endif
+ )
+ return 0;
+ break;
+ case DHCP_REQUEST:
+ if (dhcpreplytype != DHCP_ACK)
+ return 0;
+ case DHCP_NOMSG:
+ }
+
+
/* Ignore packet unless it gives us a root tag we didn't have */
- if (ifctx->state == IF_BOOTP_RESOLVED &&
+ if ((ifctx->state == IF_BOOTP_RESOLVED ||
+ (ifctx->dhcpquerytype == DHCP_DISCOVER &&
+ (ifctx->state == IF_DHCP_OFFERED ||
+ ifctx->state == IF_DHCP_RESOLVED))) &&
(bootpc_tag(&gctx->tmptag, &ifctx->reply,
ifctx->replylen,
TAG_ROOT) != NULL ||
@@ -459,7 +542,31 @@ bootpc_received(struct bootpc_globalcontext *gctx,
ifctx->replylen = gctx->replylen;
/* XXX: Only reset if 'perfect' response */
- ifctx->state = IF_BOOTP_RESOLVED;
+ if (ifctx->state == IF_BOOTP_UNRESOLVED)
+ ifctx->state = IF_BOOTP_RESOLVED;
+ else if (ifctx->state == IF_DHCP_UNRESOLVED &&
+ ifctx->dhcpquerytype == DHCP_DISCOVER) {
+ if (dhcpreplytype == DHCP_OFFER)
+ ifctx->state = IF_DHCP_OFFERED;
+ else
+ ifctx->state = IF_BOOTP_RESOLVED; /* Fallback */
+ } else if (ifctx->state == IF_DHCP_OFFERED &&
+ ifctx->dhcpquerytype == DHCP_REQUEST)
+ ifctx->state = IF_DHCP_RESOLVED;
+
+
+ if (ifctx->dhcpquerytype == DHCP_DISCOVER &&
+ ifctx->state != IF_BOOTP_RESOLVED) {
+ p = bootpc_tag(&gctx->tmptag, &ifctx->reply,
+ ifctx->replylen, TAG_DHCP_SERVERID);
+ if (p != NULL && gctx->tmptag.taglen == 4) {
+ memcpy(&ifctx->dhcpserver, p, 4);
+ ifctx->gotdhcpserver = 1;
+ } else
+ ifctx->gotdhcpserver = 0;
+ return 1;
+ }
+
ifctx->gotrootpath = (bootpc_tag(&gctx->tmptag, &ifctx->reply,
ifctx->replylen,
TAG_ROOT) != NULL);
@@ -488,6 +595,8 @@ bootpc_call(struct bootpc_globalcontext *gctx,
struct bootpc_ifcontext *ifctx;
int outstanding;
int gotrootpath;
+ int retry;
+ const char *s;
/*
* Create socket and set its recieve timeout.
@@ -568,7 +677,7 @@ bootpc_call(struct bootpc_globalcontext *gctx,
for (ifctx = gctx->interfaces;
ifctx != NULL;
ifctx = ifctx->next) {
- if (ifctx->state == IF_BOOTP_RESOLVED &&
+ if (bootpc_ifctx_isresolved(ifctx) != 0 &&
bootpc_tag(&gctx->tmptag, &ifctx->reply,
ifctx->replylen,
TAG_ROOT) != NULL)
@@ -579,21 +688,45 @@ bootpc_call(struct bootpc_globalcontext *gctx,
ifctx != NULL;
ifctx = ifctx->next) {
ifctx->outstanding = 0;
- if (ifctx->state == IF_BOOTP_RESOLVED &&
+ if (bootpc_ifctx_isresolved(ifctx) != 0 &&
gotrootpath != 0) {
continue;
}
- if (ifctx->state == IF_BOOTP_FAILED)
+ if (bootpc_ifctx_isfailed(ifctx) != 0)
continue;
outstanding++;
ifctx->outstanding = 1;
+ /* Proceed to next step in DHCP negotiation */
+ if ((ifctx->state == IF_DHCP_OFFERED &&
+ ifctx->dhcpquerytype != DHCP_REQUEST) ||
+ (ifctx->state == IF_DHCP_UNRESOLVED &&
+ ifctx->dhcpquerytype != DHCP_DISCOVER) ||
+ (ifctx->state == IF_BOOTP_UNRESOLVED &&
+ ifctx->dhcpquerytype != DHCP_NOMSG)) {
+ ifctx->sentmsg = 0;
+ bootpc_compose_query(ifctx, gctx, procp);
+ }
+
/* Send BOOTP request (or re-send). */
if (ifctx->sentmsg == 0) {
- printf("Sending BOOTP Query packet from "
+ switch(ifctx->dhcpquerytype) {
+ case DHCP_DISCOVER:
+ s = "DHCP Discover";
+ break;
+ case DHCP_REQUEST:
+ s = "DHCP Request";
+ break;
+ case DHCP_NOMSG:
+ default:
+ s = "BOOTP Query";
+ break;
+ }
+ printf("Sending %s packet from "
"interface %s (%*D)\n",
+ s,
ifctx->ireq.ifr_name,
ifctx->sdl->sdl_alen,
(unsigned char *) LLADDR(ifctx->sdl),
@@ -657,7 +790,7 @@ bootpc_call(struct bootpc_globalcontext *gctx,
if (timo < MAX_RESEND_DELAY)
timo++;
else {
- printf("BOOTP timeout for server ");
+ printf("DHCP/BOOTP timeout for server ");
print_sin_addr(&dst);
printf("\n");
}
@@ -686,10 +819,10 @@ bootpc_call(struct bootpc_globalcontext *gctx,
for (ifctx = gctx->interfaces;
ifctx != NULL;
ifctx = ifctx->next) {
- if (ifctx->state == IF_BOOTP_RESOLVED)
- continue;
- if (ifctx->state == IF_BOOTP_FAILED)
+ if (bootpc_ifctx_isresolved(ifctx) != 0 ||
+ bootpc_ifctx_isfailed(ifctx) != 0)
continue;
+
ifctx->call.secs = htons(gctx->secs);
}
if (error == EWOULDBLOCK)
@@ -728,8 +861,28 @@ bootpc_call(struct bootpc_globalcontext *gctx,
}
if (ifctx != NULL) {
- printf("Received BOOTP Reply packet"
+ s = bootpc_tag(&gctx->tmptag,
+ &gctx->reply,
+ gctx->replylen,
+ TAG_DHCP_MSGTYPE);
+ if (s != NULL) {
+ switch (*s) {
+ case DHCP_OFFER:
+ s = "DHCP Offer";
+ break;
+ case DHCP_ACK:
+ s = "DHCP Ack";
+ break;
+ default:
+ s = "DHCP (unexpected)";
+ break;
+ }
+ } else
+ s = "BOOTP Reply";
+
+ printf("Received %s packet"
" on %s from ",
+ s,
ifctx->ireq.ifr_name);
print_in_addr(gctx->reply.siaddr);
if (gctx->reply.giaddr.s_addr !=
@@ -763,6 +916,21 @@ bootpc_call(struct bootpc_globalcontext *gctx,
if (gctx->secs > BOOTP_TIMEOUT && BOOTP_TIMEOUT > 0)
break;
#endif
+ /* Force a retry if halfway in DHCP negotiation */
+ retry = 0;
+ for (ifctx = gctx->interfaces; ifctx != NULL;
+ ifctx = ifctx->next) {
+ if (ifctx->state == IF_DHCP_OFFERED) {
+ if (ifctx->dhcpquerytype == DHCP_DISCOVER)
+ retry = 1;
+ else
+ ifctx->state = IF_DHCP_UNRESOLVED;
+ }
+ }
+
+ if (retry != 0)
+ continue;
+
if (gotrootpath != 0) {
gctx->gotrootpath = gotrootpath;
if (rtimo != 0 && time_second >= rtimo)
@@ -776,8 +944,10 @@ bootpc_call(struct bootpc_globalcontext *gctx,
*/
for (ifctx = gctx->interfaces; ifctx != NULL; ifctx = ifctx->next) {
- if (ifctx->state != IF_BOOTP_RESOLVED) {
- printf("BOOTP timeout for interface %s\n",
+ if (bootpc_ifctx_isresolved(ifctx) == 0) {
+ printf("%s timeout for interface %s\n",
+ ifctx->dhcpquerytype != DHCP_NOMSG ?
+ "DHCP" : "BOOTP",
ifctx->ireq.ifr_name);
}
}
@@ -790,7 +960,7 @@ bootpc_call(struct bootpc_globalcontext *gctx,
}
#ifndef BOOTP_NFSROOT
for (ifctx = gctx->interfaces; ifctx != NULL; ifctx = ifctx->next) {
- if (ifctx->state == IF_BOOTP_RESOLVED) {
+ if (bootpc_ifctx_isresolved(ifctx) != 0) {
error = 0;
goto out;
}
@@ -920,7 +1090,7 @@ bootpc_adjust_interface(struct bootpc_ifcontext *ifctx,
netmask = &ifctx->netmask;
gw = &ifctx->gw;
- if (ifctx->state != IF_BOOTP_RESOLVED) {
+ if (bootpc_ifctx_isresolved(ifctx) == 0) {
/* Shutdown interfaces where BOOTP failed */
@@ -1161,12 +1331,11 @@ bootpc_compose_query(ifctx, gctx, procp)
struct bootpc_globalcontext *gctx;
struct proc *procp;
{
-
- gctx->gotrootpath = 0;
- gctx->gotswappath = 0;
- gctx->gotgw = 0;
+ unsigned char *vendp;
+ uint32_t leasetime;
+
ifctx->gotrootpath = 0;
-
+
bzero((caddr_t) &ifctx->call, sizeof(ifctx->call));
/* bootpc part */
@@ -1174,19 +1343,53 @@ bootpc_compose_query(ifctx, gctx, procp)
ifctx->call.htype = 1; /* 10mb ethernet */
ifctx->call.hlen = ifctx->sdl->sdl_alen;/* Hardware address length */
ifctx->call.hops = 0;
- ifctx->xid++;
+ if (bootpc_ifctx_isunresolved(ifctx) != 0)
+ ifctx->xid++;
ifctx->call.xid = txdr_unsigned(ifctx->xid);
bcopy(LLADDR(ifctx->sdl), &ifctx->call.chaddr, ifctx->sdl->sdl_alen);
- ifctx->call.vend[0] = 99; /* RFC1048 cookie */
- ifctx->call.vend[1] = 130;
- ifctx->call.vend[2] = 83;
- ifctx->call.vend[3] = 99;
- ifctx->call.vend[4] = TAG_MAXMSGSIZE;
- ifctx->call.vend[5] = 2;
- ifctx->call.vend[6] = (sizeof(struct bootp_packet) >> 8) & 255;
- ifctx->call.vend[7] = sizeof(struct bootp_packet) & 255;
- ifctx->call.vend[8] = TAG_END;
+ vendp = ifctx->call.vend;
+ *vendp++ = 99; /* RFC1048 cookie */
+ *vendp++ = 130;
+ *vendp++ = 83;
+ *vendp++ = 99;
+ *vendp++ = TAG_MAXMSGSIZE;
+ *vendp++ = 2;
+ *vendp++ = (sizeof(struct bootp_packet) >> 8) & 255;
+ *vendp++ = sizeof(struct bootp_packet) & 255;
+ ifctx->dhcpquerytype = DHCP_NOMSG;
+ switch (ifctx->state) {
+ case IF_DHCP_UNRESOLVED:
+ *vendp++ = TAG_DHCP_MSGTYPE;
+ *vendp++ = 1;
+ *vendp++ = DHCP_DISCOVER;
+ ifctx->dhcpquerytype = DHCP_DISCOVER;
+ ifctx->gotdhcpserver = 0;
+ break;
+ case IF_DHCP_OFFERED:
+ *vendp++ = TAG_DHCP_MSGTYPE;
+ *vendp++ = 1;
+ *vendp++ = DHCP_REQUEST;
+ ifctx->dhcpquerytype = DHCP_REQUEST;
+ *vendp++ = TAG_DHCP_REQ_ADDR;
+ *vendp++ = 4;
+ memcpy(vendp, &ifctx->reply.yiaddr, 4);
+ vendp += 4;
+ if (ifctx->gotdhcpserver != 0) {
+ *vendp++ = TAG_DHCP_SERVERID;
+ *vendp++ = 4;
+ memcpy(vendp, &ifctx->dhcpserver, 4);
+ vendp += 4;
+ }
+ *vendp++ = TAG_DHCP_LEASETIME;
+ *vendp++ = 4;
+ leasetime = htonl(300);
+ memcpy(vendp, &leasetime, 4);
+ vendp += 4;
+ default:
+ ;
+ }
+ *vendp = TAG_END;
ifctx->call.secs = 0;
ifctx->call.flags = htons(0x8000); /* We need an broadcast answer */
@@ -1537,6 +1740,10 @@ bootpc_init(void)
#endif
}
+ gctx->gotrootpath = 0;
+ gctx->gotswappath = 0;
+ gctx->gotgw = 0;
+
for (ifctx = gctx->interfaces; ifctx != NULL; ifctx = ifctx->next)
bootpc_fakeup_interface(ifctx, gctx, procp);
@@ -1559,7 +1766,7 @@ bootpc_init(void)
mountopts(&nd->swap_args, NULL);
for (ifctx = gctx->interfaces; ifctx != NULL; ifctx = ifctx->next)
- if (ifctx->state == IF_BOOTP_RESOLVED)
+ if (bootpc_ifctx_isresolved(ifctx) != 0)
bootpc_decode_reply(nd, ifctx, gctx);
if (gctx->gotswappath == 0)
@@ -1582,7 +1789,7 @@ bootpc_init(void)
for (ifctx = gctx->interfaces;
ifctx != NULL;
ifctx = ifctx->next)
- if (ifctx->state == IF_BOOTP_RESOLVED)
+ if (bootpc_ifctx_isresolved(ifctx) != 0)
break;
}
if (ifctx == NULL)
OpenPOWER on IntegriCloud