mirror of
https://github.com/ipxe/ipxe
synced 2025-12-22 21:11:03 +03: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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user