Переглянути джерело

[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>
tags/v1.20.1
Michael Brown 11 роки тому
джерело
коміт
252d28f098
5 змінених файлів з 117 додано та 81 видалено
  1. 24
    6
      src/include/ipxe/in.h
  2. 17
    1
      src/include/ipxe/tcpip.h
  3. 14
    39
      src/net/tcp.c
  4. 44
    0
      src/net/tcpip.c
  5. 18
    35
      src/net/udp.c

+ 24
- 6
src/include/ipxe/in.h Переглянути файл

@@ -59,19 +59,22 @@ struct sockaddr_in {
59 59
 	 * Always set to @c AF_INET for IPv4 addresses
60 60
 	 */
61 61
 	sa_family_t sin_family;
62
+	/** Flags (part of struct @c sockaddr_tcpip) */
63
+	uint16_t sin_flags;
62 64
 	/** TCP/IP port (part of struct @c sockaddr_tcpip) */
63 65
 	uint16_t sin_port;
64 66
 	/** IPv4 address */
65 67
 	struct in_addr sin_addr;
66 68
 	/** Padding
67 69
 	 *
68
-	 * This ensures that a struct @c sockaddr_tcpip is large
69
-	 * enough to hold a socket address for any TCP/IP address
70
-	 * family.
70
+	 * This ensures that a struct @c sockaddr_in is large enough
71
+	 * to hold a socket address for any TCP/IP address family.
71 72
 	 */
72
-	char pad[ sizeof ( struct sockaddr ) - sizeof ( sa_family_t )
73
-					     - sizeof ( uint16_t )
74
-					     - sizeof ( struct in_addr ) ];
73
+	char pad[ sizeof ( struct sockaddr ) -
74
+		  ( sizeof ( sa_family_t ) /* sin_family */ +
75
+		    sizeof ( uint16_t ) /* sin_flags */ +
76
+		    sizeof ( uint16_t ) /* sin_port */ +
77
+		    sizeof ( struct in_addr ) /* sin_addr */ ) ];
75 78
 } __attribute__ (( may_alias ));
76 79
 
77 80
 /**
@@ -83,11 +86,26 @@ struct sockaddr_in6 {
83 86
 	 * Always set to @c AF_INET6 for IPv6 addresses
84 87
 	 */
85 88
 	sa_family_t sin6_family;
89
+	/** Flags (part of struct @c sockaddr_tcpip) */
90
+	uint16_t sin6_flags;
86 91
 	/** TCP/IP port (part of struct @c sockaddr_tcpip) */
87 92
 	uint16_t sin6_port;
88 93
         uint32_t        sin6_flowinfo;  /* Flow number */
89 94
         struct in6_addr sin6_addr;      /* 128-bit destination address */
90 95
         uint32_t        sin6_scope_id;  /* Scope ID */
96
+	/** Padding
97
+	 *
98
+	 * This ensures that a struct @c sockaddr_in6 is large
99
+	 * enough to hold a socket address for any TCP/IP address
100
+	 * family.
101
+	 */
102
+	char pad[ sizeof ( struct sockaddr ) -
103
+		  ( sizeof ( sa_family_t ) /* sin6_family */ +
104
+		    sizeof ( uint16_t ) /* sin6_flags */ +
105
+		    sizeof ( uint16_t ) /* sin6_port */ +
106
+		    sizeof ( uint32_t ) /* sin6_flowinfo */ +
107
+		    sizeof ( struct in6_addr ) /* sin6_addr */ +
108
+		    sizeof ( uint32_t ) /* sin6_scope_id */ ) ];
91 109
 } __attribute__ (( may_alias ));
92 110
 
93 111
 extern int inet_aton ( const char *cp, struct in_addr *inp );

+ 17
- 1
src/include/ipxe/tcpip.h Переглянути файл

@@ -24,6 +24,16 @@ struct net_device;
24 24
  */
25 25
 #define TCPIP_EMPTY_CSUM 0xffff
26 26
 
27
+/** TCP/IP address flags */
28
+enum tcpip_st_flags {
29
+	/** Bind to a privileged port (less than 1024)
30
+	 *
31
+	 * This value is chosen as 1024 to optimise the calculations
32
+	 * in tcpip_bind().
33
+	 */
34
+	TCPIP_BIND_PRIVILEGED = 0x0400,
35
+};
36
+
27 37
 /**
28 38
  * TCP/IP socket address
29 39
  *
@@ -33,6 +43,8 @@ struct net_device;
33 43
 struct sockaddr_tcpip {
34 44
 	/** Socket address family (part of struct @c sockaddr) */
35 45
 	sa_family_t st_family;
46
+	/** Flags */
47
+	uint16_t st_flags;
36 48
 	/** TCP/IP port */
37 49
 	uint16_t st_port;
38 50
 	/** Padding
@@ -42,7 +54,9 @@ struct sockaddr_tcpip {
42 54
 	 * family.
43 55
 	 */
44 56
 	char pad[ sizeof ( struct sockaddr ) -
45
-		  ( sizeof ( sa_family_t ) + sizeof ( uint16_t ) ) ];
57
+		  ( sizeof ( sa_family_t ) /* st_family */ +
58
+		    sizeof ( uint16_t ) /* st_flags */ +
59
+		    sizeof ( uint16_t ) /* st_port */ ) ];
46 60
 } __attribute__ (( may_alias ));
47 61
 
48 62
 /** 
@@ -125,6 +139,8 @@ extern int tcpip_tx ( struct io_buffer *iobuf, struct tcpip_protocol *tcpip,
125 139
 extern uint16_t generic_tcpip_continue_chksum ( uint16_t partial,
126 140
 						const void *data, size_t len );
127 141
 extern uint16_t tcpip_chksum ( const void *data, size_t len );
142
+extern int tcpip_bind ( struct sockaddr_tcpip *st_local,
143
+			int ( * available ) ( int port ) );
128 144
 
129 145
 /* Use generic_tcpip_continue_chksum() if no architecture-specific
130 146
  * version is available

+ 14
- 39
src/net/tcp.c Переглянути файл

@@ -157,6 +157,7 @@ static LIST_HEAD ( tcp_conns );
157 157
 static struct interface_descriptor tcp_xfer_desc;
158 158
 static void tcp_expired ( struct retry_timer *timer, int over );
159 159
 static void tcp_wait_expired ( struct retry_timer *timer, int over );
160
+static struct tcp_connection * tcp_demux ( unsigned int local_port );
160 161
 static int tcp_rx_ack ( struct tcp_connection *tcp, uint32_t ack,
161 162
 			uint32_t win );
162 163
 
@@ -226,46 +227,14 @@ tcp_dump_flags ( struct tcp_connection *tcp, unsigned int flags ) {
226 227
  */
227 228
 
228 229
 /**
229
- * Bind TCP connection to local port
230
+ * Check if local TCP port is available
230 231
  *
231
- * @v tcp		TCP connection
232 232
  * @v port		Local port number
233
- * @ret rc		Return status code
234
- *
235
- * If the port is 0, the connection is assigned an available port
236
- * between 1024 and 65535.
233
+ * @ret port		Local port number, or negative error
237 234
  */
238
-static int tcp_bind ( struct tcp_connection *tcp, unsigned int port ) {
239
-	struct tcp_connection *existing;
240
-	uint16_t try_port;
241
-	unsigned int i;
242
-
243
-	/* If no port is specified, find an available port */
244
-	if ( ! port ) {
245
-		try_port = random();
246
-		for ( i = 0 ; i < 65536 ; i++ ) {
247
-			try_port++;
248
-			if ( try_port < 1024 )
249
-				continue;
250
-			if ( tcp_bind ( tcp, try_port ) == 0 )
251
-				return 0;
252
-		}
253
-		DBGC ( tcp, "TCP %p could not bind: no free ports\n", tcp );
254
-		return -EADDRINUSE;
255
-	}
256
-
257
-	/* Attempt bind to local port */
258
-	list_for_each_entry ( existing, &tcp_conns, list ) {
259
-		if ( existing->local_port == port ) {
260
-			DBGC ( tcp, "TCP %p could not bind: port %d in use\n",
261
-			       tcp, port );
262
-			return -EADDRINUSE;
263
-		}
264
-	}
265
-	tcp->local_port = port;
235
+static int tcp_port_available ( int port ) {
266 236
 
267
-	DBGC ( tcp, "TCP %p bound to port %d\n", tcp, port );
268
-	return 0;
237
+	return ( tcp_demux ( port ) ? -EADDRINUSE : port );
269 238
 }
270 239
 
271 240
 /**
@@ -281,7 +250,7 @@ static int tcp_open ( struct interface *xfer, struct sockaddr *peer,
281 250
 	struct sockaddr_tcpip *st_peer = ( struct sockaddr_tcpip * ) peer;
282 251
 	struct sockaddr_tcpip *st_local = ( struct sockaddr_tcpip * ) local;
283 252
 	struct tcp_connection *tcp;
284
-	unsigned int bind_port;
253
+	int port;
285 254
 	int rc;
286 255
 
287 256
 	/* Allocate and initialise structure */
@@ -303,9 +272,15 @@ static int tcp_open ( struct interface *xfer, struct sockaddr *peer,
303 272
 	memcpy ( &tcp->peer, st_peer, sizeof ( tcp->peer ) );
304 273
 
305 274
 	/* Bind to local port */
306
-	bind_port = ( st_local ? ntohs ( st_local->st_port ) : 0 );
307
-	if ( ( rc = tcp_bind ( tcp, bind_port ) ) != 0 )
275
+	port = tcpip_bind ( st_local, tcp_port_available );
276
+	if ( port < 0 ) {
277
+		rc = port;
278
+		DBGC ( tcp, "TCP %p could not bind: %s\n",
279
+		       tcp, strerror ( rc ) );
308 280
 		goto err;
281
+	}
282
+	tcp->local_port = port;
283
+	DBGC ( tcp, "TCP %p bound to port %d\n", tcp, tcp->local_port );
309 284
 
310 285
 	/* Start timer to initiate SYN */
311 286
 	start_timer_nodelay ( &tcp->timer );

+ 44
- 0
src/net/tcpip.c Переглянути файл

@@ -1,4 +1,5 @@
1 1
 #include <stdint.h>
2
+#include <stdlib.h>
2 3
 #include <string.h>
3 4
 #include <errno.h>
4 5
 #include <byteswap.h>
@@ -133,3 +134,46 @@ uint16_t generic_tcpip_continue_chksum ( uint16_t partial,
133 134
 uint16_t tcpip_chksum ( const void *data, size_t len ) {
134 135
 	return tcpip_continue_chksum ( TCPIP_EMPTY_CSUM, data, len );
135 136
 }
137
+
138
+/**
139
+ * Bind to local TCP/IP port
140
+ *
141
+ * @v st_local		Local TCP/IP socket address, or NULL
142
+ * @v available		Function to check port availability
143
+ * @ret port		Local port number, or negative error
144
+ */
145
+int tcpip_bind ( struct sockaddr_tcpip *st_local,
146
+		 int ( * available ) ( int port ) ) {
147
+	uint16_t flags = 0;
148
+	uint16_t try_port = 0;
149
+	uint16_t min_port;
150
+	uint16_t max_port;
151
+	unsigned int offset;
152
+	unsigned int i;
153
+
154
+	/* Extract parameters from local socket address */
155
+	if ( st_local ) {
156
+		flags = st_local->st_flags;
157
+		try_port = ntohs ( st_local->st_port );
158
+	}
159
+
160
+	/* If an explicit port is specified, check its availability */
161
+	if ( try_port )
162
+		return available ( try_port );
163
+
164
+	/* Otherwise, find an available port in the range [1,1023] or
165
+	 * [1025,65535] as appropriate.
166
+	 */
167
+	min_port = ( ( ( ! flags ) & TCPIP_BIND_PRIVILEGED ) + 1 );
168
+	max_port = ( ( flags & TCPIP_BIND_PRIVILEGED ) - 1 );
169
+	offset = random();
170
+	for ( i = 0 ; i <= max_port ; i++ ) {
171
+		try_port = ( ( i + offset ) & max_port );
172
+		if ( try_port < min_port )
173
+			continue;
174
+		if ( available ( try_port ) < 0 )
175
+			continue;
176
+		return try_port;
177
+	}
178
+	return -EADDRINUSE;
179
+}

+ 18
- 35
src/net/udp.c Переглянути файл

@@ -48,45 +48,19 @@ static struct interface_descriptor udp_xfer_desc;
48 48
 struct tcpip_protocol udp_protocol __tcpip_protocol;
49 49
 
50 50
 /**
51
- * Bind UDP connection to local port
51
+ * Check if local UDP port is available
52 52
  *
53
- * @v udp		UDP connection
54
- * @ret rc		Return status code
55
- *
56
- * Opens the UDP connection and binds to the specified local port.  If
57
- * no local port is specified, the first available port will be used.
53
+ * @v port		Local port number
54
+ * @ret port		Local port number, or negative error
58 55
  */
59
-static int udp_bind ( struct udp_connection *udp ) {
60
-	struct udp_connection *existing;
61
-	static uint16_t try_port = 1023;
62
-
63
-	/* If no port specified, find the first available port */
64
-	if ( ! udp->local.st_port ) {
65
-		while ( try_port ) {
66
-			try_port++;
67
-			if ( try_port < 1024 )
68
-				continue;
69
-			udp->local.st_port = htons ( try_port );
70
-			if ( udp_bind ( udp ) == 0 )
71
-				return 0;
72
-		}
73
-		return -EADDRINUSE;
74
-	}
56
+static int udp_port_available ( int port ) {
57
+	struct udp_connection *udp;
75 58
 
76
-	/* Attempt bind to local port */
77
-	list_for_each_entry ( existing, &udp_conns, list ) {
78
-		if ( existing->local.st_port == udp->local.st_port ) {
79
-			DBGC ( udp, "UDP %p could not bind: port %d in use\n",
80
-			       udp, ntohs ( udp->local.st_port ) );
59
+	list_for_each_entry ( udp, &udp_conns, list ) {
60
+		if ( udp->local.st_port == htons ( port ) )
81 61
 			return -EADDRINUSE;
82
-		}
83 62
 	}
84
-
85
-	/* Add to UDP connection list */
86
-	DBGC ( udp, "UDP %p bound to port %d\n",
87
-	       udp, ntohs ( udp->local.st_port ) );
88
-
89
-	return 0;
63
+	return port;
90 64
 }
91 65
 
92 66
 /**
@@ -104,6 +78,7 @@ static int udp_open_common ( struct interface *xfer,
104 78
 	struct sockaddr_tcpip *st_peer = ( struct sockaddr_tcpip * ) peer;
105 79
 	struct sockaddr_tcpip *st_local = ( struct sockaddr_tcpip * ) local;
106 80
 	struct udp_connection *udp;
81
+	int port;
107 82
 	int rc;
108 83
 
109 84
 	/* Allocate and initialise structure */
@@ -120,8 +95,16 @@ static int udp_open_common ( struct interface *xfer,
120 95
 
121 96
 	/* Bind to local port */
122 97
 	if ( ! promisc ) {
123
-		if ( ( rc = udp_bind ( udp ) ) != 0 )
98
+		port = tcpip_bind ( st_local, udp_port_available );
99
+		if ( port < 0 ) {
100
+			rc = port;
101
+			DBGC ( udp, "UDP %p could not bind: %s\n",
102
+			       udp, strerror ( rc ) );
124 103
 			goto err;
104
+		}
105
+		udp->local.st_port = htons ( port );
106
+		DBGC ( udp, "UDP %p bound to port %d\n",
107
+		       udp, ntohs ( udp->local.st_port ) );
125 108
 	}
126 109
 
127 110
 	/* Attach parent interface, transfer reference to connection

Завантаження…
Відмінити
Зберегти