summaryrefslogtreecommitdiffstats
path: root/src/net/tcp.c
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/tcp.c
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/tcp.c')
-rw-r--r--src/net/tcp.c53
1 files changed, 14 insertions, 39 deletions
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 );