* Always set to @c AF_INET for IPv4 addresses
*/
sa_family_t sin_family;
+ /** Flags (part of struct @c sockaddr_tcpip) */
+ uint16_t sin_flags;
/** TCP/IP port (part of struct @c sockaddr_tcpip) */
uint16_t sin_port;
/** IPv4 address */
struct in_addr sin_addr;
/** Padding
*
- * This ensures that a struct @c sockaddr_tcpip is large
- * enough to hold a socket address for any TCP/IP address
- * family.
+ * This ensures that a struct @c sockaddr_in is large enough
+ * to hold a socket address for any TCP/IP address family.
*/
- char pad[ sizeof ( struct sockaddr ) - sizeof ( sa_family_t )
- - sizeof ( uint16_t )
- - sizeof ( struct in_addr ) ];
+ char pad[ sizeof ( struct sockaddr ) -
+ ( sizeof ( sa_family_t ) /* sin_family */ +
+ sizeof ( uint16_t ) /* sin_flags */ +
+ sizeof ( uint16_t ) /* sin_port */ +
+ sizeof ( struct in_addr ) /* sin_addr */ ) ];
} __attribute__ (( may_alias ));
/**
* Always set to @c AF_INET6 for IPv6 addresses
*/
sa_family_t sin6_family;
+ /** Flags (part of struct @c sockaddr_tcpip) */
+ uint16_t sin6_flags;
/** TCP/IP port (part of struct @c sockaddr_tcpip) */
uint16_t sin6_port;
uint32_t sin6_flowinfo; /* Flow number */
struct in6_addr sin6_addr; /* 128-bit destination address */
uint32_t sin6_scope_id; /* Scope ID */
+ /** Padding
+ *
+ * This ensures that a struct @c sockaddr_in6 is large
+ * enough to hold a socket address for any TCP/IP address
+ * family.
+ */
+ char pad[ sizeof ( struct sockaddr ) -
+ ( sizeof ( sa_family_t ) /* sin6_family */ +
+ sizeof ( uint16_t ) /* sin6_flags */ +
+ sizeof ( uint16_t ) /* sin6_port */ +
+ sizeof ( uint32_t ) /* sin6_flowinfo */ +
+ sizeof ( struct in6_addr ) /* sin6_addr */ +
+ sizeof ( uint32_t ) /* sin6_scope_id */ ) ];
} __attribute__ (( may_alias ));
extern int inet_aton ( const char *cp, struct in_addr *inp );
*/
#define TCPIP_EMPTY_CSUM 0xffff
+/** TCP/IP address flags */
+enum tcpip_st_flags {
+ /** Bind to a privileged port (less than 1024)
+ *
+ * This value is chosen as 1024 to optimise the calculations
+ * in tcpip_bind().
+ */
+ TCPIP_BIND_PRIVILEGED = 0x0400,
+};
+
/**
* TCP/IP socket address
*
struct sockaddr_tcpip {
/** Socket address family (part of struct @c sockaddr) */
sa_family_t st_family;
+ /** Flags */
+ uint16_t st_flags;
/** TCP/IP port */
uint16_t st_port;
/** Padding
* family.
*/
char pad[ sizeof ( struct sockaddr ) -
- ( sizeof ( sa_family_t ) + sizeof ( uint16_t ) ) ];
+ ( sizeof ( sa_family_t ) /* st_family */ +
+ sizeof ( uint16_t ) /* st_flags */ +
+ sizeof ( uint16_t ) /* st_port */ ) ];
} __attribute__ (( may_alias ));
/**
extern uint16_t generic_tcpip_continue_chksum ( uint16_t partial,
const void *data, size_t len );
extern uint16_t tcpip_chksum ( const void *data, size_t len );
+extern int tcpip_bind ( struct sockaddr_tcpip *st_local,
+ int ( * available ) ( int port ) );
/* Use generic_tcpip_continue_chksum() if no architecture-specific
* version is available
static struct interface_descriptor tcp_xfer_desc;
static void tcp_expired ( struct retry_timer *timer, int over );
static void tcp_wait_expired ( struct retry_timer *timer, int over );
+static struct tcp_connection * tcp_demux ( unsigned int local_port );
static int tcp_rx_ack ( struct tcp_connection *tcp, uint32_t ack,
uint32_t win );
*/
/**
- * Bind TCP connection to local port
+ * Check if local TCP port is available
*
- * @v tcp TCP connection
* @v port Local port number
- * @ret rc Return status code
- *
- * If the port is 0, the connection is assigned an available port
- * between 1024 and 65535.
+ * @ret port Local port number, or negative error
*/
-static int tcp_bind ( struct tcp_connection *tcp, unsigned int port ) {
- struct tcp_connection *existing;
- uint16_t try_port;
- unsigned int i;
-
- /* If no port is specified, find an available port */
- if ( ! port ) {
- try_port = random();
- for ( i = 0 ; i < 65536 ; i++ ) {
- try_port++;
- if ( try_port < 1024 )
- continue;
- if ( tcp_bind ( tcp, try_port ) == 0 )
- return 0;
- }
- DBGC ( tcp, "TCP %p could not bind: no free ports\n", tcp );
- return -EADDRINUSE;
- }
-
- /* Attempt bind to local port */
- list_for_each_entry ( existing, &tcp_conns, list ) {
- if ( existing->local_port == port ) {
- DBGC ( tcp, "TCP %p could not bind: port %d in use\n",
- tcp, port );
- return -EADDRINUSE;
- }
- }
- tcp->local_port = port;
+static int tcp_port_available ( int port ) {
- DBGC ( tcp, "TCP %p bound to port %d\n", tcp, port );
- return 0;
+ return ( tcp_demux ( port ) ? -EADDRINUSE : port );
}
/**
struct sockaddr_tcpip *st_peer = ( struct sockaddr_tcpip * ) peer;
struct sockaddr_tcpip *st_local = ( struct sockaddr_tcpip * ) local;
struct tcp_connection *tcp;
- unsigned int bind_port;
+ int port;
int rc;
/* Allocate and initialise structure */
memcpy ( &tcp->peer, st_peer, sizeof ( tcp->peer ) );
/* Bind to local port */
- bind_port = ( st_local ? ntohs ( st_local->st_port ) : 0 );
- if ( ( rc = tcp_bind ( tcp, bind_port ) ) != 0 )
+ port = tcpip_bind ( st_local, tcp_port_available );
+ if ( port < 0 ) {
+ rc = port;
+ DBGC ( tcp, "TCP %p could not bind: %s\n",
+ tcp, strerror ( rc ) );
goto err;
+ }
+ tcp->local_port = port;
+ DBGC ( tcp, "TCP %p bound to port %d\n", tcp, tcp->local_port );
/* Start timer to initiate SYN */
start_timer_nodelay ( &tcp->timer );
#include <stdint.h>
+#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <byteswap.h>
uint16_t tcpip_chksum ( const void *data, size_t len ) {
return tcpip_continue_chksum ( TCPIP_EMPTY_CSUM, data, len );
}
+
+/**
+ * Bind to local TCP/IP port
+ *
+ * @v st_local Local TCP/IP socket address, or NULL
+ * @v available Function to check port availability
+ * @ret port Local port number, or negative error
+ */
+int tcpip_bind ( struct sockaddr_tcpip *st_local,
+ int ( * available ) ( int port ) ) {
+ uint16_t flags = 0;
+ uint16_t try_port = 0;
+ uint16_t min_port;
+ uint16_t max_port;
+ unsigned int offset;
+ unsigned int i;
+
+ /* Extract parameters from local socket address */
+ if ( st_local ) {
+ flags = st_local->st_flags;
+ try_port = ntohs ( st_local->st_port );
+ }
+
+ /* If an explicit port is specified, check its availability */
+ if ( try_port )
+ return available ( try_port );
+
+ /* Otherwise, find an available port in the range [1,1023] or
+ * [1025,65535] as appropriate.
+ */
+ min_port = ( ( ( ! flags ) & TCPIP_BIND_PRIVILEGED ) + 1 );
+ max_port = ( ( flags & TCPIP_BIND_PRIVILEGED ) - 1 );
+ offset = random();
+ for ( i = 0 ; i <= max_port ; i++ ) {
+ try_port = ( ( i + offset ) & max_port );
+ if ( try_port < min_port )
+ continue;
+ if ( available ( try_port ) < 0 )
+ continue;
+ return try_port;
+ }
+ return -EADDRINUSE;
+}
struct tcpip_protocol udp_protocol __tcpip_protocol;
/**
- * Bind UDP connection to local port
+ * Check if local UDP port is available
*
- * @v udp UDP connection
- * @ret rc Return status code
- *
- * Opens the UDP connection and binds to the specified local port. If
- * no local port is specified, the first available port will be used.
+ * @v port Local port number
+ * @ret port Local port number, or negative error
*/
-static int udp_bind ( struct udp_connection *udp ) {
- struct udp_connection *existing;
- static uint16_t try_port = 1023;
-
- /* If no port specified, find the first available port */
- if ( ! udp->local.st_port ) {
- while ( try_port ) {
- try_port++;
- if ( try_port < 1024 )
- continue;
- udp->local.st_port = htons ( try_port );
- if ( udp_bind ( udp ) == 0 )
- return 0;
- }
- return -EADDRINUSE;
- }
+static int udp_port_available ( int port ) {
+ struct udp_connection *udp;
- /* Attempt bind to local port */
- list_for_each_entry ( existing, &udp_conns, list ) {
- if ( existing->local.st_port == udp->local.st_port ) {
- DBGC ( udp, "UDP %p could not bind: port %d in use\n",
- udp, ntohs ( udp->local.st_port ) );
+ list_for_each_entry ( udp, &udp_conns, list ) {
+ if ( udp->local.st_port == htons ( port ) )
return -EADDRINUSE;
- }
}
-
- /* Add to UDP connection list */
- DBGC ( udp, "UDP %p bound to port %d\n",
- udp, ntohs ( udp->local.st_port ) );
-
- return 0;
+ return port;
}
/**
struct sockaddr_tcpip *st_peer = ( struct sockaddr_tcpip * ) peer;
struct sockaddr_tcpip *st_local = ( struct sockaddr_tcpip * ) local;
struct udp_connection *udp;
+ int port;
int rc;
/* Allocate and initialise structure */
/* Bind to local port */
if ( ! promisc ) {
- if ( ( rc = udp_bind ( udp ) ) != 0 )
+ port = tcpip_bind ( st_local, udp_port_available );
+ if ( port < 0 ) {
+ rc = port;
+ DBGC ( udp, "UDP %p could not bind: %s\n",
+ udp, strerror ( rc ) );
goto err;
+ }
+ udp->local.st_port = htons ( port );
+ DBGC ( udp, "UDP %p bound to port %d\n",
+ udp, ntohs ( udp->local.st_port ) );
}
/* Attach parent interface, transfer reference to connection