mirror of
https://github.com/xcat2/xNBA.git
synced 2024-11-28 12:19:46 +00:00
[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>
This commit is contained in:
parent
0350682865
commit
252d28f098
@ -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 );
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
static int tcp_port_available ( int port ) {
|
||||
|
||||
/* 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;
|
||||
|
||||
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 );
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
static int udp_port_available ( int port ) {
|
||||
struct udp_connection *udp;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* 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
|
||||
|
Loading…
Reference in New Issue
Block a user