summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMichael Brown2013-08-06 16:56:54 +0200
committerMichael Brown2013-08-06 16:56:54 +0200
commit252d28f098bfd12df59fe147d7e8354be61a6da8 (patch)
tree234bc573f3c7a27730d04016c809dc6ae5ba8c24 /src
parent[ipv6] Rename sin_{family,port} to sin6_{family,port} in struct sockaddr_in6 (diff)
downloadipxe-252d28f098bfd12df59fe147d7e8354be61a6da8.tar.gz
ipxe-252d28f098bfd12df59fe147d7e8354be61a6da8.tar.xz
ipxe-252d28f098bfd12df59fe147d7e8354be61a6da8.zip
[tcpip] Allow binding to unspecified privileged ports (below 1024)
Originally-implemented-by: Marin Hannache <git@mareo.fr> Signed-off-by: Michael Brown <mcb30@ipxe.org>
Diffstat (limited to 'src')
-rw-r--r--src/include/ipxe/in.h30
-rw-r--r--src/include/ipxe/tcpip.h18
-rw-r--r--src/net/tcp.c53
-rw-r--r--src/net/tcpip.c44
-rw-r--r--src/net/udp.c53
5 files changed, 117 insertions, 81 deletions
diff --git a/src/include/ipxe/in.h b/src/include/ipxe/in.h
index 1208ae38..eee9159f 100644
--- a/src/include/ipxe/in.h
+++ b/src/include/ipxe/in.h
@@ -59,19 +59,22 @@ struct sockaddr_in {
* 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 ));
/**
@@ -83,11 +86,26 @@ struct sockaddr_in6 {
* 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 );
diff --git a/src/include/ipxe/tcpip.h b/src/include/ipxe/tcpip.h
index 34d7f88f..0cc688a9 100644
--- a/src/include/ipxe/tcpip.h
+++ b/src/include/ipxe/tcpip.h
@@ -24,6 +24,16 @@ struct net_device;
*/
#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
*
@@ -33,6 +43,8 @@ struct net_device;
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
@@ -42,7 +54,9 @@ struct sockaddr_tcpip {
* 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 ));
/**
@@ -125,6 +139,8 @@ extern int tcpip_tx ( struct io_buffer *iobuf, struct tcpip_protocol *tcpip,
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
diff --git a/src/net/tcp.c b/src/net/tcp.c
index b97107fc..0e18c831 100644
--- a/src/net/tcp.c
+++ b/src/net/tcp.c
@@ -157,6 +157,7 @@ static LIST_HEAD ( tcp_conns );
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 );
@@ -226,46 +227,14 @@ tcp_dump_flags ( struct tcp_connection *tcp, unsigned int flags ) {
*/
/**
- * 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 );
}
/**
@@ -281,7 +250,7 @@ static int tcp_open ( struct interface *xfer, struct sockaddr *peer,
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 */
@@ -303,9 +272,15 @@ static int tcp_open ( struct interface *xfer, struct sockaddr *peer,
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 );
diff --git a/src/net/tcpip.c b/src/net/tcpip.c
index 8e187f7e..721a4e48 100644
--- a/src/net/tcpip.c
+++ b/src/net/tcpip.c
@@ -1,4 +1,5 @@
#include <stdint.h>
+#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <byteswap.h>
@@ -133,3 +134,46 @@ uint16_t generic_tcpip_continue_chksum ( uint16_t partial,
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;
+}
diff --git a/src/net/udp.c b/src/net/udp.c
index bae5f4a7..edc7488a 100644
--- a/src/net/udp.c
+++ b/src/net/udp.c
@@ -48,45 +48,19 @@ static struct interface_descriptor udp_xfer_desc;
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;
}
/**
@@ -104,6 +78,7 @@ static int udp_open_common ( struct interface *xfer,
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 */
@@ -120,8 +95,16 @@ static int udp_open_common ( struct interface *xfer,
/* 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