This patch allow users to do PXE netbooting when they select NAT networking mode. In NAT mode, we'll use the slirp dhcp & tftpserver, the content of the tftpserver will be a "simple user directory" (in my case my home dir).

So this patch fills the filename field of a bootp answer if the dhcp client announce to looking at a pxe server.

I did my tests with the famous pxelinux (thx HPA for this tool) but it shows that the slirp tftp server didn't allow tsize negociation. I did a very simple hack to make this working. So the patch adds the tsize negociation to the slirp tftp server.

At the end, I did a patch for allowing the configuration of the tftp dir and the name of the boot strap.
You can define them by setting the setextradata on

"VBoxInternal/Devices/pcnet/0/LUN#0/Config/bootp_filename" "/pxelinux.0"
"VBoxInternal/Devices/pcnet/0/LUN#0/Config/tftpdir" "/home/erwan/.VirtualBox/tftpboot/"

This patch is not perfect, I'm not a developper so I may missed a lot of things, did some things which doesn't watch your quality process. I did a Proof-of-concept, it works but may need some improvements to make it "ready to use". It may just need a cleanup from experienced VBox devs.

As I said in the subject, this contribution is under the MIT licence.

I hope this patch could be integrated from a way or another because it provides users a very easy way to do pxe booting without having the knowledge of how to setup the dhcp/tftp/bridge stuff.

Regards,
Erwan Velu
Index: src/VBox/Devices/Network/DrvNAT.cpp
===================================================================
--- src/VBox/Devices/Network/DrvNAT.cpp	(révision 880)
+++ src/VBox/Devices/Network/DrvNAT.cpp	(copie de travail)
@@ -298,7 +298,30 @@
     RTCritSectDelete(&pData->CritSect);
 }
 
+static int drvNATFindPxeOptions(PCFGMNODE pCfgHandle,char *bootp_filename, char *tftpdir) {
 
+   char bootp[255];
+   char tftp[255];   
+   /* Find bootp_filename */
+   int rc = CFGMR3QueryString(pCfgHandle, "bootp_filename", &bootp[0], sizeof(bootp));
+   if (rc == VERR_CFGM_VALUE_NOT_FOUND) {
+	  LogRel(("Error, bootp_filename can't be found\n"));
+   } else {
+	  LogRel(("Found bootp_filename = %s\n",bootp));
+	  strncpy(bootp_filename,bootp,255);
+   }
+   
+   rc = CFGMR3QueryString(pCfgHandle, "tftpdir", &tftp[0], sizeof(tftp));
+   if (rc == VERR_CFGM_VALUE_NOT_FOUND) {
+	  LogRel(("Error, tftpdir can't be found\n"));
+   } else {
+	  LogRel(("Found tftpdir = %s\n",tftp));
+	  strncpy(tftpdir,tftp,255);
+   }
+
+ return 0;
+}
+
 /**
  * Sets up the redirectors.
  *
@@ -388,14 +411,14 @@
 {
     PDRVNAT pData = PDMINS2DATA(pDrvIns, PDRVNAT);
     LogFlow(("drvNATConstruct:\n"));
-
+    
     /*
      * Validate the config.
      */
-    if (!CFGMR3AreValuesValid(pCfgHandle, "\0"))
+    if (!CFGMR3AreValuesValid(pCfgHandle, "bootp_filename\0tftpdir\0"))
         return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES, "");
 
-    /*
+        /*
      * Init the static parts.
      */
     pData->pDrvIns                      = pDrvIns;
@@ -433,10 +456,13 @@
         if (VBOX_SUCCESS(rc))
         {
 #endif
+	    char  bootp_filename[255];
+	    char  tftpdir[255];
+	    drvNATFindPxeOptions(pCfgHandle,bootp_filename,tftpdir);
             /*
              * Initialize slirp.
              */
-            rc = slirp_init();
+            rc = slirp_init(bootp_filename,tftpdir);
             if (VBOX_SUCCESS(rc))
             {
                 rc = drvNATConstructRedir(pCfgHandle);
Index: src/VBox/Devices/Network/slirp/slirp.h
===================================================================
--- src/VBox/Devices/Network/slirp/slirp.h	(révision 880)
+++ src/VBox/Devices/Network/slirp/slirp.h	(copie de travail)
@@ -345,7 +345,7 @@
 void if_output _P((struct socket *, struct mbuf *));
 
 /* ip_input.c */
-void ip_init _P((void));
+void ip_init _P((char *bootp_filename, char*tftpdir));
 void ip_input _P((struct mbuf *));
 struct ip * ip_reass _P((register struct ipasfrag *, register struct ipq *));
 void ip_freef _P((struct ipq *));
Index: src/VBox/Devices/Network/slirp/tftp.c
===================================================================
--- src/VBox/Devices/Network/slirp/tftp.c	(révision 880)
+++ src/VBox/Devices/Network/slirp/tftp.c	(copie de travail)
@@ -171,6 +171,49 @@
   return 0;
 }
 
+static int tftp_send_oack(struct tftp_session *spt, 
+			   int errorcode, const char *msg,
+			   struct tftp_t *recv_tp)
+{
+  struct sockaddr_in saddr, daddr;
+  struct mbuf *m;
+  struct tftp_t *tp;
+  int nobytes;
+  char errcode[16];
+  m = m_get();
+
+  if (!m) {
+    return -1;
+  }
+
+  memset(m->m_data, 0, m->m_size);
+
+  m->m_data += if_maxlinkhdr;
+  tp = (void *)m->m_data;
+  m->m_data += sizeof(struct udpiphdr);
+ 
+  tp->tp_op = htons(TFTP_OACK);
+  strcpy(tp->x.tp_buf,msg);
+  sprintf(tp->x.tp_buf+strlen(msg)+1,"%u",errorcode);
+  sprintf(errcode,"%u",errorcode);
+
+  saddr.sin_addr = recv_tp->ip.ip_dst;
+  saddr.sin_port = recv_tp->udp.uh_dport;
+
+  daddr.sin_addr = spt->client_ip;
+  daddr.sin_port = spt->client_port;
+
+  nobytes = 2;
+
+  m->m_len = sizeof(struct tftp_t) - 514 + 2 + strlen(errcode) + strlen(msg) - 
+        sizeof(struct ip) - sizeof(struct udphdr);
+
+  udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
+
+//  tftp_session_terminate(spt);
+
+  return 0;
+}
 static int tftp_send_data(struct tftp_session *spt, 
 			  u_int16_t block_nr,
 			  struct tftp_t *recv_tp)
@@ -232,12 +275,15 @@
   return 0;
 }
 
-static void tftp_handle_rrq(struct tftp_t *tp, int pktlen)
+static void tftp_handle_rrq(struct tftp_t *tp, int pktlen, char *tftpdir)
 {
   struct tftp_session *spt;
-  int s, k, n;
+  int s, k, n, blksize,tsize,p;
+  char *blksize_pos, *tsize_pos;
   u_int8_t *src, *dst;
-
+  char tftp_path[TFTP_FILENAME_MAX];
+  struct stat stat_p;	
+  
   s = tftp_session_allocate(tp);
 
   if (s < 0) {
@@ -268,7 +314,20 @@
   if (k >= n) {
     return;
   }
-  
+   /* only allow exported prefixes */
+#ifndef VBOX
+  if (!tftp_prefix) {
+#else /* VBOX */
+  if (!tftpdir) {
+#endif /* VBOX */
+      tftp_send_error(spt, 2, "Access violation", tp);
+      return;
+  }
+
+  p=snprintf(tftp_path,TFTP_FILENAME_MAX, "%s/%s",tftpdir, spt->filename);
+  if (p>=TFTP_FILENAME_MAX) return;
+  strncpy((char *)spt->filename,tftp_path,TFTP_FILENAME_MAX);
+
   k++;
   
   /* check mode */
@@ -281,8 +340,34 @@
       return;
   }
 
+ k+=6;/* skipping octet*/
+
+ /* Looking for other options*/ 
+ while (k<n) {
+   blksize_pos=strstr(&src[k],"blksize");
+   if (blksize_pos != NULL) {
+	blksize=atoi(blksize_pos+strlen("blksize")+1);
+	k+=strlen("blksize")+strlen(blksize_pos+strlen("blksize")+1);
+   }
+
+   tsize_pos=strstr(&src[k],"tsize");
+   if (tsize_pos != NULL) {
+	tsize=atoi(tsize_pos+strlen("tsize")+1);
+	if (tsize==0) {
+		 /* we need to return the file size to the client*/
+		 if ( stat (spt->filename, &stat_p) == 0 ) {
+			tsize=stat_p.st_size;
+		 } else	 {
+			tftp_send_error(spt, 1, "File not found", tp);
+		 }
+	}
+	tftp_send_oack(spt,tsize,"tsize",tp);
+	k+=strlen("tsize")+strlen(tsize_pos+strlen("tsize")+1);
+   }
+ k++;
+ }
+
   /* do sanity checks on the filename */
-
 #ifndef VBOX
   if ((spt->filename[0] != '/')
       || (spt->filename[strlen(spt->filename) - 1] == '/')
@@ -296,21 +381,8 @@
       return;
   }
 
-  /* only allow exported prefixes */
 
-#ifndef VBOX
-  if (!tftp_prefix
-      || (strncmp(spt->filename, tftp_prefix, strlen(tftp_prefix)) != 0)) {
-#else /* VBOX */
-  if (!tftp_prefix
-      || (strncmp((char *)spt->filename, tftp_prefix, strlen(tftp_prefix)) != 0)) {
-#endif /* VBOX */
-      tftp_send_error(spt, 2, "Access violation", tp);
-      return;
-  }
-
   /* check if the file exists */
-  
   if (tftp_read_data(spt, 0, spt->filename, 0) < 0) {
       tftp_send_error(spt, 1, "File not found", tp);
       return;
@@ -336,13 +408,13 @@
   }
 }
 
-void tftp_input(struct mbuf *m)
+void tftp_input(struct mbuf *m, char *tftpdir)
 {
   struct tftp_t *tp = (struct tftp_t *)m->m_data;
 
   switch(ntohs(tp->tp_op)) {
   case TFTP_RRQ:
-    tftp_handle_rrq(tp, m->m_len);
+    tftp_handle_rrq(tp, m->m_len, tftpdir);
     break;
 
   case TFTP_ACK:
Index: src/VBox/Devices/Network/slirp/tftp.h
===================================================================
--- src/VBox/Devices/Network/slirp/tftp.h	(révision 880)
+++ src/VBox/Devices/Network/slirp/tftp.h	(copie de travail)
@@ -9,6 +9,7 @@
 #define TFTP_DATA   3
 #define TFTP_ACK    4
 #define TFTP_ERROR  5
+#define TFTP_OACK   6
 
 #define TFTP_FILENAME_MAX 512
 
@@ -29,4 +30,4 @@
   } x;
 };
 
-void tftp_input(struct mbuf *m);
+void tftp_input(struct mbuf *m, char *tftpdir);
Index: src/VBox/Devices/Network/slirp/bootp.c
===================================================================
--- src/VBox/Devices/Network/slirp/bootp.c	(révision 880)
+++ src/VBox/Devices/Network/slirp/bootp.c	(copie de travail)
@@ -107,7 +107,7 @@
 }
 
 static void dhcp_decode(const uint8_t *buf, int size,
-                        int *pmsg_type)
+                        int *pmsg_type, bool *pxe_request)
 {
     const uint8_t *p, *p_end;
     int len, tag;
@@ -139,6 +139,11 @@
                 if (len >= 1)
                     *pmsg_type = p[0];
                 break;
+	    case RFC1533_CLASSIDENTIFIER:
+		if ((strncmp(p,"PXEClient",9) == 0) || (strncmp(p,"Etherboot",9) == 0)) {
+			*pxe_request=true;
+		}
+		break;
             default:
                 break;
             }
@@ -147,7 +152,7 @@
     }
 }
 
-static void bootp_reply(struct bootp_t *bp)
+static void bootp_reply(struct bootp_t *bp, char *bootp_filename)
 {
     BOOTPClient *bc;
     struct mbuf *m;
@@ -156,9 +161,10 @@
     struct in_addr dns_addr;
     int dhcp_msg_type, val;
     uint8_t *q;
+    bool pxe_request=false;
 
     /* extract exact DHCP msg type */
-    dhcp_decode(bp->bp_vend, DHCP_OPT_LEN, &dhcp_msg_type);
+    dhcp_decode(bp->bp_vend, DHCP_OPT_LEN, &dhcp_msg_type,&pxe_request);
     dprintf("bootp packet op=%d msgtype=%d\n", bp->bp_op, dhcp_msg_type);
     
     if (dhcp_msg_type == 0)
@@ -223,6 +229,10 @@
                 addr >> 24, (addr >> 16) & 0xff, (addr >> 8) & 0xff, addr & 0xff));
     }
 #endif /* VBOX */
+    if (pxe_request==true) {
+	    LogRel(("PXE Request Detected, setting filename\n"));
+	    strcpy(rbp->bp_file,bootp_filename);
+    }
     dprintf("offered addr=%08x\n", ntohl(daddr.sin_addr.s_addr));
 
     saddr.sin_addr.s_addr = htonl(ntohl(special_addr.s_addr) | CTL_ALIAS);
@@ -303,11 +313,11 @@
     udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
 }
 
-void bootp_input(struct mbuf *m)
+void bootp_input(struct mbuf *m,char *bootp_filename)
 {
     struct bootp_t *bp = mtod(m, struct bootp_t *);
 
     if (bp->bp_op == BOOTP_REQUEST) {
-        bootp_reply(bp);
+        bootp_reply(bp,bootp_filename);
     }
 }
Index: src/VBox/Devices/Network/slirp/ip_input.c
===================================================================
--- src/VBox/Devices/Network/slirp/ip_input.c	(révision 880)
+++ src/VBox/Devices/Network/slirp/ip_input.c	(copie de travail)
@@ -54,11 +54,11 @@
  * All protocols not implemented in kernel go to raw IP protocol handler.
  */
 void
-ip_init()
+ip_init(char *bootp_filename, char *tftpdir)
 {
 	ipq.next = ipq.prev = ptr_to_u32(&ipq);
 	ip_id = tt.tv_sec & 0xffff;
-	udp_init();
+	udp_init(bootp_filename, tftpdir);
 	tcp_init();
 	ip_defttl = IPDEFTTL;
 }
Index: src/VBox/Devices/Network/slirp/bootp.h
===================================================================
--- src/VBox/Devices/Network/slirp/bootp.h	(révision 880)
+++ src/VBox/Devices/Network/slirp/bootp.h	(copie de travail)
@@ -57,6 +57,7 @@
 #define RFC1533_NBSCOPE		47
 #define RFC1533_XFS		48
 #define RFC1533_XDM		49
+#define RFC1533_CLASSIDENTIFIER	60
 
 #define RFC2132_REQ_ADDR	50
 #define RFC2132_LEASE_TIME      51
@@ -113,4 +114,4 @@
     uint8_t bp_vend[DHCP_OPT_LEN];
 };
 
-void bootp_input(struct mbuf *m);
+void bootp_input(struct mbuf *m, char *bootp_filename);
Index: src/VBox/Devices/Network/slirp/udp.c
===================================================================
--- src/VBox/Devices/Network/slirp/udp.c	(révision 880)
+++ src/VBox/Devices/Network/slirp/udp.c	(copie de travail)
@@ -48,6 +48,8 @@
 struct udpstat udpstat;
 
 struct socket udb;
+char bootp_filename[255];
+char tftpdir[255];
 
 /*
  * UDP protocol implementation.
@@ -62,9 +64,11 @@
 struct	socket *udp_last_so = &udb;
 
 void
-udp_init()
+udp_init(char *bootp, char *tftp)
 {
 	udb.so_next = udb.so_prev = &udb;
+	strncpy(bootp_filename,bootp,255);
+	strncpy(tftpdir,tftp,255);
 }
 /* m->m_data  points at ip packet header 
  * m->m_len   length ip packet 
@@ -149,7 +153,7 @@
          *  handle DHCP/BOOTP
          */
         if (ntohs(uh->uh_dport) == BOOTP_SERVER) {
-            bootp_input(m);
+            bootp_input(m,bootp_filename);
             goto bad;
         }
 
@@ -157,7 +161,7 @@
          *  handle TFTP
          */
         if (ntohs(uh->uh_dport) == TFTP_SERVER) {
-            tftp_input(m);
+            tftp_input(m,tftpdir);
             goto bad;
         }
 
Index: src/VBox/Devices/Network/slirp/udp.h
===================================================================
--- src/VBox/Devices/Network/slirp/udp.h	(révision 880)
+++ src/VBox/Devices/Network/slirp/udp.h	(copie de travail)
@@ -96,7 +96,7 @@
 extern struct socket udb;
 struct mbuf;
 
-void udp_init _P((void));
+void udp_init _P((char *bootp_filename, char *tftpdir));
 void udp_input _P((register struct mbuf *, int));
 int udp_output _P((struct socket *, struct mbuf *, struct sockaddr_in *));
 int udp_attach _P((struct socket *));
Index: src/VBox/Devices/Network/slirp/libslirp.h
===================================================================
--- src/VBox/Devices/Network/slirp/libslirp.h	(révision 880)
+++ src/VBox/Devices/Network/slirp/libslirp.h	(copie de travail)
@@ -29,7 +29,7 @@
 #ifndef VBOX
 void slirp_init(void);
 #else /* VBOX */
-int slirp_init(void);
+int slirp_init(char *bootp_filename, char *tftpdir);
 void slirp_term(void);
 void slirp_link_up(void);
 void slirp_link_down(void);
Index: src/VBox/Devices/Network/slirp/slirp.c
===================================================================
--- src/VBox/Devices/Network/slirp/slirp.c	(révision 880)
+++ src/VBox/Devices/Network/slirp/slirp.c	(copie de travail)
@@ -191,7 +191,7 @@
 #else /* VBOX */
 /** Number of slirp users. Used for making sure init and term are only executed once. */
 static int g_cUsers = 0;
-int slirp_init(void)
+int slirp_init(char *bootp_filename, char *tftpdir)
 {
     if (g_cUsers != 0) {
         g_cUsers++;
@@ -215,7 +215,7 @@
     link_up = 1;
 
     if_init();
-    ip_init();
+    ip_init(bootp_filename, tftpdir);
 
     /* Initialise mbufs *after* setting the MTU */
     m_init();
_______________________________________________
vbox-dev mailing list
[email protected]
http://vbox.innotek.de/mailman/listinfo/vbox-dev

Reply via email to