summaryrefslogtreecommitdiffstats
path: root/src/net
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/net
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/net')
-rw-r--r--src/net/tcp.c53
-rw-r--r--src/net/tcpip.c44
-rw-r--r--src/net/udp.c53
3 files changed, 76 insertions, 74 deletions
diff --git a/src/net/tcp.c b/src/net/tcp.c
index b97107fc8..0e18c831d 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 8e187f7ec..721a4e483 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 bae5f4a73..edc7488a3 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