kern/73004: [patch] PXE loader malfunction in multiple PXE server environment

Ralf Wenk RZ-FreeBSD1004 at fh-karlsruhe.de
Fri Oct 22 06:30:27 PDT 2004


>Number:         73004
>Category:       kern
>Synopsis:       [patch] PXE loader malfunction in multiple PXE server environment
>Confidential:   no
>Severity:       serious
>Priority:       low
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Fri Oct 22 13:30:26 GMT 2004
>Closed-Date:
>Last-Modified:
>Originator:     Ralf Wenk
>Release:        FreeBSD 5.3-STABLE i386
>Organization:
FH Karlsruhe, University of Applied Sciences
>Environment:
System: FreeBSD RZ-Wenk1 5.3-STABLE FreeBSD 5.3-STABLE #0: Fri Oct 22 11:14:32 CEST 2004 root at RZ-Wenk1:/usr/obj/usr/src/sys/fsc-t-diskless i386


>Description:
The pxeboot still do not support an environment with multipe DHCP/PXE
bootservers. This patch uses the PXEs decision of which bootserver to
use as well as enables the kernel to return values for the dhcp.*
kernel environment variables used in /etc/rc.d/hostname and
/etc/rc.d/resolv. See also problem report 65523.

>How-To-Repeat:
Use a multipe DHCP/PXE environment an try not to use the nearest DHCP/PXE
bootserver.
	
>Fix:

	

--- pxe.c.udiff begins here ---
--- pxe.c.old	Fri Oct 22 15:03:44 2004
+++ pxe.c	Fri Oct 22 15:04:14 2004
@@ -42,6 +42,7 @@
 #include <nfsv2.h>
 #include <iodesc.h>
 
+#define SUPPORT_DHCP			/* PXE uses DHCP */
 #include <bootp.h>
 #include <bootstrap.h>
 #include "btxv86.h"
@@ -133,6 +134,102 @@
 };
 
 /*
+ * This is ugly:
+ * libstand/bootp.c contains a similar RFC 1048 parser function named
+ * vend_rfc1048(), but the function is local to bootp.c and also uses
+ * a variable local to bootp.c if compiled with SUPPORT_DHCP defined.
+ * While the first problem could easily be fixed the second one would
+ * need an additional function in bootp.c to set the local variable.
+ */
+static int
+pxe_vend_rfc1048(void)
+{
+	char vm_rfc1048[4] = VM_RFC1048;
+	u_char buffer[256];
+	u_char *cp, *ep;
+	int size;
+	u_char tag;
+
+	if (bcmp(bootplayer.vendor.v.magic, vm_rfc1048, sizeof(vm_rfc1048)) == 0)
+	{
+		cp = (u_char *)bootplayer.vendor.d;
+		ep = cp + sizeof(bootplayer.vendor.d);
+
+		/* Step over magic cookie */
+		cp += sizeof(int);
+
+		while (cp < ep) {
+			tag = *cp++;
+			size = *cp++;
+			if (tag == TAG_END)
+				break;
+
+			if (tag == TAG_SUBNET_MASK) {
+				bcopy(cp, &netmask, sizeof(netmask));
+#ifdef	PXE_DEBUG
+				printf("pxe_vend_rfc1048: SUBNET_MASK %s\n", intoa(netmask));
+#endif
+			}
+			if (tag == TAG_GATEWAY) {
+				bcopy(cp, &gateip.s_addr, sizeof(gateip.s_addr));
+#ifdef	PXE_DEBUG
+				printf("pxe_vend_rfc1048: GATEWAY %s\n", intoa(gateip.s_addr));
+#endif
+			}
+			if (tag == TAG_SWAPSERVER) {
+				/* let it override bp_siaddr */
+				bcopy(cp, &rootip.s_addr, sizeof(swapip.s_addr));
+#ifdef	PXE_DEBUG
+				printf("pxe_vend_rfc1048: SWAPSERVER %s\n", intoa(rootip.s_addr));
+#endif
+			}
+			if (tag == TAG_ROOTPATH) {
+				strncpy(rootpath, (char *)cp, sizeof(rootpath));
+				rootpath[size] = '\0';
+#ifdef	PXE_DEBUG
+				printf("pxe_vend_rfc1048: ROOTPATH %s\n", rootpath);
+#endif
+			}
+			if (tag == TAG_HOSTNAME) {
+				strncpy(hostname, (char *)cp, sizeof(hostname));
+				hostname[size] = '\0';
+#ifdef	PXE_DEBUG
+				printf("pxe_vend_rfc1048: HOSTNAME %s\n", hostname);
+#endif
+				setenv("dhcp.host-name", hostname, 1);
+			}
+			if (tag == TAG_DOMAINNAME) {
+				strncpy(buffer, (char *)cp, sizeof(buffer));
+				buffer[size] = '\0';
+#ifdef	PXE_DEBUG
+				printf("pxe_vend_rfc1048: DOMAINNAME %s\n", buffer);
+#endif
+				setenv("dhcp.domain-name", buffer, 1);
+			}
+			if (tag == TAG_DOMAIN_SERVER) {
+				int count = 0;		/* DNS server(s) */
+
+				buffer[0] = '\0';
+				while ( count < (size >> 2)) {
+					strcat(buffer, inet_ntoa(*((struct in_addr*)cp + count)));
+					strcat(buffer, ",");
+					count++;
+				}
+				buffer[strlen(buffer) - 1] = '\0';
+#ifdef	PXE_DEBUG
+				printf("pxe_vend_rfc1048: DOMAINNAMESERVER %s\n", buffer);
+#endif
+				setenv("dhcp.domain-name-server", buffer, 1);
+			}
+			cp += size;
+		}
+		return(0);
+	}
+	else
+	  return(-1);
+}
+
+/*
  * This function is called by the loader to enable PXE support if we
  * are booted by PXE.  The passed in pointer is a pointer to the
  * PXENV+ structure.
@@ -274,12 +371,52 @@
 	}
 	if (rootip.s_addr == 0) {
 		/*
-		 * Do a bootp/dhcp request to find out where our
-		 * NFS/TFTP server is.  Even if we dont get back
-		 * the proper information, fall back to the server
-		 * which brought us to life and a default rootpath.
+		 * PXE looks into the content of all answers to its request
+		 * and uses a finite state machine to select one of them. 
+		 * It is even possible that there are several requests
+		 * generated and the information collected is combined.
+		 * E.g. in the case of several PXE servers it is possible
+		 * that not the answer of the fastest one is selected. This
+		 * breaks the simple way of just using the first answer.
+		 * But fortunately PXE stores the selected information 
+		 * in the bootplayer structure for us to use.
+		 */
+#ifdef	PXE_DEBUG
+		printf("pxe_open: bootplayer.cip: %s\n", intoa( bootplayer.cip));
+		printf("pxe_open: bootplayer.yip: %s\n", intoa( bootplayer.yip));
+		printf("pxe_open: bootplayer.sip: %s\n", intoa( bootplayer.sip));
+		printf("pxe_open: bootplayer.gip: %s\n", intoa( bootplayer.gip));
+		sprintf(temp, "%6D", bootplayer.CAddr, ":");
+		printf("pxe_open: bootplayer.CAddr: %s\n", temp);
+		printf("pxe_open: bootplayer.Sname: %s\n", bootplayer.Sname);
+		printf("pxe_open: bootplayer.bootfile: %s\n", bootplayer.bootfile);
+#endif
+		/*
+		 * Calling bootp() sets some global variables as a side effect
+		 * which are used later in the (legacy) code. As we try to avoid
+		 * calling boot(), we had to do the same thing.
 		 */
-		bootp(pxe_sock, BOOTP_PXE);
+		myip.s_addr = bootplayer.yip;
+		if (IN_CLASSA(ntohl(myip.s_addr)))
+			netmask = htonl(IN_CLASSA_NET);
+		else if (IN_CLASSB(ntohl(myip.s_addr)))
+			netmask = htonl(IN_CLASSB_NET);
+		else
+			netmask = htonl(IN_CLASSC_NET);
+		if (pxe_vend_rfc1048() != 0) {
+			/*
+			 * We are not happy with the information found in the
+			 * bootplayer structure. As some last resort
+			 * do a bootp/dhcp request to find out where our
+			 * NFS/TFTP server is. Even if we dont get back
+			 * the proper information, fall back to the server
+			 * which brought us to life and a default rootpath.
+			 */
+			bootp(pxe_sock, BOOTP_PXE);
+		}
+#ifdef	PXE_DEBUG
+		printf("pxe_open: server addr: %s\n", inet_ntoa(rootip));
+#endif
 		if (rootip.s_addr == 0)
 			rootip.s_addr = bootplayer.sip;
 		if (!rootpath[1])
@@ -299,7 +436,7 @@
 		printf("pxe_open: server path: %s\n", rootpath);
 		printf("pxe_open: gateway ip:  %s\n", inet_ntoa(gateip));
 
-		setenv("boot.netif.ip", inet_ntoa(myip), 1);
+		setenv("boot.netif.ip", intoa(bootplayer.yip), 1);
 		setenv("boot.netif.netmask", intoa(netmask), 1);
 		setenv("boot.netif.gateway", inet_ntoa(gateip), 1);
 		if (bootplayer.Hardware == ETHER_TYPE) {
--- pxe.c.udiff ends here ---


>Release-Note:
>Audit-Trail:
>Unformatted:


More information about the freebsd-bugs mailing list