blob: a43ac465f29e1e93c910864fc978955174c7c977 [file] [log] [blame]
#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;
}