[syslinux:elflink] net: Return of the legacy network stack

syslinux-bot for Matt Fleming matt.fleming at intel.com
Wed Mar 6 09:42:07 PST 2013


Commit-ID:  16aa878d78086e9bc1c1f1053dc24da295f81e05
Gitweb:     http://www.syslinux.org/commit/16aa878d78086e9bc1c1f1053dc24da295f81e05
Author:     Matt Fleming <matt.fleming at intel.com>
AuthorDate: Tue, 26 Feb 2013 16:24:56 +0000
Committer:  Matt Fleming <matt.fleming at intel.com>
CommitDate: Wed, 6 Mar 2013 17:04:39 +0000

net: Return of the legacy network stack

While it's nice having the shiny new lwIP stack, there is definitely
merit in being able to choose between two different network stacks. We
want to keep the legacy network stack around as it is known to handle
funky BIOS implementations and provides a good reference point when
bugs are suspected in the lwIP code.

Users now have a choice of .0 files. pxelinux.0 uses the legacy
network stack, while lpxelinux.0 uses lwIP.

Note that not every protocol is converted to using this new API. The
http, ftp and tcp code is still inherently tied to the netconn API,
and is only available with lpxelinux.0 and the lwIP stack. It's
unlikely that this code will ever be fixed up to work with the legacy
network stack.

Network stack operations are abstracted behind the net_core_*
interface, and each network stack has private data fields contained
within a struct net_private.

Cc: H. Peter Anvin <hpa at zytor.com>
Cc: Eric W. Biederman <ebiederm at xmission.com>
Cc: Gene Cumm <gene.cumm at gmail.com>
Signed-off-by: Matt Fleming <matt.fleming at intel.com>

---
 Makefile                   |   7 +-
 core/Makefile              |  63 +++++++-
 core/fs/pxe/core.c         | 207 ++++++++++++++++++++++++++
 core/fs/pxe/ftp.c          |  32 ++--
 core/fs/pxe/http.c         |   8 +-
 core/fs/pxe/pxe.c          |  36 +----
 core/fs/pxe/pxe.h          |  44 ++++--
 core/fs/pxe/tcp.c          |  28 ++--
 core/fs/pxe/tftp.c         | 128 +++++-----------
 core/include/net.h         |  33 +++++
 core/legacynet/core.c      | 186 ++++++++++++++++++++++++
 core/legacynet/dnsresolv.c | 353 +++++++++++++++++++++++++++++++++++++++++++++
 core/legacynet/idle.c      | 112 ++++++++++++++
 core/legacynet/portnum.c   |  68 +++++++++
 core/pxelinux.asm          |   2 +
 15 files changed, 1129 insertions(+), 178 deletions(-)

diff --git a/Makefile b/Makefile
index c241df0..be3425b 100644
--- a/Makefile
+++ b/Makefile
@@ -45,7 +45,8 @@ INSTALLABLE_MODULES = $(MODULES)
 BTARGET  = version.gen version.h version.mk
 BOBJECTS = $(BTARGET) \
 	mbr/*.bin \
-	core/pxelinux.0 core/isolinux.bin core/isolinux-debug.bin \
+	core/pxelinux.0 core/lpxelinux.0 \
+	core/isolinux.bin core/isolinux-debug.bin \
 	gpxe/gpxelinux.0 dos/syslinux.com \
 	win32/syslinux.exe win64/syslinux64.exe \
 	dosutil/*.com dosutil/*.sys \
@@ -72,7 +73,7 @@ INSTALL_SBIN  = extlinux/extlinux
 # Things to install in /usr/lib/syslinux
 INSTALL_AUX   =	core/pxelinux.0 gpxe/gpxelinux.0 gpxe/gpxelinuxk.0 \
 		core/isolinux.bin core/isolinux-debug.bin \
-		dos/syslinux.com \
+		dos/syslinux.com core/lpxelinux.0 \
 		mbr/*.bin $(INSTALLABLE_MODULES)
 INSTALL_AUX_OPT = win32/syslinux.exe win64/syslinux64.exe
 INSTALL_DIAG  =	diag/mbr/handoff.bin \
@@ -85,7 +86,7 @@ INSTALLSUBDIRS = com32 utils dosutil
 EXTBOOTINSTALL = $(INSTALLABLE_MODULES)
 
 # Things to install in /tftpboot
-NETINSTALLABLE = core/pxelinux.0 gpxe/gpxelinux.0 \
+NETINSTALLABLE = core/pxelinux.0 gpxe/gpxelinux.0 core/lpxelinux.0 \
 		 $(INSTALLABLE_MODULES)
 
 all:
diff --git a/core/Makefile b/core/Makefile
index 03c1e66..1890b24 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -26,7 +26,7 @@ include $(MAKEDIR)/embedded.mk
 
 OPTFLAGS =
 INCLUDES = -I./include -I$(com32)/include -I$(com32)/lib \
-	-I./lwip/src/include -I./lwip/src/include/ipv4
+	-I./lwip/src/include -I./lwip/src/include/ipv4 -I./fs/pxe
 
 # This is very similar to cp437; technically it's for Norway and Denmark,
 # but it's unlikely the characters that are different will be used in
@@ -36,7 +36,7 @@ CODEPAGE = cp865
 # The targets to build in this directory...
 BTARGET  = kwdhash.gen \
 	   ldlinux.bss ldlinux.sys ldlinux.bin \
-	   isolinux.bin isolinux-debug.bin pxelinux.0
+	   isolinux.bin isolinux-debug.bin pxelinux.0 lpxelinux.0
 
 # All primary source files for the main syslinux files
 NASMSRC	 := $(wildcard *.asm)
@@ -50,13 +50,32 @@ ALLSRC    = $(NASMSRC) $(NASMHDR) $(CSRC) $(SSRC) $(CHDR) $(OTHERSRC)
 COBJ	 := $(patsubst %.c,%.o,$(CSRC))
 SOBJ	 := $(patsubst %.S,%.o,$(SSRC))
 
-# Don't include console objects
-COBJS	 = $(filter-out ./rawcon.o ./plaincon.o,$(COBJ))
+# To make this compatible with the following $(filter-out), make sure
+# we prefix everything with ./
+CORE_PXE_CSRC = \
+	$(addprefix ./fs/pxe/, dhcp_option.c pxe.c tftp.c urlparse.c)
+
+LPXELINUX_CSRC = $(CORE_PXE_CSRC) \
+	$(shell find ./lwip -name '*.c' -print) \
+	$(addprefix ./fs/pxe/, \
+		core.c dnsresolv.c ftp.c ftp_readdir.c gpxeurl.c http.c \
+		http_readdir.c idle.c isr.c tcp.c)
+
+PXELINUX_CSRC = $(CORE_PXE_CSRC) \
+	$(shell find ./legacynet -name '*.c' -print)
+
+LPXELINUX_OBJS = $(LPXELINUX_CSRC:%.c=%.o)
+PXELINUX_OBJS  = $(PXELINUX_CSRC:%.c=%.o)
+
+# Don't include console and network stack specific objects
+FILTER_OBJS = ./rawcon.o ./plaincon.o $(LPXELINUX_OBJS) $(PXELINUX_OBJS)
+COBJS	 = $(filter-out $(FILTER_OBJS),$(COBJ))
+SOBJS	 = $(filter-out $(FILTER_OBJS),$(SOBJ))
 
 LIB	 = libcom32.a
 LIBS	 = $(LIB) --whole-archive $(com32)/lib/libcom32core.a
 LIBDEP   = $(filter-out -% %start%,$(LIBS))
-LIBOBJS	 = $(COBJS) $(SOBJ)
+LIBOBJS	 = $(COBJS) $(SOBJS)
 
 NASMDEBUG = -g -F dwarf
 NASMOPT  += $(NASMDEBUG)
@@ -96,7 +115,8 @@ kwdhash.gen: keywords genhash.pl
 		-DHEXDATE="$(HEXDATE)" \
 		-l $(@:.o=.lsr) -o $@ -MP -MD .$@.d $<
 
-AUXLIBS = libisolinux.a libisolinux-debug.a libldlinux.a libpxelinux.a
+AUXLIBS = libisolinux.a libisolinux-debug.a libldlinux.a \
+	libpxelinux.a liblpxelinux.a
 
 %.elf: %.o $(LIBDEP) syslinux.ld $(AUXLIBS)
 	$(LD) $(LDFLAGS) -Bsymbolic -pie -E --hash-style=gnu -T syslinux.ld -M -o $@ $< \
@@ -113,8 +133,20 @@ libisolinux.a: rawcon.o
 libisolinux-debug.a: libisolinux.a
 	cp $^ $@
 
-libpxelinux.a: libisolinux.a
-	cp $^ $@
+# Legacy network stack
+libpxelinux.a: rawcon.o $(PXELINUX_OBJS)
+	rm -f $@
+	$(AR) cq $@ $^
+	$(RANLIB) $@
+	rm $(PXELINUX_OBJS)
+
+# LwIP network stack
+liblpxelinux.a: rawcon.o
+	$(MAKE) CFLAGS="$(CFLAGS) -DIS_LPXELINUX" $(LPXELINUX_OBJS)
+	rm -f $@
+	$(AR) cq $@ $^ $(LPXELINUX_OBJS)
+	$(RANLIB) $@
+	rm $(LPXELINUX_OBJS)
 
 libldlinux.a: plaincon.o
 	rm -f $@
@@ -126,9 +158,24 @@ $(LIB): $(LIBOBJS)
 	$(AR) cq $@ $^
 	$(RANLIB) $@
 
+pxelinux.o: pxelinux.asm kwdhash.gen ../version.gen
+	$(NASM) -f elf $(NASMOPT) -DDATE_STR="'$(DATE)'" \
+		-DHEXDATE="$(HEXDATE)" \
+		-DIS_LPXELINUX=0 \
+		-l $(@:.o=.lsr) -o $@ -MP -MD .$@.d $<
+
 pxelinux.0: pxelinux.bin
 	cp -f $< $@
 
+lpxelinux.o: pxelinux.asm kwdhash.gen ../version.gen
+	$(NASM) -f elf $(NASMOPT) -DDATE_STR="'$(DATE)'" \
+		-DHEXDATE="$(HEXDATE)" \
+		-DIS_LPXELINUX=1 \
+		-l $(@:.o=.lsr) -o $@ -MP -MD .$@.d $<
+
+lpxelinux.0: lpxelinux.bin
+	cp -f $< $@
+
 ldlinux.bss: ldlinux.bin
 	dd if=$< of=$@ bs=512 count=1
 
diff --git a/core/fs/pxe/core.c b/core/fs/pxe/core.c
new file mode 100644
index 0000000..9246f66
--- /dev/null
+++ b/core/fs/pxe/core.c
@@ -0,0 +1,207 @@
+#include <syslinux/pxe_api.h>
+#include <lwip/api.h>
+#include <lwip/tcpip.h>
+#include <lwip/dns.h>
+#include <core.h>
+#include <net.h>
+#include "pxe.h"
+
+/**
+ * Open a socket
+ *
+ * @param:socket, the socket to open
+ * @param:proto, the protocol of the new connection
+ *
+ * @out: error code, 0 on success, -1 on failure
+ */
+int net_core_open(struct pxe_pvt_inode *socket, enum net_core_proto proto)
+{
+    struct net_private *priv = &socket->private;
+    enum netconn_type type;
+    int err;
+
+    switch (proto) {
+    case NET_CORE_TCP:
+	type = NETCONN_TCP;
+	break;
+    case NET_CORE_UDP:
+	type = NETCONN_UDP;
+	break;
+    default:
+	type = NETCONN_INVALID;
+	break;
+    }
+
+    priv->conn = netconn_new(type);
+    if (!priv->conn)
+	return -1;
+
+    priv->conn->recv_timeout = 15; /* A 15 ms recv timeout... */
+    err = netconn_bind(priv->conn, NULL, 0);
+    if (err) {
+	printf("netconn_bind error %d\n", err);
+	return -1;
+    }
+
+    return 0;
+}
+
+/**
+ * Close a socket
+ *
+ * @param:socket, the socket to open
+ */
+void net_core_close(struct pxe_pvt_inode *socket)
+{
+    struct net_private *priv = &socket->private;
+
+    if (priv->conn) {
+	netconn_delete(priv->conn);
+	priv->conn = NULL;
+    }
+}
+
+/**
+ * Establish a connection on an open socket
+ *
+ * @param:socket, the open socket
+ * @param:ip, the ip address
+ * @param:port, the port number, host-byte order
+ */
+void net_core_connect(struct pxe_pvt_inode *socket, uint32_t ip,
+		      uint16_t port)
+{
+    struct net_private *priv = &socket->private;
+    struct ip_addr addr;
+
+    addr.addr = ip;
+    netconn_connect(priv->conn, &addr, port);
+}
+
+/**
+ * Tear down a connection on an open socket
+ *
+ * @param:socket, the open socket
+ */
+void net_core_disconnect(struct pxe_pvt_inode *socket)
+{
+    struct net_private *priv = &socket->private;
+    netconn_disconnect(priv->conn);
+}
+
+/**
+ * Read data from the network stack
+ *
+ * @param:socket, the open socket
+ * @param:buf, location of buffer to store data
+ * @param:buf_len, size of buffer
+
+ * @out: src_ip, ip address of the data source
+ * @out: src_port, port number of the data source, host-byte order
+ */
+int net_core_recv(struct pxe_pvt_inode *socket, void *buf, uint16_t *buf_len,
+		  uint32_t *src_ip, uint16_t *src_port)
+{
+    struct net_private *priv = &socket->private;
+    struct netbuf *nbuf;
+    u16_t nbuf_len;
+    int err;
+
+    err = netconn_recv(priv->conn, &nbuf);
+    if (err)
+	return err;
+
+    if (!nbuf)
+	return -1;
+
+    *src_ip = netbuf_fromaddr(nbuf)->addr;
+    *src_port = netbuf_fromport(nbuf);
+
+    netbuf_first(nbuf);		/* XXX needed? */
+    nbuf_len = netbuf_len(nbuf);
+    if (nbuf_len <= *buf_len)
+	netbuf_copy(nbuf, buf, nbuf_len);
+    else
+	nbuf_len = 0; /* impossible mtu < PKTBUF_SIZE */
+    netbuf_delete(nbuf);
+
+    *buf_len = nbuf_len;
+    return 0;
+}
+
+/**
+ * Send a UDP packet.
+ *
+ * @param:socket, the open socket
+ * @param:data, data buffer to send
+ * @param:len, size of data bufer
+ */
+void net_core_send(struct pxe_pvt_inode *socket, const void *data, size_t len)
+{
+    struct netconn *conn = socket->private.conn;
+    struct netbuf *nbuf;
+    void *pbuf;
+    int err;
+
+    nbuf = netbuf_new();
+    if (!nbuf) {
+	printf("netbuf allocation error\n");
+	return;
+    }
+
+    pbuf = netbuf_alloc(nbuf, len);
+    if (!pbuf) {
+	printf("pbuf allocation error\n");
+	goto out;
+    }
+
+    memcpy(pbuf, data, len);
+
+    err = netconn_send(conn, nbuf);
+    if (err) {
+	printf("netconn_send error %d\n", err);
+	goto out;
+    }
+
+out:
+    netbuf_delete(nbuf);
+}
+
+/**
+ * Network stack-specific initialization
+ */
+void net_core_init(void)
+{
+    int err;
+    int i;
+
+    http_bake_cookies();
+
+    /* Initialize lwip */
+    tcpip_init(NULL, NULL);
+
+    /* Start up the undi driver for lwip */
+    err = undiif_start(IPInfo.myip, IPInfo.netmask, IPInfo.gateway);
+    if (err) {
+       printf("undiif driver failed to start: %d\n", err);
+       kaboom();
+    }
+
+    for (i = 0; i < DNS_MAX_SERVERS; i++) {
+	/* Transfer the DNS information to lwip */
+	dns_setserver(i, (struct ip_addr *)&dns_server[i]);
+    }
+}
+
+void probe_undi(void)
+{
+    /* Probe UNDI information */
+    pxe_call(PXENV_UNDI_GET_INFORMATION, &pxe_undi_info);
+    pxe_call(PXENV_UNDI_GET_IFACE_INFO,  &pxe_undi_iface);
+
+    printf("UNDI: baseio %04x int %d MTU %d type %d \"%s\" flags 0x%x\n",
+	   pxe_undi_info.BaseIo, pxe_undi_info.IntNumber,
+	   pxe_undi_info.MaxTranUnit, pxe_undi_info.HwType,
+	   pxe_undi_iface.IfaceType, pxe_undi_iface.ServiceFlags);
+}
+
diff --git a/core/fs/pxe/ftp.c b/core/fs/pxe/ftp.c
index 50c9257..5d5a6f0 100644
--- a/core/fs/pxe/ftp.c
+++ b/core/fs/pxe/ftp.c
@@ -69,7 +69,7 @@ static int ftp_cmd_response(struct inode *inode, const char *cmd,
 	*q++ = '\n';
 	cmd_len += 2;
 
-	err = netconn_write(socket->conn, cmd_buf, cmd_len, NETCONN_COPY);
+	err = netconn_write(socket->private.conn, cmd_buf, cmd_len, NETCONN_COPY);
 	if (err)
 	    return -1;
     }
@@ -166,7 +166,7 @@ static void ftp_close_file(struct inode *inode)
     int resp;
 
     ctlsock = socket->ctl ? PVT(socket->ctl) : NULL;
-    if (ctlsock->conn) {
+    if (ctlsock->private.conn) {
 	resp = ftp_cmd_response(socket->ctl, "QUIT", NULL, NULL, NULL);
 	while (resp == 226) {
 	    resp = ftp_cmd_response(socket->ctl, NULL, NULL, NULL, NULL);
@@ -209,11 +209,11 @@ void ftp_open(struct url_info *url, int flags, struct inode *inode,
 	return;
     ctlsock = PVT(socket->ctl);
     ctlsock->ops = &tcp_conn_ops; /* The control connection is just TCP */
-    ctlsock->conn = netconn_new(NETCONN_TCP);
-    if (!ctlsock->conn)
+    ctlsock->private.conn = netconn_new(NETCONN_TCP);
+    if (!ctlsock->private.conn)
 	goto err_free;
     addr.addr = url->ip;
-    err = netconn_connect(ctlsock->conn, &addr, url->port);
+    err = netconn_connect(ctlsock->private.conn, &addr, url->port);
     if (err)
 	goto err_delete;
 
@@ -248,10 +248,10 @@ void ftp_open(struct url_info *url, int flags, struct inode *inode,
     if (resp != 227 || pasv_bytes != 6)
 	goto err_disconnect;
 
-    socket->conn = netconn_new(NETCONN_TCP);
-    if (!socket->conn)
+    socket->private.conn = netconn_new(NETCONN_TCP);
+    if (!socket->private.conn)
 	goto err_disconnect;
-    err = netconn_connect(socket->conn, (struct ip_addr *)&pasv_data[0],
+    err = netconn_connect(socket->private.conn, (struct ip_addr *)&pasv_data[0],
 			  ntohs(*(uint16_t *)&pasv_data[4]));
     if (err)
 	goto err_disconnect;
@@ -266,15 +266,15 @@ void ftp_open(struct url_info *url, int flags, struct inode *inode,
     return;			/* Sucess! */
 
 err_disconnect:
-    if (ctlsock->conn)
-	netconn_write(ctlsock->conn, "QUIT\r\n", 6, NETCONN_NOCOPY);
-    if (socket->conn)
-	netconn_delete(socket->conn);
-    if (ctlsock->buf)
-	netbuf_delete(ctlsock->buf);
+    if (ctlsock->private.conn)
+	netconn_write(ctlsock->private.conn, "QUIT\r\n", 6, NETCONN_NOCOPY);
+    if (socket->private.conn)
+	netconn_delete(socket->private.conn);
+    if (ctlsock->private.buf)
+	netbuf_delete(ctlsock->private.buf);
 err_delete:
-    if (ctlsock->conn)
-	netconn_delete(ctlsock->conn);
+    if (ctlsock->private.conn)
+	netconn_delete(ctlsock->private.conn);
 err_free:
     free_socket(socket->ctl);
 }
diff --git a/core/fs/pxe/http.c b/core/fs/pxe/http.c
index d5022eb..9bf0c92 100644
--- a/core/fs/pxe/http.c
+++ b/core/fs/pxe/http.c
@@ -191,8 +191,8 @@ void http_open(struct url_info *url, int flags, struct inode *inode,
     inode->size = content_length = -1;
 
     /* Start the http connection */
-    socket->conn = netconn_new(NETCONN_TCP);
-    if (!socket->conn) {
+    socket->private.conn = netconn_new(NETCONN_TCP);
+    if (!socket->private.conn) {
 	printf("netconn_new failed\n");
         return;
     }
@@ -201,7 +201,7 @@ void http_open(struct url_info *url, int flags, struct inode *inode,
     if (!url->port)
 	url->port = HTTP_PORT;
 
-    err = netconn_connect(socket->conn, &addr, url->port);
+    err = netconn_connect(socket->private.conn, &addr, url->port);
     if (err) {
 	printf("netconn_connect error %d\n", err);
 	goto fail;
@@ -225,7 +225,7 @@ void http_open(struct url_info *url, int flags, struct inode *inode,
     if (header_bytes >= header_len)
 	goto fail;		/* Buffer overflow */
 
-    err = netconn_write(socket->conn, header_buf,
+    err = netconn_write(socket->private.conn, header_buf,
 			header_bytes, NETCONN_NOCOPY);
     if (err) {
 	printf("netconn_write error %d\n", err);
diff --git a/core/fs/pxe/pxe.c b/core/fs/pxe/pxe.c
index ff8a0b3..9f18f28 100644
--- a/core/fs/pxe/pxe.c
+++ b/core/fs/pxe/pxe.c
@@ -7,14 +7,11 @@
 #include <minmax.h>
 #include <fcntl.h>
 #include <sys/cpu.h>
-#include <lwip/api.h>
-#include <lwip/dns.h>
-#include <lwip/tcpip.h>
-#include <lwip/opt.h>
 #include "pxe.h"
 #include "thread.h"
 #include "url.h"
 #include "tftp.h"
+#include <net.h>
 
 __lowmem t_PXENV_UNDI_GET_INFORMATION pxe_undi_info;
 __lowmem t_PXENV_UNDI_GET_IFACE_INFO  pxe_undi_iface;
@@ -38,8 +35,10 @@ static struct url_scheme {
     int ok_flags;
 } url_schemes[] = {
     { "tftp", tftp_open, 0 },
+#ifdef IS_LPXELINUX
     { "http", http_open, O_DIRECTORY },
     { "ftp",  ftp_open,  O_DIRECTORY },
+#endif
     { NULL, NULL, 0 },
 };
 #define OK_FLAGS_MASK	(O_DIRECTORY|O_WRONLY)
@@ -772,14 +771,7 @@ static int pxe_init(bool quiet)
 
     real_base_mem = max(code_seg, data_seg) >> 6; /* Convert to kilobytes */
 
-    /* Probe UNDI information */
-    pxe_call(PXENV_UNDI_GET_INFORMATION, &pxe_undi_info);
-    pxe_call(PXENV_UNDI_GET_IFACE_INFO,  &pxe_undi_iface);
-    
-    printf("UNDI: baseio %04x int %d MTU %d type %d \"%s\" flags 0x%x\n",
-	   pxe_undi_info.BaseIo, pxe_undi_info.IntNumber,
-	   pxe_undi_info.MaxTranUnit, pxe_undi_info.HwType,
-	   pxe_undi_iface.IfaceType, pxe_undi_iface.ServiceFlags);
+    probe_undi();
 
     return 0;
 }
@@ -809,9 +801,7 @@ static void gpxe_init(void)
  */
 static void network_init(void)
 {
-    int err;
     int pkt_len;
-    int i;
     struct bootp_t *bp;
     const size_t dhcp_max_packet = 4096;
 
@@ -870,9 +860,8 @@ static void network_init(void)
     if (have_uuid)
 	sysappend_set_uuid(uuid);
     ip_init();
-    /* print_sysappend(); */
-    http_bake_cookies();
 
+    /* print_sysappend(); */
     /*
      * Check to see if we got any PXELINUX-specific DHCP options; in particular,
      * if we didn't get the magic enable, do not recognize any other options.
@@ -880,20 +869,7 @@ static void network_init(void)
     if ((DHCPMagic & 1) == 0)
         DHCPMagic = 0;
 
-    /* Initialize lwip */
-    tcpip_init(NULL, NULL);
-
-    /* Start up the undi driver for lwip */
-    err = undiif_start(IPInfo.myip, IPInfo.netmask, IPInfo.gateway);
-    if (err) {
-       printf("undiif driver failed to start: %d\n", err);
-       kaboom();
-    }
-
-    for (i = 0; i < DNS_MAX_SERVERS; i++) {
-	/* Transfer the DNS information to lwip */
-	dns_setserver(i, (struct ip_addr *)&dns_server[i]);
-    }
+    net_core_init();
 }
 
 /*
diff --git a/core/fs/pxe/pxe.h b/core/fs/pxe/pxe.h
index 4a43a9d..2d4be3b 100644
--- a/core/fs/pxe/pxe.h
+++ b/core/fs/pxe/pxe.h
@@ -114,19 +114,29 @@ struct pxe_conn_ops {
     int (*readdir)(struct inode *inode, struct dirent *dirent);
 };    
 
-struct pxe_pvt_inode {
+struct net_private {
+#ifdef IS_LPXELINUX
     struct netconn *conn;      /* lwip network connection */
     struct netbuf *buf;	       /* lwip cached buffer */
-    uint16_t tftp_remoteport;  /* Remote port number */
-    uint32_t tftp_filepos;     /* bytes downloaded (including buffer) */
-    uint32_t tftp_blksize;     /* Block size for this connection(*) */
-    uint16_t tftp_bytesleft;   /* Unclaimed data bytes */
-    uint16_t tftp_lastpkt;     /* Sequence number of last packet (HBO) */
-    char    *tftp_dataptr;     /* Pointer to available data */
-    uint8_t  tftp_goteof;      /* 1 if the EOF packet received */
-    uint8_t  tftp_unused[3];   /* Currently unused */
-    char    *tftp_pktbuf;      /* Packet buffer */
-    struct inode *ctl;	       /* Control connection (for FTP) */
+#else
+    uint16_t tftp_localport;   /* Local port number  (0=not in use) */
+    uint32_t tftp_remoteip;    /* Remote IP address (0 = disconnected) */
+#endif
+
+};
+
+struct pxe_pvt_inode {
+    struct net_private private;   /* Network stack private data */
+    uint16_t tftp_remoteport;     /* Remote port number */
+    uint32_t tftp_filepos;        /* bytes downloaded (including buffer) */
+    uint32_t tftp_blksize;        /* Block size for this connection(*) */
+    uint16_t tftp_bytesleft;      /* Unclaimed data bytes */
+    uint16_t tftp_lastpkt;        /* Sequence number of last packet (HBO) */
+    char    *tftp_dataptr;        /* Pointer to available data */
+    uint8_t  tftp_goteof;         /* 1 if the EOF packet received */
+    uint8_t  tftp_unused[3];      /* Currently unused */
+    char    *tftp_pktbuf;         /* Packet buffer */
+    struct inode *ctl;	          /* Control connection (for FTP) */
     const struct pxe_conn_ops *ops;
 };
 
@@ -176,6 +186,18 @@ extern uint8_t uuid_type;
 extern uint8_t uuid[];
 
 /*
+ * Compute the suitable gateway for a specific route -- too many
+ * vendor PXE stacks don't do this correctly...
+ */
+static inline uint32_t gateway(uint32_t ip)
+{
+    if ((ip ^ IPInfo.myip) & IPInfo.netmask)
+	return IPInfo.gateway;
+    else
+	return 0;
+}
+
+/*
  * functions
  */
 
diff --git a/core/fs/pxe/tcp.c b/core/fs/pxe/tcp.c
index ec7679e..528fbce 100644
--- a/core/fs/pxe/tcp.c
+++ b/core/fs/pxe/tcp.c
@@ -25,13 +25,13 @@ void tcp_close_file(struct inode *inode)
 {
     struct pxe_pvt_inode *socket = PVT(inode);
 
-    if (socket->conn) {
-	netconn_delete(socket->conn);
-	socket->conn = NULL;
+    if (socket->private.conn) {
+	netconn_delete(socket->private.conn);
+	socket->private.conn = NULL;
     }
-    if (socket->buf) {
-	netbuf_delete(socket->buf);
-        socket->buf = NULL;
+    if (socket->private.buf) {
+	netbuf_delete(socket->private.buf);
+        socket->private.buf = NULL;
     }
 }
 
@@ -43,16 +43,16 @@ void tcp_fill_buffer(struct inode *inode)
     err_t err;
 
     /* Clean up or advance an inuse netbuf */
-    if (socket->buf) {
-	if (netbuf_next(socket->buf) < 0) {
-	    netbuf_delete(socket->buf);
-	    socket->buf = NULL;
+    if (socket->private.buf) {
+	if (netbuf_next(socket->private.buf) < 0) {
+	    netbuf_delete(socket->private.buf);
+	    socket->private.buf = NULL;
 	}
     }
     /* If needed get a new netbuf */
-    if (!socket->buf) {
-	err = netconn_recv(socket->conn, &(socket->buf));
-	if (!socket->buf || err) {
+    if (!socket->private.buf) {
+	err = netconn_recv(socket->private.conn, &(socket->private.buf));
+	if (!socket->private.buf || err) {
 	    socket->tftp_goteof = 1;
 	    if (inode->size == -1)
 		inode->size = socket->tftp_filepos;
@@ -61,7 +61,7 @@ void tcp_fill_buffer(struct inode *inode)
 	}
     }
     /* Report the current fragment of the netbuf */
-    err = netbuf_data(socket->buf, &data, &len);
+    err = netbuf_data(socket->private.buf, &data, &len);
     if (err) {
 	printf("netbuf_data err: %d\n", err);
 	kaboom();
diff --git a/core/fs/pxe/tftp.c b/core/fs/pxe/tftp.c
index f6ea297..9b755c9 100644
--- a/core/fs/pxe/tftp.c
+++ b/core/fs/pxe/tftp.c
@@ -1,10 +1,10 @@
 #include <minmax.h>
-#include <lwip/api.h>
+#include <net.h>
 #include "pxe.h"
 #include "url.h"
 #include "tftp.h"
 
-static const uint8_t TimeoutTable[] = {
+const uint8_t TimeoutTable[] = {
     2, 2, 3, 3, 4, 5, 6, 7, 9, 10, 12, 15, 18, 21, 26, 31, 37, 44,
     53, 64, 77, 92, 110, 132, 159, 191, 229, 255, 255, 255, 255, 0
 };
@@ -38,43 +38,7 @@ static void tftp_close_file(struct inode *inode)
     if (!socket->tftp_goteof) {
 	tftp_error(inode, 0, "No error, file close");
     }
-    if (socket->conn) {
-	netconn_delete(socket->conn);
-	socket->conn = NULL;
-    }
-}
-
-/*
- * Send a UDP packet.
- */
-static void udp_send(struct netconn *conn, const void *data, size_t len)
-{
-    struct netbuf *nbuf;
-    void *pbuf;
-    int err;
-
-    nbuf = netbuf_new();
-    if (!nbuf) {
-	printf("netbuf allocation error\n");
-	return;
-    }
-    
-    pbuf = netbuf_alloc(nbuf, len);
-    if (!pbuf) {
-	printf("pbuf allocation error\n");
-	goto out;
-    }
-
-    memcpy(pbuf, data, len);
-
-    err = netconn_send(conn, nbuf);
-    if (err) {
-	printf("netconn_send error %d\n", err);
-	goto out;
-    }
-
-out:
-    netbuf_delete(nbuf);
+    net_core_close(socket);
 }
 
 /**
@@ -100,7 +64,7 @@ static void tftp_error(struct inode *inode, uint16_t errnum,
     memcpy(err_buf.err_msg, errstr, len);
     err_buf.err_msg[len] = '\0';
 
-    udp_send(socket->conn, &err_buf, 4 + len + 1);
+    net_core_send(socket, &err_buf, 4 + len + 1);
 }
 
 /**
@@ -119,7 +83,7 @@ static void ack_packet(struct inode *inode, uint16_t ack_num)
     ack_packet_buf[0]     = TFTP_ACK;
     ack_packet_buf[1]     = htons(ack_num);
 
-    udp_send(socket->conn, ack_packet_buf, 4);
+    net_core_send(socket, ack_packet_buf, 4);
 }
 
 /*
@@ -135,10 +99,11 @@ static void tftp_get_packet(struct inode *inode)
     uint16_t serial;
     jiffies_t oldtime;
     struct tftp_packet *pkt = NULL;
-    struct netbuf *nbuf;
-    u16_t nbuf_len;
+    uint16_t buf_len;
     struct pxe_pvt_inode *socket = PVT(inode);
-    err_t err;
+    uint16_t src_port;
+    uint32_t src_ip;
+    int err;
 
     /*
      * Start by ACKing the previous packet; this should cause
@@ -152,8 +117,10 @@ static void tftp_get_packet(struct inode *inode)
     ack_packet(inode, socket->tftp_lastpkt);
 
     while (timeout) {
-	err = netconn_recv(socket->conn, &nbuf);
-	if (!nbuf || err) {
+	buf_len = socket->tftp_blksize + 4;
+	err = net_core_recv(socket, socket->tftp_pktbuf, &buf_len,
+			    &src_ip, &src_port);
+	if (err) {
 	    jiffies_t now = jiffies();
 
 	    if (now-oldtime >= timeout) {
@@ -166,15 +133,7 @@ static void tftp_get_packet(struct inode *inode)
             continue;
 	}
 
-	netbuf_first(nbuf);
-	nbuf_len = 0;
-	nbuf_len = netbuf_len(nbuf);
-	if (nbuf_len <= socket->tftp_blksize + 4)
-	    netbuf_copy(nbuf, socket->tftp_pktbuf, nbuf_len);
-	else
-	    nbuf_len = 0;	/* invalid packet */
-	netbuf_delete(nbuf);
-	if (nbuf_len < 4)	/* Bad size for a DATA packet */
+	if (buf_len < 4)	/* Bad size for a DATA packet */
 	    continue;
 
         pkt = (struct tftp_packet *)(socket->tftp_pktbuf);
@@ -207,7 +166,7 @@ static void tftp_get_packet(struct inode *inode)
 
     /* It's the packet we want.  We're also EOF if the size < blocksize */
     socket->tftp_lastpkt = last_pkt;    /* Update last packet number */
-    buffersize = nbuf_len - 4;		/* Skip TFTP header */
+    buffersize = buf_len - 4;		/* Skip TFTP header */
     socket->tftp_dataptr = socket->tftp_pktbuf + 4;
     socket->tftp_filepos += buffersize;
     socket->tftp_bytesleft = buffersize;
@@ -243,8 +202,7 @@ void tftp_open(struct url_info *url, int flags, struct inode *inode,
 {
     struct pxe_pvt_inode *socket = PVT(inode);
     char *buf;
-    struct netbuf *nbuf;
-    u16_t nbuf_len;
+    uint16_t buf_len;
     char *p;
     char *options;
     char *data;
@@ -262,7 +220,8 @@ void tftp_open(struct url_info *url, int flags, struct inode *inode,
     uint16_t opcode;
     uint16_t blk_num;
     uint32_t opdata, *opdata_ptr;
-    struct ip_addr addr;
+    uint16_t src_port;
+    uint32_t src_ip;
 
     (void)redir;		/* TFTP does not redirect */
     (void)flags;
@@ -279,17 +238,9 @@ void tftp_open(struct url_info *url, int flags, struct inode *inode,
 	url->port = TFTP_PORT;
 
     socket->ops = &tftp_conn_ops;
-    socket->conn = netconn_new(NETCONN_UDP);
-    if (!socket->conn)
+    if (net_core_open(socket, NET_CORE_UDP))
 	return;
 
-    socket->conn->recv_timeout = 15; /* A 15 ms recv timeout... */
-    err = netconn_bind(socket->conn, NULL, 0);
-    if (err) {
-	printf("netconn_bind error %d\n", err);
-	return;
-    }
-
     buf = rrq_packet_buf;
     *(uint16_t *)buf = TFTP_RRQ;  /* TFTP opcode */
     buf += 2;
@@ -303,48 +254,42 @@ void tftp_open(struct url_info *url, int flags, struct inode *inode,
     rrq_len = buf - rrq_packet_buf;
 
     timeout_ptr = TimeoutTable;   /* Reset timeout */
-    
 sendreq:
-    netconn_disconnect(socket->conn);
+    net_core_disconnect(socket);
     timeout = *timeout_ptr++;
     if (!timeout)
 	return;			/* No file available... */
     oldtime = jiffies();
 
-    addr.addr = url->ip;
-    netconn_connect(socket->conn, &addr, url->port);
-    udp_send(socket->conn, rrq_packet_buf, rrq_len);
+    net_core_connect(socket, url->ip, url->port);
+    net_core_send(socket, rrq_packet_buf, rrq_len);
 
     /* If the WRITE call fails, we let the timeout take care of it... */
 wait_pkt:
-    netconn_disconnect(socket->conn);
+    net_core_disconnect(socket);
     for (;;) {
-	err = netconn_recv(socket->conn, &nbuf);
-	if (!nbuf || err) {
+	buf_len = sizeof(reply_packet_buf);
+
+	err = net_core_recv(socket, reply_packet_buf, &buf_len,
+			    &src_ip, &src_port);
+	if (err) {
 	    jiffies_t now = jiffies();
 	    if (now - oldtime >= timeout)
 		 goto sendreq;
 	} else {
 	    /* Make sure the packet actually came from the server */
-	    bool ok_source;
-	    ok_source = netbuf_fromaddr(nbuf)->addr == url->ip;
-	    nbuf_len = netbuf_len(nbuf);
-	    if (nbuf_len <= PKTBUF_SIZE)
-		netbuf_copy(nbuf, reply_packet_buf, nbuf_len);
-	    else
-		nbuf_len = 0; /* impossible mtu < PKTBUF_SIZE */
-	    netbuf_delete(nbuf);
-	    if (ok_source)
-	        break;
+	    if (src_ip == url->ip)
+		break;
 	}
     }
 
-    netconn_connect(socket->conn, netbuf_fromaddr(nbuf), netbuf_fromport(nbuf));
+    net_core_disconnect(socket);
+    net_core_connect(socket, src_ip, src_port);
 
     /* filesize <- -1 == unknown */
     inode->size = -1;
     socket->tftp_blksize = TFTP_BLOCKSIZE;
-    buffersize = nbuf_len - 2;	  /* bytes after opcode */
+    buffersize = buf_len - 2;	  /* bytes after opcode */
     if (buffersize < 0)
         goto wait_pkt;                     /* Garbled reply */
 
@@ -491,9 +436,8 @@ err_reply:
     inode->size = 0;
 
 done:
-    if (!inode->size) {
-	netconn_delete(socket->conn);
-	socket->conn = NULL;
-    }
+    if (!inode->size)
+	net_core_close(socket);
+
     return;
 }
diff --git a/core/include/net.h b/core/include/net.h
new file mode 100644
index 0000000..4f6819f
--- /dev/null
+++ b/core/include/net.h
@@ -0,0 +1,33 @@
+#ifndef _NET_H
+#define _NET_H
+
+#include <stdint.h>
+#include <stddef.h>
+
+/* Protocol family */
+enum net_core_proto {
+    NET_CORE_TCP,
+    NET_CORE_UDP,
+};
+
+void net_core_init(void);
+
+struct pxe_pvt_inode;
+
+int net_core_open(struct pxe_pvt_inode *socket, enum net_core_proto proto);
+void net_core_close(struct pxe_pvt_inode *socket);
+
+void net_core_connect(struct pxe_pvt_inode *socket,
+		      uint32_t ip, uint16_t port);
+void net_core_disconnect(struct pxe_pvt_inode *socket);
+
+int net_core_recv(struct pxe_pvt_inode *socket, void *buf, uint16_t *buf_len,
+		  uint32_t *src_ip, uint16_t *src_port);
+
+void net_core_send(struct pxe_pvt_inode *socket,
+		   const void *data, size_t len);
+
+void probe_undi(void);
+void pxe_init_isr(void);
+
+#endif /* _NET_H */
diff --git a/core/legacynet/core.c b/core/legacynet/core.c
new file mode 100644
index 0000000..2163c69
--- /dev/null
+++ b/core/legacynet/core.c
@@ -0,0 +1,186 @@
+#include <syslinux/pxe_api.h>
+#include <com32.h>
+#include <core.h>
+#include <net.h>
+#include <pxe.h>
+#include <minmax.h>
+
+/* Common receive buffer */
+static __lowmem char packet_buf[PKTBUF_SIZE] __aligned(16);
+
+extern uint16_t get_port(void);
+extern void free_port(uint16_t);
+
+/**
+ * Open a socket
+ *
+ * @param:socket, the socket to open
+ * @param:proto, the protocol of the new connection
+ *
+ * @out: error code, 0 on success, -1 on failure
+ */
+int net_core_open(struct pxe_pvt_inode *socket __unused,
+		  enum net_core_proto proto)
+{
+    struct net_private *priv = &socket->private;
+
+    /* The legacy stack only supports UDP */
+    if (proto != NET_CORE_UDP)
+	return -1;
+
+    /* Allocate local UDP port number */
+    priv->tftp_localport = get_port();
+
+    return 0;
+}
+
+/**
+ * Close a socket
+ *
+ * @param:socket, the socket to open
+ */
+void net_core_close(struct pxe_pvt_inode *socket)
+{
+    struct net_private *priv = &socket->private;
+
+    if (priv->tftp_localport)
+	free_port(priv->tftp_localport);
+}
+
+/**
+ * Establish a connection on an open socket
+ *
+ * @param:socket, the open socket
+ * @param:ip, the ip address
+ * @param:port, the port number, host-byte order
+ */
+void net_core_connect(struct pxe_pvt_inode *socket, uint32_t ip,
+		      uint16_t port)
+{
+    struct net_private *priv = &socket->private;
+
+    socket->tftp_remoteport = htons(port);
+    priv->tftp_remoteip = ip;
+
+}
+
+/**
+ * Tear down a connection on an open socket
+ *
+ * @param:socket, the open socket
+ */
+void net_core_disconnect(struct pxe_pvt_inode *socket __unused)
+{
+}
+
+/**
+ * Read data from the network stack
+ *
+ * @param:socket, the open socket
+ * @param:buf, location of buffer to store data
+ * @param:buf_len, size of buffer
+
+ * @out: src_ip, ip address of the data source
+ * @out: src_port, port number of the data source, host-byte order
+ */
+int net_core_recv(struct pxe_pvt_inode *socket, void *buf, uint16_t *buf_len,
+		  uint32_t *src_ip, uint16_t *src_port)
+{
+    static __lowmem struct s_PXENV_UDP_READ  udp_read;
+    struct net_private *priv = &socket->private;
+    uint16_t bytes;
+    int err;
+
+    udp_read.status      = 0;
+    udp_read.buffer      = FAR_PTR(packet_buf);
+    udp_read.buffer_size = PKTBUF_SIZE;
+    udp_read.dest_ip     = IPInfo.myip;
+    udp_read.d_port      = priv->tftp_localport;
+
+    err = pxe_call(PXENV_UDP_READ, &udp_read);
+    if (err)
+	return err;
+
+    if (udp_read.status)
+	return udp_read.status;
+
+    bytes = min(udp_read.buffer_size, *buf_len);
+    memcpy(buf, packet_buf, bytes);
+
+    *src_ip = udp_read.src_ip;
+    *src_port = ntohs(udp_read.s_port);
+    *buf_len = bytes;
+
+    return 0;
+}
+
+/**
+ * Send a UDP packet.
+ *
+ * @param:socket, the open socket
+ * @param:data, data buffer to send
+ * @param:len, size of data bufer
+ */
+void net_core_send(struct pxe_pvt_inode *socket, const void *data, size_t len)
+{
+    static __lowmem struct s_PXENV_UDP_WRITE udp_write;
+    struct net_private *priv = &socket->private;
+    void *lbuf;
+    uint16_t tid;
+
+    lbuf = lmalloc(len);
+    if (!lbuf)
+	return;
+
+    memcpy(lbuf, data, len);
+
+    tid = priv->tftp_localport;   /* TID(local port No) */
+    udp_write.buffer    = FAR_PTR(lbuf);
+    udp_write.ip        = priv->tftp_remoteip;
+    udp_write.gw        = gateway(udp_write.ip);
+    udp_write.src_port  = tid;
+    udp_write.dst_port  = socket->tftp_remoteport;
+    udp_write.buffer_size = len;
+
+    pxe_call(PXENV_UDP_WRITE, &udp_write);
+
+    lfree(lbuf);
+}
+
+/**
+ * Network stack-specific initialization
+ *
+ * Initialize UDP stack
+ */
+void net_core_init(void)
+{
+    int err;
+    static __lowmem struct s_PXENV_UDP_OPEN udp_open;
+    udp_open.src_ip = IPInfo.myip;
+    err = pxe_call(PXENV_UDP_OPEN, &udp_open);
+    if (err || udp_open.status) {
+        printf("Failed to initialize UDP stack ");
+        printf("%d\n", udp_open.status);
+	kaboom();
+    }
+}
+
+void probe_undi(void)
+{
+}
+
+void pxe_init_isr(void)
+{
+}
+
+int reset_pxe(void)
+{
+    static __lowmem struct s_PXENV_UDP_CLOSE udp_close;
+    int err = 0;
+
+    pxe_idle_cleanup();
+
+    pxe_call(PXENV_UDP_CLOSE, &udp_close);
+
+    return err;
+}
diff --git a/core/legacynet/dnsresolv.c b/core/legacynet/dnsresolv.c
new file mode 100644
index 0000000..fab36ae
--- /dev/null
+++ b/core/legacynet/dnsresolv.c
@@ -0,0 +1,353 @@
+#include <stdio.h>
+#include <string.h>
+#include <core.h>
+#include "pxe.h"
+
+/* DNS CLASS values we care about */
+#define CLASS_IN	1
+
+/* DNS TYPE values we care about */
+#define TYPE_A		1
+#define TYPE_CNAME	5
+
+/*
+ * The DNS header structure
+ */
+struct dnshdr {
+    uint16_t id;
+    uint16_t flags;
+    /* number of entries in the question section */
+    uint16_t qdcount;
+    /* number of resource records in the answer section */
+    uint16_t ancount;
+    /* number of name server resource records in the authority records section*/
+    uint16_t nscount;
+    /* number of resource records in the additional records section */
+    uint16_t arcount;
+} __attribute__ ((packed));
+
+/*
+ * The DNS query structure
+ */
+struct dnsquery {
+    uint16_t qtype;
+    uint16_t qclass;
+} __attribute__ ((packed));
+
+/*
+ * The DNS Resource recodes structure
+ */
+struct dnsrr {
+    uint16_t type;
+    uint16_t class;
+    uint32_t ttl;
+    uint16_t rdlength;   /* The lenght of this rr data */
+    char     rdata[];
+} __attribute__ ((packed));
+
+
+#define DNS_PORT	htons(53)               /* Default DNS port */
+#define DNS_MAX_SERVERS 4		/* Max no of DNS servers */
+
+uint32_t dns_server[DNS_MAX_SERVERS] = {0, };
+
+
+/*
+ * Turn a string in _src_ into a DNS "label set" in _dst_; returns the
+ * number of dots encountered. On return, *dst is updated.
+ */
+int dns_mangle(char **dst, const char *p)
+{
+    char *q = *dst;
+    char *count_ptr;
+    char c;
+    int dots = 0;
+
+    count_ptr = q;
+    *q++ = 0;
+
+    while (1) {
+        c = *p++;
+        if (c == 0 || c == ':' || c == '/')
+            break;
+        if (c == '.') {
+            dots++;
+            count_ptr = q;
+            *q++ = 0;
+            continue;
+        }
+
+        *count_ptr += 1;
+        *q++ = c;
+    }
+
+    if (*count_ptr)
+        *q++ = 0;
+
+    /* update the strings */
+    *dst = q;
+    return dots;
+}
+
+
+/*
+ * Compare two sets of DNS labels, in _s1_ and _s2_; the one in _s2_
+ * is allowed pointers relative to a packet in buf.
+ *
+ */
+static bool dns_compare(const void *s1, const void *s2, const void *buf)
+{
+    const uint8_t *q = s1;
+    const uint8_t *p = s2;
+    unsigned int c0, c1;
+
+    while (1) {
+	c0 = p[0];
+        if (c0 >= 0xc0) {
+	    /* Follow pointer */
+	    c1 = p[1];
+	    p = (const uint8_t *)buf + ((c0 - 0xc0) << 8) + c1;
+	} else if (c0) {
+	    c0++;		/* Include the length byte */
+	    if (memcmp(q, p, c0))
+		return false;
+	    q += c0;
+	    p += c0;
+	} else {
+	    return *q == 0;
+	}
+    }
+}
+
+/*
+ * Copy a DNS label into a buffer, considering the possibility that we might
+ * have to follow pointers relative to "buf".
+ * Returns a pointer to the first free byte *after* the terminal null.
+ */
+static void *dns_copylabel(void *dst, const void *src, const void *buf)
+{
+    uint8_t *q = dst;
+    const uint8_t *p = src;
+    unsigned int c0, c1;
+
+    while (1) {
+	c0 = p[0];
+        if (c0 >= 0xc0) {
+	    /* Follow pointer */
+	    c1 = p[1];
+	    p = (const uint8_t *)buf + ((c0 - 0xc0) << 8) + c1;
+	} else if (c0) {
+	    c0++;		/* Include the length byte */
+	    memcpy(q, p, c0);
+	    p += c0;
+	    q += c0;
+	} else {
+	    *q++ = 0;
+	    return q;
+	}
+    }
+}
+
+/*
+ * Skip past a DNS label set in DS:SI
+ */
+static char *dns_skiplabel(char *label)
+{
+    uint8_t c;
+
+    while (1) {
+        c = *label++;
+        if (c >= 0xc0)
+            return ++label; /* pointer is two bytes */
+        if (c == 0)
+            return label;
+        label += c;
+    }
+}
+
+extern const uint8_t TimeoutTable[];
+extern uint16_t get_port(void);
+extern void free_port(uint16_t port);
+
+/*
+ * Actual resolver function
+ * Points to a null-terminated or :-terminated string in _name_
+ * and returns the ip addr in _ip_ if it exists and can be found.
+ * If _ip_ = 0 on exit, the lookup failed. _name_ will be updated
+ *
+ * XXX: probably need some caching here.
+ */
+__export uint32_t dns_resolv(const char *name)
+{
+    static char __lowmem DNSSendBuf[PKTBUF_SIZE];
+    static char __lowmem DNSRecvBuf[PKTBUF_SIZE];
+    char *p;
+    int err;
+    int dots;
+    int same;
+    int rd_len;
+    int ques, reps;    /* number of questions and replies */
+    uint8_t timeout;
+    const uint8_t *timeout_ptr = TimeoutTable;
+    uint32_t oldtime;
+    uint32_t srv;
+    uint32_t *srv_ptr;
+    struct dnshdr *hd1 = (struct dnshdr *)DNSSendBuf;
+    struct dnshdr *hd2 = (struct dnshdr *)DNSRecvBuf;
+    struct dnsquery *query;
+    struct dnsrr *rr;
+    static __lowmem struct s_PXENV_UDP_WRITE udp_write;
+    static __lowmem struct s_PXENV_UDP_READ  udp_read;
+    uint16_t local_port;
+    uint32_t result = 0;
+
+    /*
+     * Return failure on an empty input... this can happen during
+     * some types of URL parsing, and this is the easiest place to
+     * check for it.
+     */
+    if (!name || !*name)
+	return 0;
+
+    /* Make sure we have at least one valid DNS server */
+    if (!dns_server[0])
+	return 0;
+
+    /* Get a local port number */
+    local_port = get_port();
+
+    /* First, fill the DNS header struct */
+    hd1->id++;                      /* New query ID */
+    hd1->flags   = htons(0x0100);   /* Recursion requested */
+    hd1->qdcount = htons(1);        /* One question */
+    hd1->ancount = 0;               /* No answers */
+    hd1->nscount = 0;               /* No NS */
+    hd1->arcount = 0;               /* No AR */
+
+    p = DNSSendBuf + sizeof(struct dnshdr);
+    dots = dns_mangle(&p, name);   /* store the CNAME */
+
+    if (!dots) {
+        p--; /* Remove final null */
+        /* Uncompressed DNS label set so it ends in null */
+        p = stpcpy(p, LocalDomain);
+    }
+
+    /* Fill the DNS query packet */
+    query = (struct dnsquery *)p;
+    query->qtype  = htons(TYPE_A);
+    query->qclass = htons(CLASS_IN);
+    p += sizeof(struct dnsquery);
+
+    /* Now send it to name server */
+    timeout_ptr = TimeoutTable;
+    timeout = *timeout_ptr++;
+    srv_ptr = dns_server;
+    while (timeout) {
+	srv = *srv_ptr++;
+	if (!srv) {
+	    srv_ptr = dns_server;
+	    srv = *srv_ptr++;
+	}
+
+        udp_write.status      = 0;
+        udp_write.ip          = srv;
+        udp_write.gw          = gateway(srv);
+        udp_write.src_port    = local_port;
+        udp_write.dst_port    = DNS_PORT;
+        udp_write.buffer_size = p - DNSSendBuf;
+        udp_write.buffer      = FAR_PTR(DNSSendBuf);
+        err = pxe_call(PXENV_UDP_WRITE, &udp_write);
+        if (err || udp_write.status)
+            continue;
+
+        oldtime = jiffies();
+	do {
+	    if (jiffies() - oldtime >= timeout)
+		goto again;
+
+            udp_read.status      = 0;
+            udp_read.src_ip      = srv;
+            udp_read.dest_ip     = IPInfo.myip;
+            udp_read.s_port      = DNS_PORT;
+            udp_read.d_port      = local_port;
+            udp_read.buffer_size = PKTBUF_SIZE;
+            udp_read.buffer      = FAR_PTR(DNSRecvBuf);
+            err = pxe_call(PXENV_UDP_READ, &udp_read);
+	} while (err || udp_read.status || hd2->id != hd1->id);
+
+        if ((hd2->flags ^ 0x80) & htons(0xf80f))
+            goto badness;
+
+        ques = htons(hd2->qdcount);   /* Questions */
+        reps = htons(hd2->ancount);   /* Replies   */
+        p = DNSRecvBuf + sizeof(struct dnshdr);
+        while (ques--) {
+            p = dns_skiplabel(p); /* Skip name */
+            p += 4;               /* Skip question trailer */
+        }
+
+        /* Parse the replies */
+        while (reps--) {
+            same = dns_compare(DNSSendBuf + sizeof(struct dnshdr),
+			       p, DNSRecvBuf);
+            p = dns_skiplabel(p);
+            rr = (struct dnsrr *)p;
+            rd_len = ntohs(rr->rdlength);
+            if (same && ntohs(rr->class) == CLASS_IN) {
+		switch (ntohs(rr->type)) {
+		case TYPE_A:
+		    if (rd_len == 4) {
+			result = *(uint32_t *)rr->rdata;
+			goto done;
+		    }
+		    break;
+		case TYPE_CNAME:
+		    dns_copylabel(DNSSendBuf + sizeof(struct dnshdr),
+				  rr->rdata, DNSRecvBuf);
+		    /*
+		     * We should probably rescan the packet from the top
+		     * here, and technically we might have to send a whole
+		     * new request here...
+		     */
+		    break;
+		default:
+		    break;
+		}
+	    }
+
+            /* not the one we want, try next */
+            p += sizeof(struct dnsrr) + rd_len;
+        }
+
+    badness:
+        /*
+         *
+         ; We got back no data from this server.
+         ; Unfortunately, for a recursive, non-authoritative
+         ; query there is no such thing as an NXDOMAIN reply,
+         ; which technically means we can't draw any
+         ; conclusions.  However, in practice that means the
+         ; domain doesn't exist.  If this turns out to be a
+         ; problem, we may want to add code to go through all
+         ; the servers before giving up.
+
+         ; If the DNS server wasn't capable of recursion, and
+         ; isn't capable of giving us an authoritative reply
+         ; (i.e. neither AA or RA set), then at least try a
+         ; different setver...
+        */
+        if (hd2->flags == htons(0x480))
+            continue;
+
+        break; /* failed */
+
+    again:
+	continue;
+    }
+
+done:
+    free_port(local_port);	/* Return port number to the free pool */
+
+    return result;
+}
diff --git a/core/legacynet/idle.c b/core/legacynet/idle.c
new file mode 100644
index 0000000..e089237
--- /dev/null
+++ b/core/legacynet/idle.c
@@ -0,0 +1,112 @@
+/* ----------------------------------------------------------------------- *
+ *
+ *   Copyright 2008 H. Peter Anvin - All Rights Reserved
+ *   Copyright 2009 Intel Corporation; author: H. Peter Anvin
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ *   Boston MA 02110-1301, USA; either version 2 of the License, or
+ *   (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+#include <stdio.h>
+#include <string.h>
+#include <core.h>
+#include <fs.h>
+#include <minmax.h>
+#include <sys/cpu.h>
+#include "pxe.h"
+
+static int pxe_idle_poll(void)
+{
+    static __lowmem char junk_pkt[PKTBUF_SIZE];
+    static __lowmem t_PXENV_UDP_READ read_buf;
+
+    memset(&read_buf, 0, sizeof read_buf);
+
+    read_buf.src_ip  = 0;	 /* Any destination */
+    read_buf.dest_ip = IPInfo.myip;
+    read_buf.s_port  = 0;	 /* Any source port */
+    read_buf.d_port  = htons(9); /* Discard port (not used...) */
+    read_buf.buffer_size = sizeof junk_pkt;
+    read_buf.buffer  = FAR_PTR(junk_pkt);
+
+    pxe_call(PXENV_UDP_READ, &read_buf);
+
+    return 0;
+}
+
+static uint32_t pxe_detect_nic_type(void)
+{
+    static __lowmem t_PXENV_UNDI_GET_NIC_TYPE nic_type;
+
+    if (pxe_call(PXENV_UNDI_GET_NIC_TYPE, &nic_type))
+	return -1;		/* Unknown NIC */
+
+    if (nic_type.NicType != PCI_NIC && nic_type.NicType != CardBus_NIC)
+	return -1;		/* Not a PCI NIC */
+
+    /*
+     * Return VID:DID as a single number, with the VID in the high word
+     * -- this is opposite from the usual order, but it makes it easier to
+     * enforce that the table is sorted.
+     */
+    return (nic_type.info.pci.Vendor_ID << 16) + nic_type.info.pci.Dev_ID;
+}
+
+#define PCI_DEV(vid, did)	(((vid) << 16) + (did))
+
+/* This array should be sorted!! */
+static const uint32_t pxe_need_idle_drain[] =
+{
+    /*
+     * Older Broadcom NICs: they need receive calls on idle to avoid
+     * FIFO stalls.
+     */
+    PCI_DEV(0x14e4, 0x1659),	/* BCM5721 */
+    PCI_DEV(0x14e4, 0x165a),	/* BCM5722 */
+    PCI_DEV(0x14e4, 0x165b),	/* BCM5723 */
+    PCI_DEV(0x14e4, 0x1668),	/* BCM5714 */
+    PCI_DEV(0x14e4, 0x1669),	/* BCM5714S */
+    PCI_DEV(0x14e4, 0x166a),	/* BCM5780 */
+    PCI_DEV(0x14e4, 0x1673),	/* BCM5755M */
+    PCI_DEV(0x14e4, 0x1674),	/* BCM5756ME */
+    PCI_DEV(0x14e4, 0x1678),	/* BCM5715 */
+    PCI_DEV(0x14e4, 0x1679),	/* BCM5715S */
+    PCI_DEV(0x14e4, 0x167b),	/* BCM5755 */
+};
+
+void pxe_idle_init(void)
+{
+    uint32_t dev_id = pxe_detect_nic_type();
+    int l, h;
+    bool found;
+
+    l = 0;
+    h = sizeof pxe_need_idle_drain / sizeof pxe_need_idle_drain[0] - 1;
+
+    found = false;
+    while (h >= l) {
+	int x = (l+h) >> 1;
+	uint32_t id = pxe_need_idle_drain[x];
+
+	if (id == dev_id) {
+	    found = true;
+	    break;
+	} else if (id < dev_id) {
+	    l = x+1;
+	} else {
+	    h = x-1;
+	}
+    }
+
+    if (found)
+	idle_hook_func = pxe_idle_poll;
+}
+
+void pxe_idle_cleanup(void)
+{
+    idle_hook_func = NULL;
+}
diff --git a/core/legacynet/portnum.c b/core/legacynet/portnum.c
new file mode 100644
index 0000000..e10af29
--- /dev/null
+++ b/core/legacynet/portnum.c
@@ -0,0 +1,68 @@
+/* ----------------------------------------------------------------------- *
+ *
+ *   Copyright 2010 Intel Corporation; author: H. Peter Anvin
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ *   Boston MA 02110-1301, USA; either version 2 of the License, or
+ *   (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <netinet/in.h>
+#include "pxe.h"
+
+/* Port number bitmap - port numbers 49152 (0xc000) to 57343 (0xefff) */
+#define PORT_NUMBER_BASE	49152
+#define PORT_NUMBER_COUNT	8192 /* Power of 2, please */
+static uint32_t port_number_bitmap[PORT_NUMBER_COUNT/32];
+static uint16_t first_port_number /* = 0 */;
+
+/*
+ * Bitmap functions
+ */
+static bool test_bit(const uint32_t *bitmap, int32_t index)
+{
+    uint8_t st;
+    asm("btl %2,%1 ; setc %0" : "=qm" (st) : "m" (*bitmap), "r" (index));
+    return st;
+}
+
+static void set_bit(uint32_t *bitmap, int32_t index)
+{
+    asm volatile("btsl %1,%0" : "+m" (*bitmap) : "r" (index) : "memory");
+}
+
+static void clr_bit(uint32_t *bitmap, int32_t index)
+{
+    asm volatile("btcl %1,%0" : "+m" (*bitmap) : "r" (index) : "memory");
+}
+
+/*
+ * Get and free a port number (host byte order)
+ */
+uint16_t get_port(void)
+{
+    uint16_t port;
+
+    do {
+	port = first_port_number++;
+	first_port_number &= PORT_NUMBER_COUNT - 1;
+    } while (test_bit(port_number_bitmap, port));
+
+    set_bit(port_number_bitmap, port);
+    return htons(port + PORT_NUMBER_BASE);
+}
+
+void free_port(uint16_t port)
+{
+    port = ntohs(port) - PORT_NUMBER_BASE;
+
+    if (port >= PORT_NUMBER_COUNT)
+	return;
+
+    clr_bit(port_number_bitmap, port);
+}
diff --git a/core/pxelinux.asm b/core/pxelinux.asm
index 7b981a3..d3215e7 100644
--- a/core/pxelinux.asm
+++ b/core/pxelinux.asm
@@ -525,7 +525,9 @@ pxe_file_exit_hook:
 ;  PXE modules
 ; -----------------------------------------------------------------------------
 
+%if IS_LPXELINUX
 %include "pxeisr.inc"
+%endif
 
 ; -----------------------------------------------------------------------------
 ;  Common modules


More information about the Syslinux-commits mailing list