Преглед изворни кода

[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 пре 12 година
родитељ
комит
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
 	 * Always set to @c AF_INET for IPv4 addresses
59
 	 * Always set to @c AF_INET for IPv4 addresses
60
 	 */
60
 	 */
61
 	sa_family_t sin_family;
61
 	sa_family_t sin_family;
62
+	/** Flags (part of struct @c sockaddr_tcpip) */
63
+	uint16_t sin_flags;
62
 	/** TCP/IP port (part of struct @c sockaddr_tcpip) */
64
 	/** TCP/IP port (part of struct @c sockaddr_tcpip) */
63
 	uint16_t sin_port;
65
 	uint16_t sin_port;
64
 	/** IPv4 address */
66
 	/** IPv4 address */
65
 	struct in_addr sin_addr;
67
 	struct in_addr sin_addr;
66
 	/** Padding
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
 } __attribute__ (( may_alias ));
78
 } __attribute__ (( may_alias ));
76
 
79
 
77
 /**
80
 /**
83
 	 * Always set to @c AF_INET6 for IPv6 addresses
86
 	 * Always set to @c AF_INET6 for IPv6 addresses
84
 	 */
87
 	 */
85
 	sa_family_t sin6_family;
88
 	sa_family_t sin6_family;
89
+	/** Flags (part of struct @c sockaddr_tcpip) */
90
+	uint16_t sin6_flags;
86
 	/** TCP/IP port (part of struct @c sockaddr_tcpip) */
91
 	/** TCP/IP port (part of struct @c sockaddr_tcpip) */
87
 	uint16_t sin6_port;
92
 	uint16_t sin6_port;
88
         uint32_t        sin6_flowinfo;  /* Flow number */
93
         uint32_t        sin6_flowinfo;  /* Flow number */
89
         struct in6_addr sin6_addr;      /* 128-bit destination address */
94
         struct in6_addr sin6_addr;      /* 128-bit destination address */
90
         uint32_t        sin6_scope_id;  /* Scope ID */
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
 } __attribute__ (( may_alias ));
109
 } __attribute__ (( may_alias ));
92
 
110
 
93
 extern int inet_aton ( const char *cp, struct in_addr *inp );
111
 extern int inet_aton ( const char *cp, struct in_addr *inp );

+ 17
- 1
src/include/ipxe/tcpip.h Прегледај датотеку

24
  */
24
  */
25
 #define TCPIP_EMPTY_CSUM 0xffff
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
  * TCP/IP socket address
38
  * TCP/IP socket address
29
  *
39
  *
33
 struct sockaddr_tcpip {
43
 struct sockaddr_tcpip {
34
 	/** Socket address family (part of struct @c sockaddr) */
44
 	/** Socket address family (part of struct @c sockaddr) */
35
 	sa_family_t st_family;
45
 	sa_family_t st_family;
46
+	/** Flags */
47
+	uint16_t st_flags;
36
 	/** TCP/IP port */
48
 	/** TCP/IP port */
37
 	uint16_t st_port;
49
 	uint16_t st_port;
38
 	/** Padding
50
 	/** Padding
42
 	 * family.
54
 	 * family.
43
 	 */
55
 	 */
44
 	char pad[ sizeof ( struct sockaddr ) -
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
 } __attribute__ (( may_alias ));
60
 } __attribute__ (( may_alias ));
47
 
61
 
48
 /** 
62
 /** 
125
 extern uint16_t generic_tcpip_continue_chksum ( uint16_t partial,
139
 extern uint16_t generic_tcpip_continue_chksum ( uint16_t partial,
126
 						const void *data, size_t len );
140
 						const void *data, size_t len );
127
 extern uint16_t tcpip_chksum ( const void *data, size_t len );
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
 /* Use generic_tcpip_continue_chksum() if no architecture-specific
145
 /* Use generic_tcpip_continue_chksum() if no architecture-specific
130
  * version is available
146
  * version is available

+ 14
- 39
src/net/tcp.c Прегледај датотеку

157
 static struct interface_descriptor tcp_xfer_desc;
157
 static struct interface_descriptor tcp_xfer_desc;
158
 static void tcp_expired ( struct retry_timer *timer, int over );
158
 static void tcp_expired ( struct retry_timer *timer, int over );
159
 static void tcp_wait_expired ( struct retry_timer *timer, int over );
159
 static void tcp_wait_expired ( struct retry_timer *timer, int over );
160
+static struct tcp_connection * tcp_demux ( unsigned int local_port );
160
 static int tcp_rx_ack ( struct tcp_connection *tcp, uint32_t ack,
161
 static int tcp_rx_ack ( struct tcp_connection *tcp, uint32_t ack,
161
 			uint32_t win );
162
 			uint32_t win );
162
 
163
 
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
  * @v port		Local port number
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
 	struct sockaddr_tcpip *st_peer = ( struct sockaddr_tcpip * ) peer;
250
 	struct sockaddr_tcpip *st_peer = ( struct sockaddr_tcpip * ) peer;
282
 	struct sockaddr_tcpip *st_local = ( struct sockaddr_tcpip * ) local;
251
 	struct sockaddr_tcpip *st_local = ( struct sockaddr_tcpip * ) local;
283
 	struct tcp_connection *tcp;
252
 	struct tcp_connection *tcp;
284
-	unsigned int bind_port;
253
+	int port;
285
 	int rc;
254
 	int rc;
286
 
255
 
287
 	/* Allocate and initialise structure */
256
 	/* Allocate and initialise structure */
303
 	memcpy ( &tcp->peer, st_peer, sizeof ( tcp->peer ) );
272
 	memcpy ( &tcp->peer, st_peer, sizeof ( tcp->peer ) );
304
 
273
 
305
 	/* Bind to local port */
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
 		goto err;
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
 	/* Start timer to initiate SYN */
285
 	/* Start timer to initiate SYN */
311
 	start_timer_nodelay ( &tcp->timer );
286
 	start_timer_nodelay ( &tcp->timer );

+ 44
- 0
src/net/tcpip.c Прегледај датотеку

1
 #include <stdint.h>
1
 #include <stdint.h>
2
+#include <stdlib.h>
2
 #include <string.h>
3
 #include <string.h>
3
 #include <errno.h>
4
 #include <errno.h>
4
 #include <byteswap.h>
5
 #include <byteswap.h>
133
 uint16_t tcpip_chksum ( const void *data, size_t len ) {
134
 uint16_t tcpip_chksum ( const void *data, size_t len ) {
134
 	return tcpip_continue_chksum ( TCPIP_EMPTY_CSUM, data, len );
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
 struct tcpip_protocol udp_protocol __tcpip_protocol;
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
 			return -EADDRINUSE;
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
 	struct sockaddr_tcpip *st_peer = ( struct sockaddr_tcpip * ) peer;
78
 	struct sockaddr_tcpip *st_peer = ( struct sockaddr_tcpip * ) peer;
105
 	struct sockaddr_tcpip *st_local = ( struct sockaddr_tcpip * ) local;
79
 	struct sockaddr_tcpip *st_local = ( struct sockaddr_tcpip * ) local;
106
 	struct udp_connection *udp;
80
 	struct udp_connection *udp;
81
+	int port;
107
 	int rc;
82
 	int rc;
108
 
83
 
109
 	/* Allocate and initialise structure */
84
 	/* Allocate and initialise structure */
120
 
95
 
121
 	/* Bind to local port */
96
 	/* Bind to local port */
122
 	if ( ! promisc ) {
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
 			goto err;
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
 	/* Attach parent interface, transfer reference to connection
110
 	/* Attach parent interface, transfer reference to connection

Loading…
Откажи
Сачувај