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