aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2017-06-05 09:30:49 -0400
committerDavid Howells <dhowells@redhat.com>2017-06-05 09:30:49 -0400
commit28036f44851e2515aa91b547b45cefddcac52ff6 (patch)
treea129c98f3113b73140e4c81a159e2d9cc076a63f
parent68d6d1ae5c0429bcc8911e1db5f80fe2cd1ca974 (diff)
rxrpc: Permit multiple service binding
Permit bind() to be called on an AF_RXRPC socket more than once (currently maximum twice) to bind multiple listening services to it. There are some restrictions: (1) All bind() calls involved must have a non-zero service ID. (2) The service IDs must all be different. (3) The rest of the address (notably the transport part) must be the same in all (a single UDP socket is shared). (4) This must be done before listen() or sendmsg() is called. This allows someone to connect to the service socket with different service IDs and lays the foundation for service upgrading. The service ID used by an incoming call can be extracted from the msg_name returned by recvmsg(). Signed-off-by: David Howells <dhowells@redhat.com>
-rw-r--r--Documentation/networking/rxrpc.txt4
-rw-r--r--net/rxrpc/af_rxrpc.c62
-rw-r--r--net/rxrpc/ar-internal.h2
-rw-r--r--net/rxrpc/call_accept.c3
-rw-r--r--net/rxrpc/local_object.c1
-rw-r--r--net/rxrpc/security.c3
6 files changed, 51 insertions, 24 deletions
diff --git a/Documentation/networking/rxrpc.txt b/Documentation/networking/rxrpc.txt
index 1b63bbc6b94f..b7115ec55e04 100644
--- a/Documentation/networking/rxrpc.txt
+++ b/Documentation/networking/rxrpc.txt
@@ -600,6 +600,10 @@ A server would be set up to accept operations in the following manner:
600 }; 600 };
601 bind(server, &srx, sizeof(srx)); 601 bind(server, &srx, sizeof(srx));
602 602
603 More than one service ID may be bound to a socket, provided the transport
604 parameters are the same. The limit is currently two. To do this, bind()
605 should be called twice.
606
603 (3) The server is then set to listen out for incoming calls: 607 (3) The server is then set to listen out for incoming calls:
604 608
605 listen(server, 100); 609 listen(server, 100);
diff --git a/net/rxrpc/af_rxrpc.c b/net/rxrpc/af_rxrpc.c
index 1e4ac889ec00..3b982bca7d22 100644
--- a/net/rxrpc/af_rxrpc.c
+++ b/net/rxrpc/af_rxrpc.c
@@ -144,31 +144,48 @@ static int rxrpc_bind(struct socket *sock, struct sockaddr *saddr, int len)
144 144
145 lock_sock(&rx->sk); 145 lock_sock(&rx->sk);
146 146
147 if (rx->sk.sk_state != RXRPC_UNBOUND) { 147 switch (rx->sk.sk_state) {
148 ret = -EINVAL; 148 case RXRPC_UNBOUND:
149 goto error_unlock; 149 rx->srx = *srx;
150 } 150 local = rxrpc_lookup_local(sock_net(&rx->sk), &rx->srx);
151 151 if (IS_ERR(local)) {
152 memcpy(&rx->srx, srx, sizeof(rx->srx)); 152 ret = PTR_ERR(local);
153 goto error_unlock;
154 }
153 155
154 local = rxrpc_lookup_local(sock_net(&rx->sk), &rx->srx); 156 if (service_id) {
155 if (IS_ERR(local)) { 157 write_lock(&local->services_lock);
156 ret = PTR_ERR(local); 158 if (rcu_access_pointer(local->service))
157 goto error_unlock; 159 goto service_in_use;
158 } 160 rx->local = local;
161 rcu_assign_pointer(local->service, rx);
162 write_unlock(&local->services_lock);
163
164 rx->sk.sk_state = RXRPC_SERVER_BOUND;
165 } else {
166 rx->local = local;
167 rx->sk.sk_state = RXRPC_CLIENT_BOUND;
168 }
169 break;
159 170
160 if (service_id) { 171 case RXRPC_SERVER_BOUND:
161 write_lock(&local->services_lock); 172 ret = -EINVAL;
162 if (rcu_access_pointer(local->service)) 173 if (service_id == 0)
163 goto service_in_use; 174 goto error_unlock;
164 rx->local = local; 175 ret = -EADDRINUSE;
165 rcu_assign_pointer(local->service, rx); 176 if (service_id == rx->srx.srx_service)
166 write_unlock(&local->services_lock); 177 goto error_unlock;
178 ret = -EINVAL;
179 srx->srx_service = rx->srx.srx_service;
180 if (memcmp(srx, &rx->srx, sizeof(*srx)) != 0)
181 goto error_unlock;
182 rx->second_service = service_id;
183 rx->sk.sk_state = RXRPC_SERVER_BOUND2;
184 break;
167 185
168 rx->sk.sk_state = RXRPC_SERVER_BOUND; 186 default:
169 } else { 187 ret = -EINVAL;
170 rx->local = local; 188 goto error_unlock;
171 rx->sk.sk_state = RXRPC_CLIENT_BOUND;
172 } 189 }
173 190
174 release_sock(&rx->sk); 191 release_sock(&rx->sk);
@@ -205,6 +222,7 @@ static int rxrpc_listen(struct socket *sock, int backlog)
205 ret = -EADDRNOTAVAIL; 222 ret = -EADDRNOTAVAIL;
206 break; 223 break;
207 case RXRPC_SERVER_BOUND: 224 case RXRPC_SERVER_BOUND:
225 case RXRPC_SERVER_BOUND2:
208 ASSERT(rx->local != NULL); 226 ASSERT(rx->local != NULL);
209 max = READ_ONCE(rxrpc_max_backlog); 227 max = READ_ONCE(rxrpc_max_backlog);
210 ret = -EINVAL; 228 ret = -EINVAL;
diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h
index de98a49adb35..781fbc253b5a 100644
--- a/net/rxrpc/ar-internal.h
+++ b/net/rxrpc/ar-internal.h
@@ -61,6 +61,7 @@ enum {
61 RXRPC_CLIENT_UNBOUND, /* Unbound socket used as client */ 61 RXRPC_CLIENT_UNBOUND, /* Unbound socket used as client */
62 RXRPC_CLIENT_BOUND, /* client local address bound */ 62 RXRPC_CLIENT_BOUND, /* client local address bound */
63 RXRPC_SERVER_BOUND, /* server local address bound */ 63 RXRPC_SERVER_BOUND, /* server local address bound */
64 RXRPC_SERVER_BOUND2, /* second server local address bound */
64 RXRPC_SERVER_LISTENING, /* server listening for connections */ 65 RXRPC_SERVER_LISTENING, /* server listening for connections */
65 RXRPC_SERVER_LISTEN_DISABLED, /* server listening disabled */ 66 RXRPC_SERVER_LISTEN_DISABLED, /* server listening disabled */
66 RXRPC_CLOSE, /* socket is being closed */ 67 RXRPC_CLOSE, /* socket is being closed */
@@ -142,6 +143,7 @@ struct rxrpc_sock {
142 u32 min_sec_level; /* minimum security level */ 143 u32 min_sec_level; /* minimum security level */
143#define RXRPC_SECURITY_MAX RXRPC_SECURITY_ENCRYPT 144#define RXRPC_SECURITY_MAX RXRPC_SECURITY_ENCRYPT
144 bool exclusive; /* Exclusive connection for a client socket */ 145 bool exclusive; /* Exclusive connection for a client socket */
146 u16 second_service; /* Additional service bound to the endpoint */
145 sa_family_t family; /* Protocol family created with */ 147 sa_family_t family; /* Protocol family created with */
146 struct sockaddr_rxrpc srx; /* local address */ 148 struct sockaddr_rxrpc srx; /* local address */
147 struct sockaddr_rxrpc connect_srx; /* Default client address from connect() */ 149 struct sockaddr_rxrpc connect_srx; /* Default client address from connect() */
diff --git a/net/rxrpc/call_accept.c b/net/rxrpc/call_accept.c
index a8515b0d4717..544df53ccf79 100644
--- a/net/rxrpc/call_accept.c
+++ b/net/rxrpc/call_accept.c
@@ -341,7 +341,8 @@ struct rxrpc_call *rxrpc_new_incoming_call(struct rxrpc_local *local,
341 341
342 /* Get the socket providing the service */ 342 /* Get the socket providing the service */
343 rx = rcu_dereference(local->service); 343 rx = rcu_dereference(local->service);
344 if (rx && service_id == rx->srx.srx_service) 344 if (rx && (service_id == rx->srx.srx_service ||
345 service_id == rx->second_service))
345 goto found_service; 346 goto found_service;
346 347
347 trace_rxrpc_abort("INV", sp->hdr.cid, sp->hdr.callNumber, sp->hdr.seq, 348 trace_rxrpc_abort("INV", sp->hdr.cid, sp->hdr.callNumber, sp->hdr.seq,
diff --git a/net/rxrpc/local_object.c b/net/rxrpc/local_object.c
index 17d79fd73ade..38b99db30e54 100644
--- a/net/rxrpc/local_object.c
+++ b/net/rxrpc/local_object.c
@@ -94,6 +94,7 @@ static struct rxrpc_local *rxrpc_alloc_local(struct rxrpc_net *rxnet,
94 rwlock_init(&local->services_lock); 94 rwlock_init(&local->services_lock);
95 local->debug_id = atomic_inc_return(&rxrpc_debug_id); 95 local->debug_id = atomic_inc_return(&rxrpc_debug_id);
96 memcpy(&local->srx, srx, sizeof(*srx)); 96 memcpy(&local->srx, srx, sizeof(*srx));
97 local->srx.srx_service = 0;
97 } 98 }
98 99
99 _leave(" = %p", local); 100 _leave(" = %p", local);
diff --git a/net/rxrpc/security.c b/net/rxrpc/security.c
index b9f5dbbe0b8b..e9f428351293 100644
--- a/net/rxrpc/security.c
+++ b/net/rxrpc/security.c
@@ -133,7 +133,8 @@ int rxrpc_init_server_conn_security(struct rxrpc_connection *conn)
133 read_lock(&local->services_lock); 133 read_lock(&local->services_lock);
134 rx = rcu_dereference_protected(local->service, 134 rx = rcu_dereference_protected(local->service,
135 lockdep_is_held(&local->services_lock)); 135 lockdep_is_held(&local->services_lock));
136 if (rx && rx->srx.srx_service == conn->service_id) 136 if (rx && (rx->srx.srx_service == conn->service_id ||
137 rx->second_service == conn->service_id))
137 goto found_service; 138 goto found_service;
138 139
139 /* the service appears to have died */ 140 /* the service appears to have died */