mirror of
https://github.com/xcat2/xNBA.git
synced 2024-11-22 17:41:55 +00:00
Partial migration of UDP to data-xfer interface. (Will not link at
present; DHCP is broken).
This commit is contained in:
parent
564ac70a42
commit
e381714c07
@ -14,7 +14,8 @@
|
||||
#include <gpxe/tcpip.h>
|
||||
#include <gpxe/if_ether.h>
|
||||
|
||||
struct net_device;
|
||||
struct xfer_interface;
|
||||
struct sockaddr;
|
||||
|
||||
/**
|
||||
* UDP constants
|
||||
@ -24,147 +25,23 @@ struct net_device;
|
||||
#define UDP_MAX_TXIOB ETH_MAX_MTU
|
||||
#define UDP_MIN_TXIOB ETH_ZLEN
|
||||
|
||||
typedef uint16_t port_t;
|
||||
|
||||
/**
|
||||
* A UDP header
|
||||
*/
|
||||
struct udp_header {
|
||||
port_t source_port;
|
||||
port_t dest_port;
|
||||
/** Source port */
|
||||
uint16_t src;
|
||||
/** Destination port */
|
||||
uint16_t dest;
|
||||
/** Length */
|
||||
uint16_t len;
|
||||
/** Checksum */
|
||||
uint16_t chksum;
|
||||
};
|
||||
|
||||
struct udp_connection;
|
||||
|
||||
/**
|
||||
* UDP operations
|
||||
*
|
||||
*/
|
||||
struct udp_operations {
|
||||
|
||||
/**
|
||||
* Transmit data
|
||||
*
|
||||
* @v conn UDP connection
|
||||
* @v buf Temporary data buffer
|
||||
* @v len Length of temporary data buffer
|
||||
* @ret rc Return status code
|
||||
*
|
||||
* The application may use the temporary data buffer to
|
||||
* construct the data to be sent. Note that merely filling
|
||||
* the buffer will do nothing; the application must call
|
||||
* udp_send() in order to actually transmit the data. Use of
|
||||
* the buffer is not compulsory; the application may call
|
||||
* udp_send() on any block of data.
|
||||
*/
|
||||
int ( * senddata ) ( struct udp_connection *conn, void *buf,
|
||||
size_t len );
|
||||
/**
|
||||
* New data received
|
||||
*
|
||||
* @v conn UDP connection
|
||||
* @v data Data
|
||||
* @v len Length of data
|
||||
* @v st_src Source address
|
||||
* @v st_dest Destination address
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
int ( * newdata ) ( struct udp_connection *conn, void *data,
|
||||
size_t len, struct sockaddr_tcpip *st_src,
|
||||
struct sockaddr_tcpip *st_dest );
|
||||
};
|
||||
|
||||
/**
|
||||
* A UDP connection
|
||||
*
|
||||
*/
|
||||
struct udp_connection {
|
||||
/** Address of the remote end of the connection */
|
||||
struct sockaddr_tcpip peer;
|
||||
/** Local port on which the connection receives packets */
|
||||
port_t local_port;
|
||||
/** Transmit buffer */
|
||||
struct io_buffer *tx_iob;
|
||||
/** List of registered connections */
|
||||
struct list_head list;
|
||||
/** Operations table for this connection */
|
||||
struct udp_operations *udp_op;
|
||||
};
|
||||
|
||||
/*
|
||||
* Functions provided to the application layer
|
||||
*/
|
||||
|
||||
/**
|
||||
* Bind UDP connection to all local ports
|
||||
*
|
||||
* @v conn UDP connection
|
||||
*
|
||||
* A promiscuous UDP connection will receive packets with any
|
||||
* destination UDP port. This is required in order to support the PXE
|
||||
* UDP API.
|
||||
*
|
||||
* If the promiscuous connection is not the only UDP connection, the
|
||||
* behaviour is undefined.
|
||||
*/
|
||||
static inline void udp_bind_promisc ( struct udp_connection *conn ) {
|
||||
conn->local_port = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect UDP connection to remote host and port
|
||||
*
|
||||
* @v conn UDP connection
|
||||
* @v peer Destination socket address
|
||||
*
|
||||
* This function sets the default address for transmitted packets,
|
||||
* i.e. the address used when udp_send() is called rather than
|
||||
* udp_sendto().
|
||||
*/
|
||||
static inline void udp_connect ( struct udp_connection *conn,
|
||||
struct sockaddr_tcpip *peer ) {
|
||||
memcpy ( &conn->peer, peer, sizeof ( conn->peer ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect UDP connection to remote port
|
||||
*
|
||||
* @v conn UDP connection
|
||||
* @v port Destination port
|
||||
*
|
||||
* This function sets only the port part of the default address for
|
||||
* transmitted packets.
|
||||
*/
|
||||
static inline void udp_connect_port ( struct udp_connection *conn,
|
||||
uint16_t port ) {
|
||||
conn->peer.st_port = port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get default address for transmitted packets
|
||||
*
|
||||
* @v conn UDP connection
|
||||
* @ret peer Default destination socket address
|
||||
*/
|
||||
static inline struct sockaddr_tcpip *
|
||||
udp_peer ( struct udp_connection *conn ) {
|
||||
return &conn->peer;
|
||||
}
|
||||
|
||||
extern int udp_bind ( struct udp_connection *conn, uint16_t local_port );
|
||||
extern int udp_open ( struct udp_connection *conn, uint16_t local_port );
|
||||
extern void udp_close ( struct udp_connection *conn );
|
||||
|
||||
extern int udp_senddata ( struct udp_connection *conn );
|
||||
extern int udp_send ( struct udp_connection *conn,
|
||||
const void *data, size_t len );
|
||||
extern int udp_sendto ( struct udp_connection *conn,
|
||||
struct sockaddr_tcpip *peer,
|
||||
const void *data, size_t len );
|
||||
int udp_sendto_via ( struct udp_connection *conn, struct sockaddr_tcpip *peer,
|
||||
struct net_device *netdev, const void *data,
|
||||
size_t len );
|
||||
extern int udp_open_promisc ( struct xfer_interface *xfer );
|
||||
extern int udp_open ( struct xfer_interface *xfer, struct sockaddr *peer,
|
||||
struct sockaddr *local );
|
||||
|
||||
#endif /* _GPXE_UDP_H */
|
||||
|
||||
|
@ -34,8 +34,6 @@
|
||||
struct pxe_udp_connection {
|
||||
/** Data transfer interface to UDP stack */
|
||||
struct xfer_interface xfer;
|
||||
/** "Connection is open" flag */
|
||||
int open;
|
||||
/** Local address */
|
||||
struct sockaddr_in local;
|
||||
/** Current PXENV_UDP_READ parameter block */
|
||||
@ -169,21 +167,19 @@ static struct pxe_udp_connection pxe_udp = {
|
||||
*
|
||||
*/
|
||||
PXENV_EXIT_t pxenv_udp_open ( struct s_PXENV_UDP_OPEN *pxenv_udp_open ) {
|
||||
int rc;
|
||||
|
||||
DBG ( "PXENV_UDP_OPEN" );
|
||||
|
||||
/* Check connection is not already open */
|
||||
if ( pxe_udp.open ) {
|
||||
pxenv_udp_open->Status = PXENV_STATUS_UDP_OPEN;
|
||||
return PXENV_EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* Record source IP address */
|
||||
pxe_udp.local.sin_addr.s_addr = pxenv_udp_open->src_ip;
|
||||
|
||||
/* Open promiscuous UDP connection */
|
||||
udp_open_promisc ( &pxe_udp.xfer );
|
||||
pxe_udp.open = 1;
|
||||
xfer_close ( &pxe_udp.xfer, 0 );
|
||||
if ( ( rc = udp_open_promisc ( &pxe_udp.xfer ) ) != 0 ) {
|
||||
pxenv_udp_open->Status = PXENV_STATUS ( rc );
|
||||
return PXENV_EXIT_FAILURE;
|
||||
}
|
||||
|
||||
pxenv_udp_open->Status = PXENV_STATUS_SUCCESS;
|
||||
return PXENV_EXIT_SUCCESS;
|
||||
@ -213,15 +209,8 @@ PXENV_EXIT_t pxenv_udp_open ( struct s_PXENV_UDP_OPEN *pxenv_udp_open ) {
|
||||
PXENV_EXIT_t pxenv_udp_close ( struct s_PXENV_UDP_CLOSE *pxenv_udp_close ) {
|
||||
DBG ( "PXENV_UDP_CLOSE" );
|
||||
|
||||
/* Check connection is open */
|
||||
if ( ! pxe_udp.open ) {
|
||||
pxenv_udp_close->Status = PXENV_STATUS_UDP_CLOSED;
|
||||
return PXENV_EXIT_SUCCESS; /* Well, it *is* closed */
|
||||
}
|
||||
|
||||
/* Close UDP connection */
|
||||
udp_close_promisc ( &pxe_udp.xfer );
|
||||
pxe_udp.open = 0;
|
||||
xfer_close ( &pxe_udp.xfer, 0 );
|
||||
|
||||
pxenv_udp_close->Status = PXENV_STATUS_SUCCESS;
|
||||
return PXENV_EXIT_SUCCESS;
|
||||
@ -281,12 +270,6 @@ PXENV_EXIT_t pxenv_udp_write ( struct s_PXENV_UDP_WRITE *pxenv_udp_write ) {
|
||||
|
||||
DBG ( "PXENV_UDP_WRITE" );
|
||||
|
||||
/* Check connection is open */
|
||||
if ( ! pxe_udp.open ) {
|
||||
pxenv_udp_write->Status = PXENV_STATUS_UDP_CLOSED;
|
||||
return PXENV_EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* Construct destination socket address */
|
||||
memset ( &dest, 0, sizeof ( dest ) );
|
||||
dest.sin_family = AF_INET;
|
||||
@ -383,12 +366,6 @@ PXENV_EXIT_t pxenv_udp_read ( struct s_PXENV_UDP_READ *pxenv_udp_read ) {
|
||||
|
||||
DBG ( "PXENV_UDP_READ" );
|
||||
|
||||
/* Check connection is open */
|
||||
if ( ! pxe_udp.open ) {
|
||||
pxenv_udp_read->Status = PXENV_STATUS_UDP_CLOSED;
|
||||
return PXENV_EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* Try receiving a packet */
|
||||
pxe_udp.pxenv_udp_read = pxenv_udp_read;
|
||||
step();
|
||||
|
483
src/net/udp.c
483
src/net/udp.c
@ -1,11 +1,14 @@
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <byteswap.h>
|
||||
#include <errno.h>
|
||||
#include <gpxe/tcpip.h>
|
||||
#include <gpxe/iobuf.h>
|
||||
#include <gpxe/netdevice.h>
|
||||
#include <gpxe/xfer.h>
|
||||
#include <gpxe/open.h>
|
||||
#include <gpxe/uri.h>
|
||||
#include <gpxe/udp.h>
|
||||
|
||||
/** @file
|
||||
@ -13,233 +16,234 @@
|
||||
* UDP protocol
|
||||
*/
|
||||
|
||||
struct tcpip_protocol udp_protocol;
|
||||
/**
|
||||
* A UDP connection
|
||||
*
|
||||
*/
|
||||
struct udp_connection {
|
||||
/** Reference counter */
|
||||
struct refcnt refcnt;
|
||||
/** List of UDP connections */
|
||||
struct list_head list;
|
||||
|
||||
/** Data transfer interface */
|
||||
struct xfer_interface xfer;
|
||||
|
||||
/** Remote socket address */
|
||||
struct sockaddr_tcpip peer;
|
||||
/** Local port on which the connection receives packets */
|
||||
unsigned int local_port;
|
||||
};
|
||||
|
||||
/**
|
||||
* List of registered UDP connections
|
||||
*/
|
||||
static LIST_HEAD ( udp_conns );
|
||||
|
||||
/* Forward declatations */
|
||||
static struct xfer_interface_operations udp_xfer_operations;
|
||||
struct tcpip_protocol udp_protocol;
|
||||
|
||||
/**
|
||||
* Bind UDP connection to local port
|
||||
*
|
||||
* @v conn UDP connection
|
||||
* @v local_port Local port, in network byte order
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
int udp_bind ( struct udp_connection *conn, uint16_t local_port ) {
|
||||
struct udp_connection *existing;
|
||||
|
||||
list_for_each_entry ( existing, &udp_conns, list ) {
|
||||
if ( existing->local_port == local_port )
|
||||
return -EADDRINUSE;
|
||||
}
|
||||
conn->local_port = local_port;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a local port
|
||||
*
|
||||
* @v conn UDP connection
|
||||
* @v local_port Local port, in network byte order, or zero
|
||||
* @v udp UDP connection
|
||||
* @v port Local port, in network byte order, or zero
|
||||
* @ret rc Return status code
|
||||
*
|
||||
* Opens the UDP connection and binds to a local port. If no local
|
||||
* port is specified, the first available port will be used.
|
||||
*/
|
||||
int udp_open ( struct udp_connection *conn, uint16_t local_port ) {
|
||||
static int udp_bind ( struct udp_connection *udp, unsigned int port ) {
|
||||
struct udp_connection *existing;
|
||||
static uint16_t try_port = 1024;
|
||||
int rc;
|
||||
|
||||
/* If no port specified, find the first available port */
|
||||
if ( ! local_port ) {
|
||||
if ( ! port ) {
|
||||
for ( ; try_port ; try_port++ ) {
|
||||
if ( try_port < 1024 )
|
||||
continue;
|
||||
if ( udp_open ( conn, htons ( try_port ) ) == 0 )
|
||||
if ( udp_bind ( udp, htons ( try_port ) ) == 0 )
|
||||
return 0;
|
||||
}
|
||||
return -EADDRINUSE;
|
||||
}
|
||||
|
||||
/* Attempt bind to local port */
|
||||
if ( ( rc = udp_bind ( conn, local_port ) ) != 0 ) {
|
||||
DBGC ( conn, "UDP %p could not bind to local port %d: %s\n",
|
||||
conn, local_port, strerror ( rc ) );
|
||||
return rc;
|
||||
list_for_each_entry ( existing, &udp_conns, list ) {
|
||||
if ( existing->local_port == port ) {
|
||||
DBGC ( udp, "UDP %p could not bind: port %d in use\n",
|
||||
udp, ntohs ( port ) );
|
||||
return -EADDRINUSE;
|
||||
}
|
||||
}
|
||||
udp->local_port = port;
|
||||
|
||||
/* Add to UDP connection list */
|
||||
list_add ( &conn->list, &udp_conns );
|
||||
DBGC ( conn, "UDP %p opened on port %d\n", conn,
|
||||
ntohs ( local_port ) );
|
||||
DBGC ( udp, "UDP %p bound to port %d\n", udp, ntohs ( port ) );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a UDP connection
|
||||
*
|
||||
* @v xfer Data transfer interface
|
||||
* @v peer Peer socket address
|
||||
* @v local Local socket address, or NULL
|
||||
* @v promisc Socket is promiscuous
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int udp_open_common ( struct xfer_interface *xfer,
|
||||
struct sockaddr *peer, struct sockaddr *local,
|
||||
int promisc ) {
|
||||
struct sockaddr_tcpip *st_peer = ( struct sockaddr_tcpip * ) peer;
|
||||
struct sockaddr_tcpip *st_local = ( struct sockaddr_tcpip * ) local;
|
||||
struct udp_connection *udp;
|
||||
unsigned int bind_port;
|
||||
int rc;
|
||||
|
||||
/* Allocate and initialise structure */
|
||||
udp = malloc ( sizeof ( *udp ) );
|
||||
if ( ! udp )
|
||||
return -ENOMEM;
|
||||
DBGC ( udp, "UDP %p allocated\n", udp );
|
||||
memset ( udp, 0, sizeof ( *udp ) );
|
||||
xfer_init ( &udp->xfer, &udp_xfer_operations, &udp->refcnt );
|
||||
memcpy ( &udp->peer, st_peer, sizeof ( udp->peer ) );
|
||||
|
||||
/* Bind to local port */
|
||||
if ( ! promisc ) {
|
||||
bind_port = ( st_local ? st_local->st_port : 0 );
|
||||
if ( ( rc = udp_bind ( udp, bind_port ) ) != 0 )
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Attach parent interface, transfer reference to connection
|
||||
* list and return
|
||||
*/
|
||||
xfer_plug_plug ( &udp->xfer, xfer );
|
||||
list_add ( &udp->list, &udp_conns );
|
||||
return 0;
|
||||
|
||||
err:
|
||||
ref_put ( &udp->refcnt );
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a UDP connection
|
||||
*
|
||||
* @v xfer Data transfer interface
|
||||
* @v peer Peer socket address
|
||||
* @v local Local socket address, or NULL
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
int udp_open ( struct xfer_interface *xfer, struct sockaddr *peer,
|
||||
struct sockaddr *local ) {
|
||||
return udp_open_common ( xfer, peer, local, 0 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a promiscuous UDP connection
|
||||
*
|
||||
* @v xfer Data transfer interface
|
||||
* @ret rc Return status code
|
||||
*
|
||||
* Promiscuous UDP connections are required in order to support the
|
||||
* PXE API.
|
||||
*/
|
||||
int udp_open_promisc ( struct xfer_interface *xfer ) {
|
||||
return udp_open_common ( xfer, NULL, NULL, 1 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Close a UDP connection
|
||||
*
|
||||
* @v conn UDP connection
|
||||
* @v udp UDP connection
|
||||
* @v rc Reason for close
|
||||
*/
|
||||
void udp_close ( struct udp_connection *conn ) {
|
||||
list_del ( &conn->list );
|
||||
DBGC ( conn, "UDP %p closed\n", conn );
|
||||
static void udp_close ( struct udp_connection *udp, int rc ) {
|
||||
|
||||
/* Close data transfer interface */
|
||||
xfer_nullify ( &udp->xfer );
|
||||
xfer_close ( &udp->xfer, rc );
|
||||
|
||||
/* Remove from list of connections and drop list's reference */
|
||||
list_del ( &udp->list );
|
||||
ref_put ( &udp->refcnt );
|
||||
|
||||
DBGC ( udp, "UDP %p closed\n", udp );
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate I/O buffer for UDP
|
||||
*
|
||||
* @v conn UDP connection
|
||||
* @ret iobuf I/O buffer, or NULL
|
||||
*/
|
||||
static struct io_buffer * udp_alloc_iob ( struct udp_connection *conn ) {
|
||||
struct io_buffer *iobuf;
|
||||
|
||||
iobuf = alloc_iob ( UDP_MAX_TXIOB );
|
||||
if ( ! iobuf ) {
|
||||
DBGC ( conn, "UDP %p cannot allocate buffer of length %d\n",
|
||||
conn, UDP_MAX_TXIOB );
|
||||
return NULL;
|
||||
}
|
||||
iob_reserve ( iobuf, UDP_MAX_HLEN );
|
||||
return iobuf;
|
||||
}
|
||||
|
||||
/**
|
||||
* User request to send data via a UDP connection
|
||||
*
|
||||
* @v conn UDP connection
|
||||
*
|
||||
* This function allocates buffer space and invokes the function's
|
||||
* senddata() callback. The callback may use the buffer space as
|
||||
* temporary storage space.
|
||||
*/
|
||||
int udp_senddata ( struct udp_connection *conn ) {
|
||||
int rc;
|
||||
|
||||
conn->tx_iob = udp_alloc_iob ( conn );
|
||||
if ( ! conn->tx_iob )
|
||||
return -ENOMEM;
|
||||
|
||||
rc = conn->udp_op->senddata ( conn, conn->tx_iob->data,
|
||||
iob_tailroom ( conn->tx_iob ) );
|
||||
if ( rc != 0 ) {
|
||||
DBGC ( conn, "UDP %p application could not send packet: %s\n",
|
||||
conn, strerror ( rc ) );
|
||||
}
|
||||
|
||||
if ( conn->tx_iob ) {
|
||||
free_iob ( conn->tx_iob );
|
||||
conn->tx_iob = NULL;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transmit data via a UDP connection to a specified address
|
||||
*
|
||||
* @v conn UDP connection
|
||||
* @v peer Destination address
|
||||
* @v netdev Network device to use if no route found, or NULL
|
||||
* @v data Data to send
|
||||
* @v len Length of data
|
||||
* @v udp UDP connection
|
||||
* @v iobuf I/O buffer
|
||||
* @v src_port Source port, or 0 to use default
|
||||
* @v dest Destination address, or NULL to use default
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
int udp_sendto_via ( struct udp_connection *conn, struct sockaddr_tcpip *peer,
|
||||
struct net_device *netdev, const void *data,
|
||||
size_t len ) {
|
||||
static int udp_tx ( struct udp_connection *udp, struct io_buffer *iobuf,
|
||||
unsigned int src_port, struct sockaddr_tcpip *dest ) {
|
||||
struct udp_header *udphdr;
|
||||
struct io_buffer *iobuf;
|
||||
struct net_device *netdev = NULL;
|
||||
size_t len;
|
||||
int rc;
|
||||
|
||||
/* Use precreated I/O buffer if one is available */
|
||||
if ( conn->tx_iob ) {
|
||||
iobuf = conn->tx_iob;
|
||||
conn->tx_iob = NULL;
|
||||
} else {
|
||||
iobuf = udp_alloc_iob ( conn );
|
||||
if ( ! iobuf )
|
||||
return -ENOMEM;
|
||||
#warning "netdev?"
|
||||
|
||||
/* Check we can accommodate the header */
|
||||
if ( ( rc = iob_ensure_headroom ( iobuf, UDP_MAX_HLEN ) ) != 0 ) {
|
||||
free_iob ( iobuf );
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Avoid overflowing TX buffer */
|
||||
if ( len > iob_tailroom ( iobuf ) )
|
||||
len = iob_tailroom ( iobuf );
|
||||
/* Fill in default values if not explicitly provided */
|
||||
if ( ! src_port )
|
||||
src_port = udp->local_port;
|
||||
if ( ! dest )
|
||||
dest = &udp->peer;
|
||||
|
||||
/* Copy payload */
|
||||
memmove ( iob_put ( iobuf, len ), data, len );
|
||||
|
||||
/*
|
||||
* Add the UDP header
|
||||
*
|
||||
* Covert all 16- and 32- bit integers into network btye order before
|
||||
* sending it over the network
|
||||
*/
|
||||
/* Add the UDP header */
|
||||
udphdr = iob_push ( iobuf, sizeof ( *udphdr ) );
|
||||
udphdr->dest_port = peer->st_port;
|
||||
udphdr->source_port = conn->local_port;
|
||||
udphdr->len = htons ( iob_len ( iobuf ) );
|
||||
len = iob_len ( iobuf );
|
||||
udphdr->dest = dest->st_port;
|
||||
udphdr->src = src_port;
|
||||
udphdr->len = htons ( len );
|
||||
udphdr->chksum = 0;
|
||||
udphdr->chksum = tcpip_chksum ( udphdr, sizeof ( *udphdr ) + len );
|
||||
udphdr->chksum = tcpip_chksum ( udphdr, len );
|
||||
|
||||
/* Dump debugging information */
|
||||
DBGC ( conn, "UDP %p TX %d->%d len %zd\n", conn,
|
||||
ntohs ( udphdr->source_port ), ntohs ( udphdr->dest_port ),
|
||||
DBGC ( udp, "UDP %p TX %d->%d len %zd\n", udp,
|
||||
ntohs ( udphdr->src ), ntohs ( udphdr->dest ),
|
||||
ntohs ( udphdr->len ) );
|
||||
|
||||
/* Send it to the next layer for processing */
|
||||
if ( ( rc = tcpip_tx ( iobuf, &udp_protocol, peer, netdev,
|
||||
if ( ( rc = tcpip_tx ( iobuf, &udp_protocol, dest, netdev,
|
||||
&udphdr->chksum ) ) != 0 ) {
|
||||
DBGC ( conn, "UDP %p could not transmit packet: %s\n",
|
||||
conn, strerror ( rc ) );
|
||||
DBGC ( udp, "UDP %p could not transmit packet: %s\n",
|
||||
udp, strerror ( rc ) );
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transmit data via a UDP connection to a specified address
|
||||
*
|
||||
* @v conn UDP connection
|
||||
* @v peer Destination address
|
||||
* @v data Data to send
|
||||
* @v len Length of data
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
int udp_sendto ( struct udp_connection *conn, struct sockaddr_tcpip *peer,
|
||||
const void *data, size_t len ) {
|
||||
return udp_sendto_via ( conn, peer, NULL, data, len );
|
||||
}
|
||||
|
||||
/**
|
||||
* Transmit data via a UDP connection
|
||||
*
|
||||
* @v conn UDP connection
|
||||
* @v data Data to send
|
||||
* @v len Length of data
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
int udp_send ( struct udp_connection *conn, const void *data, size_t len ) {
|
||||
return udp_sendto ( conn, &conn->peer, data, len );
|
||||
}
|
||||
|
||||
/**
|
||||
* Identify UDP connection by local port number
|
||||
*
|
||||
* @v local_port Local port (in network-endian order)
|
||||
* @ret conn TCP connection, or NULL
|
||||
* @ret udp UDP connection, or NULL
|
||||
*/
|
||||
static struct udp_connection * udp_demux ( uint16_t local_port ) {
|
||||
struct udp_connection *conn;
|
||||
static struct udp_connection * udp_demux ( unsigned int local_port ) {
|
||||
struct udp_connection *udp;
|
||||
|
||||
list_for_each_entry ( conn, &udp_conns, list ) {
|
||||
if ( ( conn->local_port == local_port ) ||
|
||||
( conn->local_port == 0 ) ) {
|
||||
return conn;
|
||||
list_for_each_entry ( udp, &udp_conns, list ) {
|
||||
if ( ( udp->local_port == local_port ) ||
|
||||
( udp->local_port == 0 ) ) {
|
||||
return udp;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
@ -257,9 +261,10 @@ static struct udp_connection * udp_demux ( uint16_t local_port ) {
|
||||
static int udp_rx ( struct io_buffer *iobuf, struct sockaddr_tcpip *st_src,
|
||||
struct sockaddr_tcpip *st_dest, uint16_t pshdr_csum ) {
|
||||
struct udp_header *udphdr = iobuf->data;
|
||||
struct udp_connection *conn;
|
||||
struct udp_connection *udp;
|
||||
struct xfer_metadata meta;
|
||||
size_t ulen;
|
||||
uint16_t csum;
|
||||
unsigned int csum;
|
||||
int rc = 0;
|
||||
|
||||
/* Sanity check packet */
|
||||
@ -294,37 +299,30 @@ static int udp_rx ( struct io_buffer *iobuf, struct sockaddr_tcpip *st_src,
|
||||
}
|
||||
|
||||
/* Parse parameters from header and strip header */
|
||||
st_src->st_port = udphdr->source_port;
|
||||
st_dest->st_port = udphdr->dest_port;
|
||||
conn = udp_demux ( udphdr->dest_port );
|
||||
st_src->st_port = udphdr->src;
|
||||
st_dest->st_port = udphdr->dest;
|
||||
udp = udp_demux ( udphdr->dest );
|
||||
iob_unput ( iobuf, ( iob_len ( iobuf ) - ulen ) );
|
||||
iob_pull ( iobuf, sizeof ( *udphdr ) );
|
||||
|
||||
/* Dump debugging information */
|
||||
DBGC ( conn, "UDP %p RX %d<-%d len %zd\n", conn,
|
||||
ntohs ( udphdr->dest_port ), ntohs ( udphdr->source_port ),
|
||||
ulen );
|
||||
DBGC ( udp, "UDP %p RX %d<-%d len %zd\n", udp,
|
||||
ntohs ( udphdr->dest ), ntohs ( udphdr->src ), ulen );
|
||||
|
||||
/* Ignore if no matching connection found */
|
||||
if ( ! conn ) {
|
||||
if ( ! udp ) {
|
||||
DBG ( "No UDP connection listening on port %d\n",
|
||||
ntohs ( udphdr->dest_port ) );
|
||||
ntohs ( udphdr->dest ) );
|
||||
rc = -ENOTCONN;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Pass data to application */
|
||||
if ( conn->udp_op->newdata ) {
|
||||
rc = conn->udp_op->newdata ( conn, iobuf->data, iob_len ( iobuf ),
|
||||
st_src, st_dest );
|
||||
if ( rc != 0 ) {
|
||||
DBGC ( conn, "UDP %p application rejected packet: %s\n",
|
||||
conn, strerror ( rc ) );
|
||||
}
|
||||
} else {
|
||||
DBGC ( conn, "UDP %p application has no newdata handler for " \
|
||||
"incoming packet\n", conn );
|
||||
}
|
||||
memset ( &meta, 0, sizeof ( meta ) );
|
||||
meta.src = ( struct sockaddr * ) st_src;
|
||||
meta.dest = ( struct sockaddr * ) st_dest;
|
||||
rc = xfer_deliver_iob_meta ( &udp->xfer, iobuf, &meta );
|
||||
iobuf = NULL;
|
||||
|
||||
done:
|
||||
free_iob ( iobuf );
|
||||
@ -336,3 +334,130 @@ struct tcpip_protocol udp_protocol __tcpip_protocol = {
|
||||
.rx = udp_rx,
|
||||
.tcpip_proto = IP_UDP,
|
||||
};
|
||||
|
||||
/***************************************************************************
|
||||
*
|
||||
* Data transfer interface
|
||||
*
|
||||
***************************************************************************
|
||||
*/
|
||||
|
||||
/**
|
||||
* Close interface
|
||||
*
|
||||
* @v xfer Data transfer interface
|
||||
* @v rc Reason for close
|
||||
*/
|
||||
static void udp_xfer_close ( struct xfer_interface *xfer, int rc ) {
|
||||
struct udp_connection *udp =
|
||||
container_of ( xfer, struct udp_connection, xfer );
|
||||
|
||||
/* Close connection */
|
||||
udp_close ( udp, rc );
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate I/O buffer for UDP
|
||||
*
|
||||
* @v xfer Data transfer interface
|
||||
* @v len Payload size
|
||||
* @ret iobuf I/O buffer, or NULL
|
||||
*/
|
||||
static struct io_buffer * udp_alloc_iob ( struct xfer_interface *xfer,
|
||||
size_t len ) {
|
||||
struct udp_connection *udp =
|
||||
container_of ( xfer, struct udp_connection, xfer );
|
||||
struct io_buffer *iobuf;
|
||||
|
||||
iobuf = alloc_iob ( UDP_MAX_HLEN + len );
|
||||
if ( ! iobuf ) {
|
||||
DBGC ( udp, "UDP %p cannot allocate buffer of length %d\n",
|
||||
udp, len );
|
||||
return NULL;
|
||||
}
|
||||
iob_reserve ( iobuf, UDP_MAX_HLEN );
|
||||
return iobuf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deliver datagram as I/O buffer
|
||||
*
|
||||
* @v xfer Data transfer interface
|
||||
* @v iobuf Datagram I/O buffer
|
||||
* @v meta Data transfer metadata, or NULL
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int udp_xfer_deliver_iob ( struct xfer_interface *xfer,
|
||||
struct io_buffer *iobuf,
|
||||
struct xfer_metadata *meta ) {
|
||||
struct udp_connection *udp =
|
||||
container_of ( xfer, struct udp_connection, xfer );
|
||||
struct sockaddr_tcpip *src;
|
||||
struct sockaddr_tcpip *dest = NULL;
|
||||
unsigned int src_port = 0;
|
||||
|
||||
/* Apply xfer metadata */
|
||||
if ( meta ) {
|
||||
src = ( struct sockaddr_tcpip * ) meta->src;
|
||||
if ( src )
|
||||
src_port = src->st_port;
|
||||
dest = ( struct sockaddr_tcpip * ) meta->dest;
|
||||
}
|
||||
|
||||
/* Transmit data, if possible */
|
||||
udp_tx ( udp, iobuf, src_port, dest );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** UDP data transfer interface operations */
|
||||
static struct xfer_interface_operations udp_xfer_operations = {
|
||||
.close = udp_xfer_close,
|
||||
.vredirect = ignore_xfer_vredirect,
|
||||
.request = ignore_xfer_request,
|
||||
.seek = ignore_xfer_seek,
|
||||
.alloc_iob = udp_alloc_iob,
|
||||
.deliver_iob = udp_xfer_deliver_iob,
|
||||
.deliver_raw = xfer_deliver_as_iob,
|
||||
};
|
||||
|
||||
/***************************************************************************
|
||||
*
|
||||
* Openers
|
||||
*
|
||||
***************************************************************************
|
||||
*/
|
||||
|
||||
/** UDP socket opener */
|
||||
struct socket_opener udp_socket_opener __socket_opener = {
|
||||
.semantics = SOCK_DGRAM,
|
||||
.family = AF_INET,
|
||||
.open = udp_open,
|
||||
};
|
||||
|
||||
/**
|
||||
* Open UDP URI
|
||||
*
|
||||
* @v xfer Data transfer interface
|
||||
* @v uri URI
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int udp_open_uri ( struct xfer_interface *xfer, struct uri *uri ) {
|
||||
struct sockaddr_tcpip peer;
|
||||
|
||||
/* Sanity check */
|
||||
if ( ! uri->host )
|
||||
return -EINVAL;
|
||||
|
||||
memset ( &peer, 0, sizeof ( peer ) );
|
||||
peer.st_port = htons ( uri_port ( uri, 0 ) );
|
||||
return xfer_open_named_socket ( xfer, SOCK_DGRAM,
|
||||
( struct sockaddr * ) &peer,
|
||||
uri->host, NULL );
|
||||
}
|
||||
|
||||
/** UDP URI opener */
|
||||
struct uri_opener udp_uri_opener __uri_opener = {
|
||||
.scheme = "udp",
|
||||
.open = udp_open_uri,
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user