#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" #include <dprintf.h> const struct url_scheme url_schemes[] = { { "tftp", tftp_open, 0 }, { "http", http_open, O_DIRECTORY }, { "ftp", ftp_open, O_DIRECTORY }, { NULL, NULL, 0 }, }; /** * Open a socket * * @param:socket, the socket to open * * @out: error code, 0 on success, -1 on failure */ int core_udp_open(struct pxe_pvt_inode *socket) { struct net_private_lwip *priv = &socket->net.lwip; int err; priv->conn = netconn_new(NETCONN_UDP); if (!priv->conn) return -1; priv->conn->recv_timeout = 15; /* A 15 ms recv timeout... */ err = netconn_bind(priv->conn, NULL, 0); if (err) { ddprintf("netconn_bind error %d\n", err); return -1; } return 0; } /** * Close a socket * * @param:socket, the socket to open */ void core_udp_close(struct pxe_pvt_inode *socket) { struct net_private_lwip *priv = &socket->net.lwip; 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 core_udp_connect(struct pxe_pvt_inode *socket, uint32_t ip, uint16_t port) { struct net_private_lwip *priv = &socket->net.lwip; struct ip_addr addr; dprintf("net_core_connect: %08X %04X\n", ntohl(ip), port); addr.addr = ip; netconn_connect(priv->conn, &addr, port); } /** * Tear down a connection on an open socket * * @param:socket, the open socket */ void core_udp_disconnect(struct pxe_pvt_inode *socket) { struct net_private_lwip *priv = &socket->net.lwip; 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 core_udp_recv(struct pxe_pvt_inode *socket, void *buf, uint16_t *buf_len, uint32_t *src_ip, uint16_t *src_port) { struct net_private_lwip *priv = &socket->net.lwip; 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 core_udp_send(struct pxe_pvt_inode *socket, const void *data, size_t len) { struct netconn *conn = socket->net.lwip.conn; struct netbuf *nbuf; void *pbuf; int err; nbuf = netbuf_new(); if (!nbuf) { ddprintf("netbuf allocation error\n"); return; } pbuf = netbuf_alloc(nbuf, len); if (!pbuf) { ddprintf("pbuf allocation error\n"); goto out; } memcpy(pbuf, data, len); err = netconn_send(conn, nbuf); if (err) { ddprintf("netconn_send error %d\n", err); goto out; } out: netbuf_delete(nbuf); } /** * Send a UDP packet to a destination * * @param:socket, the open socket * @param:data, data buffer to send * @param:len, size of data bufer * @param:ip, the ip address * @param:port, the port number, host-byte order */ void core_udp_sendto(struct pxe_pvt_inode *socket, const void *data, size_t len, uint32_t ip, uint16_t port) { struct netconn *conn = socket->net.lwip.conn; struct ip_addr addr; struct netbuf *nbuf; void *pbuf; int err; nbuf = netbuf_new(); if (!nbuf) { ddprintf("netbuf allocation error\n"); return; } pbuf = netbuf_alloc(nbuf, len); if (!pbuf) { ddprintf("pbuf allocation error\n"); goto out; } memcpy(pbuf, data, len); dprintf("core_udp_sendto: %08X %04X\n", ntohl(ip), port); addr.addr = ip; err = netconn_sendto(conn, nbuf, &addr, port); if (err) { ddprintf("netconn_sendto 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) { ddprintf("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); ddprintf("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); } int core_tcp_open(struct pxe_pvt_inode *socket) { socket->net.lwip.conn = netconn_new(NETCONN_TCP); if (!socket->net.lwip.conn) return -1; return 0; } int core_tcp_connect(struct pxe_pvt_inode *socket, uint32_t ip, uint16_t port) { struct ip_addr addr; err_t err; addr.addr = ip; err = netconn_connect(socket->net.lwip.conn, &addr, port); if (err) { printf("netconn_connect error %d\n", err); return -1; } return 0; } int core_tcp_write(struct pxe_pvt_inode *socket, const void *data, size_t len, bool copy) { err_t err; u8_t flags = copy ? NETCONN_COPY : NETCONN_NOCOPY; err = netconn_write(socket->net.lwip.conn, data, len, flags); if (err) { printf("netconn_write failed: %d\n", err); return -1; } return 0; } void core_tcp_close_file(struct inode *inode) { struct pxe_pvt_inode *socket = PVT(inode); if (socket->net.lwip.conn) { netconn_delete(socket->net.lwip.conn); socket->net.lwip.conn = NULL; } if (socket->net.lwip.buf) { netbuf_delete(socket->net.lwip.buf); socket->net.lwip.buf = NULL; } } bool core_tcp_is_connected(struct pxe_pvt_inode *socket) { if (socket->net.lwip.conn) return true; return false; } void core_tcp_fill_buffer(struct inode *inode) { struct pxe_pvt_inode *socket = PVT(inode); void *data; u16_t len; err_t err; /* Clean up or advance an inuse netbuf */ if (socket->net.lwip.buf) { if (netbuf_next(socket->net.lwip.buf) < 0) { netbuf_delete(socket->net.lwip.buf); socket->net.lwip.buf = NULL; } } /* If needed get a new netbuf */ if (!socket->net.lwip.buf) { err = netconn_recv(socket->net.lwip.conn, &(socket->net.lwip.buf)); if (!socket->net.lwip.buf || err) { socket->tftp_goteof = 1; if (inode->size == -1) inode->size = socket->tftp_filepos; socket->ops->close(inode); return; } } /* Report the current fragment of the netbuf */ err = netbuf_data(socket->net.lwip.buf, &data, &len); if (err) { printf("netbuf_data err: %d\n", err); kaboom(); } socket->tftp_dataptr = data; socket->tftp_filepos += len; socket->tftp_bytesleft = len; return; }