Skip to content

Commit

Permalink
[tcpip] Allow binding to unspecified privileged ports (below 1024)
Browse files Browse the repository at this point in the history
Originally-implemented-by: Marin Hannache <git@mareo.fr>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
  • Loading branch information
mcb30 committed Aug 6, 2013
1 parent 0350682 commit 252d28f
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 81 deletions.
30 changes: 24 additions & 6 deletions src/include/ipxe/in.h
Expand Up @@ -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 ));

/**
Expand All @@ -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 );
Expand Down
18 changes: 17 additions & 1 deletion src/include/ipxe/tcpip.h
Expand Up @@ -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
*
Expand All @@ -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
Expand All @@ -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 ));

/**
Expand Down Expand Up @@ -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
Expand Down
53 changes: 14 additions & 39 deletions src/net/tcp.c
Expand Up @@ -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 );

Expand Down Expand Up @@ -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 );
}

/**
Expand All @@ -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 */
Expand All @@ -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 );
Expand Down
44 changes: 44 additions & 0 deletions src/net/tcpip.c
@@ -1,4 +1,5 @@
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <byteswap.h>
Expand Down Expand Up @@ -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;
}
53 changes: 18 additions & 35 deletions src/net/udp.c
Expand Up @@ -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;
}

/**
Expand All @@ -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 */
Expand All @@ -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
Expand Down

0 comments on commit 252d28f

Please sign in to comment.