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