i386/65523: [patch] PXE loader malfunction in multiple PXE server environment

Ralf Wenk RZ-FreeBSD0404 at fh-karlsruhe.de
Wed Apr 14 03:40:09 PDT 2004


>Number:         65523
>Category:       i386
>Synopsis:       [patch] PXE loader malfunction in multiple PXE server environment
>Confidential:   no
>Severity:       serious
>Priority:       low
>Responsible:    freebsd-i386
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Wed Apr 14 03:40:09 PDT 2004
>Closed-Date:
>Last-Modified:
>Originator:     Ralf Wenk <RZ-FreeBSD0404 at fh-karlsruhe.de>
>Release:        FreeBSD 5.2.1-RELEASE-p4 i386
>Organization:
FH Karlsruhe, University of Applied Sciences
>Environment:
System: FreeBSD RZ-TP600E 5.2.1-RELEASE-p4 FreeBSD 5.2.1-RELEASE-p4 #10: Wed Apr 7 17:02:55 CEST 2004 root at RZ-TP600E:/usr/src/sys/i386/compile/tp600e i386


>Description:
In an environment with multiple PXE servers the assumption that the first
answer to an request is the one choosen by PXE may be wrong. PXE looks
into the answers and uses a finite state machine to decide which answer
to use and how to react. So simple extracting needed information by doing
an additional DHCP cycle could lead to unexpected results. Extracting
that information from PXE is a much better way.

>How-To-Repeat:
Set up a multi DHCP and PXE server environment with a DHCP proxy.
Try to use the reply data provided by the proxy instead of the one of
a server in the broadcast domain. The current code will always choose the
first (fastest) answer.

>Fix:

--- pxe.c.udiff begins here ---
--- pxe.c.ori	Tue Aug 26 01:28:31 2003
+++ pxe.c	Wed Apr 14 11:33:26 2004
@@ -133,6 +133,76 @@
 };
 
 /*
+ * 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 *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;
+
+#ifdef	PXE_DEBUG
+		if (pxe_debug)
+			printf("pxe_vend_rfc1048 dhcp info. len=%d\n", len);
+#endif
+		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_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
+			}
+			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 +344,40 @@
 	}
 	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.
 		 */
-		bootp(pxe_sock, BOOTP_PXE);
+#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
+		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])
--- pxe.c.udiff ends here ---


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


More information about the freebsd-i386 mailing list