diff options
Diffstat (limited to 'net/rxrpc')
-rw-r--r-- | net/rxrpc/Makefile | 1 | ||||
-rw-r--r-- | net/rxrpc/af_rxrpc.c | 20 | ||||
-rw-r--r-- | net/rxrpc/ar-internal.h | 156 | ||||
-rw-r--r-- | net/rxrpc/call_accept.c | 38 | ||||
-rw-r--r-- | net/rxrpc/call_event.c | 14 | ||||
-rw-r--r-- | net/rxrpc/call_object.c | 273 | ||||
-rw-r--r-- | net/rxrpc/conn_client.c | 282 | ||||
-rw-r--r-- | net/rxrpc/conn_event.c | 44 | ||||
-rw-r--r-- | net/rxrpc/conn_object.c | 636 | ||||
-rw-r--r-- | net/rxrpc/conn_service.c | 230 | ||||
-rw-r--r-- | net/rxrpc/input.c | 71 | ||||
-rw-r--r-- | net/rxrpc/insecure.c | 7 | ||||
-rw-r--r-- | net/rxrpc/local_object.c | 19 | ||||
-rw-r--r-- | net/rxrpc/peer_object.c | 2 | ||||
-rw-r--r-- | net/rxrpc/proc.c | 23 | ||||
-rw-r--r-- | net/rxrpc/rxkad.c | 191 | ||||
-rw-r--r-- | net/rxrpc/utils.c | 37 |
17 files changed, 986 insertions, 1058 deletions
diff --git a/net/rxrpc/Makefile b/net/rxrpc/Makefile index 6522e50fb750..10f3f48a16a8 100644 --- a/net/rxrpc/Makefile +++ b/net/rxrpc/Makefile @@ -10,6 +10,7 @@ af-rxrpc-y := \ conn_client.o \ conn_event.o \ conn_object.o \ + conn_service.o \ input.o \ insecure.o \ key.o \ diff --git a/net/rxrpc/af_rxrpc.c b/net/rxrpc/af_rxrpc.c index 5d3e795a7c48..d6e4e3b69dc3 100644 --- a/net/rxrpc/af_rxrpc.c +++ b/net/rxrpc/af_rxrpc.c @@ -788,27 +788,7 @@ static void __exit af_rxrpc_exit(void) proto_unregister(&rxrpc_proto); rxrpc_destroy_all_calls(); rxrpc_destroy_all_connections(); - ASSERTCMP(atomic_read(&rxrpc_n_skbs), ==, 0); - - /* We need to flush the scheduled work twice because the local endpoint - * records involve a work item in their destruction as they can only be - * destroyed from process context. However, a connection may have a - * work item outstanding - and this will pin the local endpoint record - * until the connection goes away. - * - * Peers don't pin locals and calls pin sockets - which prevents the - * module from being unloaded - so we should only need two flushes. - */ - _debug("flush scheduled work"); - flush_workqueue(rxrpc_workqueue); - _debug("flush scheduled work 2"); - flush_workqueue(rxrpc_workqueue); - _debug("synchronise RCU"); - rcu_barrier(); - _debug("destroy locals"); - ASSERT(idr_is_empty(&rxrpc_client_conn_ids)); - idr_destroy(&rxrpc_client_conn_ids); rxrpc_destroy_all_locals(); remove_proc_entry("rxrpc_conns", init_net.proc_net); diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index 702db72196fb..1bb9e7ac9e14 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -10,6 +10,7 @@ */ #include <linux/atomic.h> +#include <linux/seqlock.h> #include <net/sock.h> #include <net/af_rxrpc.h> #include <rxrpc/packet.h> @@ -35,7 +36,6 @@ struct rxrpc_crypt { queue_delayed_work(rxrpc_workqueue, (WS), (D)) #define rxrpc_queue_call(CALL) rxrpc_queue_work(&(CALL)->processor) -#define rxrpc_queue_conn(CONN) rxrpc_queue_work(&(CONN)->processor) struct rxrpc_connection; @@ -141,17 +141,16 @@ struct rxrpc_security { int (*init_connection_security)(struct rxrpc_connection *); /* prime a connection's packet security */ - void (*prime_packet_security)(struct rxrpc_connection *); + int (*prime_packet_security)(struct rxrpc_connection *); /* impose security on a packet */ - int (*secure_packet)(const struct rxrpc_call *, + int (*secure_packet)(struct rxrpc_call *, struct sk_buff *, size_t, void *); /* verify the security on a received packet */ - int (*verify_packet)(const struct rxrpc_call *, struct sk_buff *, - u32 *); + int (*verify_packet)(struct rxrpc_call *, struct sk_buff *, u32 *); /* issue a challenge */ int (*issue_challenge)(struct rxrpc_connection *); @@ -208,7 +207,7 @@ struct rxrpc_peer { struct hlist_head error_targets; /* targets for net error distribution */ struct work_struct error_distributor; struct rb_root service_conns; /* Service connections */ - rwlock_t conn_lock; + seqlock_t service_conn_lock; spinlock_t lock; /* access lock */ unsigned int if_mtu; /* interface MTU for this peer */ unsigned int mtu; /* network MTU for this peer */ @@ -231,18 +230,12 @@ struct rxrpc_peer { * Keys for matching a connection. */ struct rxrpc_conn_proto { - unsigned long hash_key; - struct rxrpc_local *local; /* Representation of local endpoint */ - u32 epoch; /* epoch of this connection */ - u32 cid; /* connection ID */ - u8 in_clientflag; /* RXRPC_CLIENT_INITIATED if we are server */ - u8 addr_size; /* Size of the address */ - sa_family_t family; /* Transport protocol */ - __be16 port; /* Peer UDP/UDP6 port */ - union { /* Peer address */ - struct in_addr ipv4_addr; - struct in6_addr ipv6_addr; - u32 raw_addr[0]; + union { + struct { + u32 epoch; /* epoch of this connection */ + u32 cid; /* connection ID */ + }; + u64 index_key; }; }; @@ -256,6 +249,37 @@ struct rxrpc_conn_parameters { }; /* + * Bits in the connection flags. + */ +enum rxrpc_conn_flag { + RXRPC_CONN_HAS_IDR, /* Has a client conn ID assigned */ + RXRPC_CONN_IN_SERVICE_CONNS, /* Conn is in peer->service_conns */ + RXRPC_CONN_IN_CLIENT_CONNS, /* Conn is in local->client_conns */ +}; + +/* + * Events that can be raised upon a connection. + */ +enum rxrpc_conn_event { + RXRPC_CONN_EV_CHALLENGE, /* Send challenge packet */ +}; + +/* + * The connection protocol state. + */ +enum rxrpc_conn_proto_state { + RXRPC_CONN_UNUSED, /* Connection not yet attempted */ + RXRPC_CONN_CLIENT, /* Client connection */ + RXRPC_CONN_SERVICE_UNSECURED, /* Service unsecured connection */ + RXRPC_CONN_SERVICE_CHALLENGING, /* Service challenging for security */ + RXRPC_CONN_SERVICE, /* Service secured connection */ + RXRPC_CONN_REMOTELY_ABORTED, /* Conn aborted by peer */ + RXRPC_CONN_LOCALLY_ABORTED, /* Conn aborted locally */ + RXRPC_CONN_NETWORK_ERROR, /* Conn terminated by network error */ + RXRPC_CONN__NR_STATES +}; + +/* * RxRPC connection definition * - matched by { local, peer, epoch, conn_id, direction } * - each connection can only handle four simultaneous calls @@ -265,44 +289,38 @@ struct rxrpc_connection { struct rxrpc_conn_parameters params; spinlock_t channel_lock; - struct rxrpc_call *channels[RXRPC_MAXCALLS]; /* active calls */ + + struct rxrpc_channel { + struct rxrpc_call __rcu *call; /* Active call */ + u32 call_id; /* ID of current call */ + u32 call_counter; /* Call ID counter */ + u32 last_call; /* ID of last call */ + u32 last_result; /* Result of last call (0/abort) */ + } channels[RXRPC_MAXCALLS]; wait_queue_head_t channel_wq; /* queue to wait for channel to become available */ + struct rcu_head rcu; struct work_struct processor; /* connection event processor */ union { struct rb_node client_node; /* Node in local->client_conns */ struct rb_node service_node; /* Node in peer->service_conns */ }; struct list_head link; /* link in master connection list */ - struct rb_root calls; /* calls on this connection */ struct sk_buff_head rx_queue; /* received conn-level packets */ const struct rxrpc_security *security; /* applied security module */ struct key *server_key; /* security for this service */ struct crypto_skcipher *cipher; /* encryption handle */ struct rxrpc_crypt csum_iv; /* packet checksum base */ unsigned long flags; -#define RXRPC_CONN_HAS_IDR 0 /* - Has a client conn ID assigned */ unsigned long events; -#define RXRPC_CONN_CHALLENGE 0 /* send challenge packet */ unsigned long put_time; /* Time at which last put */ - rwlock_t lock; /* access lock */ spinlock_t state_lock; /* state-change lock */ atomic_t usage; - enum { /* current state of connection */ - RXRPC_CONN_UNUSED, /* - connection not yet attempted */ - RXRPC_CONN_CLIENT, /* - client connection */ - RXRPC_CONN_SERVER_UNSECURED, /* - server unsecured connection */ - RXRPC_CONN_SERVER_CHALLENGING, /* - server challenging for security */ - RXRPC_CONN_SERVER, /* - server secured connection */ - RXRPC_CONN_REMOTELY_ABORTED, /* - conn aborted by peer */ - RXRPC_CONN_LOCALLY_ABORTED, /* - conn aborted locally */ - RXRPC_CONN_NETWORK_ERROR, /* - conn terminated by network error */ - } state; + enum rxrpc_conn_proto_state state : 8; /* current state of connection */ u32 local_abort; /* local abort code */ u32 remote_abort; /* remote abort code */ int error; /* local error incurred */ int debug_id; /* debug ID for printks */ - unsigned int call_counter; /* call ID counter */ atomic_t serial; /* packet serial number counter */ atomic_t hi_serial; /* highest serial number received */ atomic_t avail_chans; /* number of channels available */ @@ -382,6 +400,7 @@ enum rxrpc_call_state { * - matched by { connection, call_id } */ struct rxrpc_call { + struct rcu_head rcu; struct rxrpc_connection *conn; /* connection carrying call */ struct rxrpc_sock *socket; /* socket responsible */ struct timer_list lifetimer; /* lifetime remaining on call */ @@ -394,11 +413,11 @@ struct rxrpc_call { struct hlist_node error_link; /* link in error distribution list */ struct list_head accept_link; /* calls awaiting acceptance */ struct rb_node sock_node; /* node in socket call tree */ - struct rb_node conn_node; /* node in connection call tree */ struct sk_buff_head rx_queue; /* received packets */ struct sk_buff_head rx_oos_queue; /* packets received out of sequence */ struct sk_buff *tx_pending; /* Tx socket buffer being filled */ wait_queue_head_t tx_waitq; /* wait for Tx window space to become available */ + __be32 crypto_buf[2]; /* Temporary packet crypto buffer */ unsigned long user_call_ID; /* user-defined call ID */ unsigned long creation_jif; /* time of call creation */ unsigned long flags; @@ -442,19 +461,12 @@ struct rxrpc_call { #define RXRPC_ACKR_WINDOW_ASZ DIV_ROUND_UP(RXRPC_MAXACKS, BITS_PER_LONG) unsigned long ackr_window[RXRPC_ACKR_WINDOW_ASZ + 1]; - struct hlist_node hash_node; - unsigned long hash_key; /* Full hash key */ - u8 in_clientflag; /* Copy of conn->in_clientflag for hashing */ - struct rxrpc_local *local; /* Local endpoint. Used for hashing. */ - sa_family_t family; /* Frame protocol */ + u8 in_clientflag; /* Copy of conn->in_clientflag */ + struct rxrpc_local *local; /* Local endpoint. */ u32 call_id; /* call ID on connection */ u32 cid; /* connection ID plus channel index */ u32 epoch; /* epoch of this connection */ u16 service_id; /* service ID */ - union { /* Peer IP address for hashing */ - __be32 ipv4_addr; - __u8 ipv6_addr[16]; /* Anticipates eventual IPv6 support */ - } peer_ip; }; /* @@ -502,8 +514,6 @@ extern struct kmem_cache *rxrpc_call_jar; extern struct list_head rxrpc_calls; extern rwlock_t rxrpc_call_lock; -struct rxrpc_call *rxrpc_find_call_hash(struct rxrpc_host_header *, - void *, sa_family_t, const void *); struct rxrpc_call *rxrpc_find_call_by_user_ID(struct rxrpc_sock *, unsigned long); struct rxrpc_call *rxrpc_new_client_call(struct rxrpc_sock *, struct rxrpc_conn_parameters *, @@ -522,8 +532,10 @@ void __exit rxrpc_destroy_all_calls(void); */ extern struct idr rxrpc_client_conn_ids; -int rxrpc_get_client_connection_id(struct rxrpc_connection *, gfp_t); -void rxrpc_put_client_connection_id(struct rxrpc_connection *); +void rxrpc_destroy_client_conn_ids(void); +int rxrpc_connect_call(struct rxrpc_call *, struct rxrpc_conn_parameters *, + struct sockaddr_rxrpc *, gfp_t); +void rxrpc_unpublish_client_conn(struct rxrpc_connection *); /* * conn_event.c @@ -539,17 +551,14 @@ extern unsigned int rxrpc_connection_expiry; extern struct list_head rxrpc_connections; extern rwlock_t rxrpc_connection_lock; -int rxrpc_connect_call(struct rxrpc_call *, struct rxrpc_conn_parameters *, - struct sockaddr_rxrpc *, gfp_t); -struct rxrpc_connection *rxrpc_find_connection(struct rxrpc_local *, - struct rxrpc_peer *, - struct sk_buff *); +int rxrpc_extract_addr_from_skb(struct sockaddr_rxrpc *, struct sk_buff *); +struct rxrpc_connection *rxrpc_alloc_connection(gfp_t); +struct rxrpc_connection *rxrpc_find_connection_rcu(struct rxrpc_local *, + struct sk_buff *); +void __rxrpc_disconnect_call(struct rxrpc_call *); void rxrpc_disconnect_call(struct rxrpc_call *); void rxrpc_put_connection(struct rxrpc_connection *); void __exit rxrpc_destroy_all_connections(void); -struct rxrpc_connection *rxrpc_incoming_connection(struct rxrpc_local *, - struct rxrpc_peer *, - struct sk_buff *); static inline bool rxrpc_conn_is_client(const struct rxrpc_connection *conn) { @@ -558,7 +567,7 @@ static inline bool rxrpc_conn_is_client(const struct rxrpc_connection *conn) static inline bool rxrpc_conn_is_service(const struct rxrpc_connection *conn) { - return conn->proto.in_clientflag; + return !rxrpc_conn_is_client(conn); } static inline void rxrpc_get_connection(struct rxrpc_connection *conn) @@ -566,6 +575,31 @@ static inline void rxrpc_get_connection(struct rxrpc_connection *conn) atomic_inc(&conn->usage); } +static inline +struct rxrpc_connection *rxrpc_get_connection_maybe(struct rxrpc_connection *conn) +{ + return atomic_inc_not_zero(&conn->usage) ? conn : NULL; +} + +static inline bool rxrpc_queue_conn(struct rxrpc_connection *conn) +{ + if (!rxrpc_get_connection_maybe(conn)) + return false; + if (!rxrpc_queue_work(&conn->processor)) + rxrpc_put_connection(conn); + return true; +} + +/* + * conn_service.c + */ +struct rxrpc_connection *rxrpc_find_service_conn_rcu(struct rxrpc_peer *, + struct sk_buff *); +struct rxrpc_connection *rxrpc_incoming_connection(struct rxrpc_local *, + struct sockaddr_rxrpc *, + struct sk_buff *); +void rxrpc_unpublish_service_conn(struct rxrpc_connection *); + /* * input.c */ @@ -618,6 +652,11 @@ static inline void rxrpc_put_local(struct rxrpc_local *local) __rxrpc_put_local(local); } +static inline void rxrpc_queue_local(struct rxrpc_local *local) +{ + rxrpc_queue_work(&local->processor); +} + /* * misc.c */ @@ -722,8 +761,7 @@ static inline void rxrpc_sysctl_exit(void) {} /* * utils.c */ -void rxrpc_get_addr_from_skb(struct rxrpc_local *, const struct sk_buff *, - struct sockaddr_rxrpc *); +int rxrpc_extract_addr_from_skb(struct sockaddr_rxrpc *, struct sk_buff *); /* * debug tracing diff --git a/net/rxrpc/call_accept.c b/net/rxrpc/call_accept.c index 202e053a3c6d..0b2832141bd0 100644 --- a/net/rxrpc/call_accept.c +++ b/net/rxrpc/call_accept.c @@ -75,7 +75,6 @@ static int rxrpc_accept_incoming_call(struct rxrpc_local *local, { struct rxrpc_connection *conn; struct rxrpc_skb_priv *sp, *nsp; - struct rxrpc_peer *peer; struct rxrpc_call *call; struct sk_buff *notification; int ret; @@ -94,15 +93,7 @@ static int rxrpc_accept_incoming_call(struct rxrpc_local *local, rxrpc_new_skb(notification); notification->mark = RXRPC_SKB_MARK_NEW_CALL; - peer = rxrpc_lookup_peer(local, srx, GFP_NOIO); - if (!peer) { - _debug("no peer"); - ret = -EBUSY; - goto error; - } - - conn = rxrpc_incoming_connection(local, peer, skb); - rxrpc_put_peer(peer); + conn = rxrpc_incoming_connection(local, srx, skb); if (IS_ERR(conn)) { _debug("no conn"); ret = PTR_ERR(conn); @@ -128,12 +119,11 @@ static int rxrpc_accept_incoming_call(struct rxrpc_local *local, spin_lock(&call->conn->state_lock); if (sp->hdr.securityIndex > 0 && - call->conn->state == RXRPC_CONN_SERVER_UNSECURED) { + call->conn->state == RXRPC_CONN_SERVICE_UNSECURED) { _debug("await conn sec"); list_add_tail(&call->accept_link, &rx->secureq); - call->conn->state = RXRPC_CONN_SERVER_CHALLENGING; - rxrpc_get_connection(call->conn); - set_bit(RXRPC_CONN_CHALLENGE, &call->conn->events); + call->conn->state = RXRPC_CONN_SERVICE_CHALLENGING; + set_bit(RXRPC_CONN_EV_CHALLENGE, &call->conn->events); rxrpc_queue_conn(call->conn); } else { _debug("conn ready"); @@ -227,20 +217,8 @@ void rxrpc_accept_incoming_calls(struct rxrpc_local *local) whdr._rsvd = 0; whdr.serviceId = htons(sp->hdr.serviceId); - /* determine the remote address */ - memset(&srx, 0, sizeof(srx)); - srx.srx_family = AF_RXRPC; - srx.transport.family = local->srx.transport.family; - srx.transport_type = local->srx.transport_type; - switch (srx.transport.family) { - case AF_INET: - srx.transport_len = sizeof(struct sockaddr_in); - srx.transport.sin.sin_port = udp_hdr(skb)->source; - srx.transport.sin.sin_addr.s_addr = ip_hdr(skb)->saddr; - break; - default: - goto busy; - } + if (rxrpc_extract_addr_from_skb(&srx, skb) < 0) + goto drop; /* get the socket providing the service */ read_lock_bh(&local->services_lock); @@ -286,6 +264,10 @@ busy: rxrpc_free_skb(skb); return; +drop: + rxrpc_free_skb(skb); + return; + invalid_service: skb->priority = RX_INVALID_OPERATION; rxrpc_reject_packet(local, skb); diff --git a/net/rxrpc/call_event.c b/net/rxrpc/call_event.c index 0ba84295f913..fc32aa5764a2 100644 --- a/net/rxrpc/call_event.c +++ b/net/rxrpc/call_event.c @@ -858,11 +858,6 @@ void rxrpc_process_call(struct work_struct *work) iov[0].iov_len = sizeof(whdr); /* deal with events of a final nature */ - if (test_bit(RXRPC_CALL_EV_RELEASE, &call->events)) { - rxrpc_release_call(call); - clear_bit(RXRPC_CALL_EV_RELEASE, &call->events); - } - if (test_bit(RXRPC_CALL_EV_RCVD_ERROR, &call->events)) { enum rxrpc_skb_mark mark; int error; @@ -1094,7 +1089,7 @@ void rxrpc_process_call(struct work_struct *work) if (call->state == RXRPC_CALL_SERVER_SECURING) { _debug("securing"); - write_lock(&call->conn->lock); + write_lock(&call->socket->call_lock); if (!test_bit(RXRPC_CALL_RELEASED, &call->flags) && !test_bit(RXRPC_CALL_EV_RELEASE, &call->events)) { _debug("not released"); @@ -1102,7 +1097,7 @@ void rxrpc_process_call(struct work_struct *work) list_move_tail(&call->accept_link, &call->socket->acceptq); } - write_unlock(&call->conn->lock); + write_unlock(&call->socket->call_lock); read_lock(&call->state_lock); if (call->state < RXRPC_CALL_COMPLETE) set_bit(RXRPC_CALL_EV_POST_ACCEPT, &call->events); @@ -1144,6 +1139,11 @@ void rxrpc_process_call(struct work_struct *work) goto maybe_reschedule; } + if (test_bit(RXRPC_CALL_EV_RELEASE, &call->events)) { + rxrpc_release_call(call); + clear_bit(RXRPC_CALL_EV_RELEASE, &call->events); + } + /* other events may have been raised since we started checking */ goto maybe_reschedule; diff --git a/net/rxrpc/call_object.c b/net/rxrpc/call_object.c index ad933daae13b..91287c9d01bb 100644 --- a/net/rxrpc/call_object.c +++ b/net/rxrpc/call_object.c @@ -14,7 +14,6 @@ #include <linux/slab.h> #include <linux/module.h> #include <linux/circ_buf.h> -#include <linux/hashtable.h> #include <linux/spinlock_types.h> #include <net/sock.h> #include <net/af_rxrpc.h> @@ -61,142 +60,6 @@ static void rxrpc_dead_call_expired(unsigned long _call); static void rxrpc_ack_time_expired(unsigned long _call); static void rxrpc_resend_time_expired(unsigned long _call); -static DEFINE_SPINLOCK(rxrpc_call_hash_lock); -static DEFINE_HASHTABLE(rxrpc_call_hash, 10); - -/* - * Hash function for rxrpc_call_hash - */ -static unsigned long rxrpc_call_hashfunc( - u8 in_clientflag, - u32 cid, - u32 call_id, - u32 epoch, - u16 service_id, - sa_family_t family, - void *localptr, - unsigned int addr_size, - const u8 *peer_addr) -{ - const u16 *p; - unsigned int i; - unsigned long key; - - _enter(""); - - key = (unsigned long)localptr; - /* We just want to add up the __be32 values, so forcing the - * cast should be okay. - */ - key += epoch; - key += service_id; - key += call_id; - key += (cid & RXRPC_CIDMASK) >> RXRPC_CIDSHIFT; - key += cid & RXRPC_CHANNELMASK; - key += in_clientflag; - key += family; - /* Step through the peer address in 16-bit portions for speed */ - for (i = 0, p = (const u16 *)peer_addr; i < addr_size >> 1; i++, p++) - key += *p; - _leave(" key = 0x%lx", key); - return key; -} - -/* - * Add a call to the hashtable - */ -static void rxrpc_call_hash_add(struct rxrpc_call *call) -{ - unsigned long key; - unsigned int addr_size = 0; - - _enter(""); - switch (call->family) { - case AF_INET: - addr_size = sizeof(call->peer_ip.ipv4_addr); - break; - case AF_INET6: - addr_size = sizeof(call->peer_ip.ipv6_addr); - break; - default: - break; - } - key = rxrpc_call_hashfunc(call->in_clientflag, call->cid, - call->call_id, call->epoch, - call->service_id, call->family, - call->conn->params.local, addr_size, - call->peer_ip.ipv6_addr); - /* Store the full key in the call */ - call->hash_key = key; - spin_lock(&rxrpc_call_hash_lock); - hash_add_rcu(rxrpc_call_hash, &call->hash_node, key); - spin_unlock(&rxrpc_call_hash_lock); - _leave(""); -} - -/* - * Remove a call from the hashtable - */ -static void rxrpc_call_hash_del(struct rxrpc_call *call) -{ - _enter(""); - spin_lock(&rxrpc_call_hash_lock); - hash_del_rcu(&call->hash_node); - spin_unlock(&rxrpc_call_hash_lock); - _leave(""); -} - -/* - * Find a call in the hashtable and return it, or NULL if it - * isn't there. - */ -struct rxrpc_call *rxrpc_find_call_hash( - struct rxrpc_host_header *hdr, - void *localptr, - sa_family_t family, - const void *peer_addr) -{ - unsigned long key; - unsigned int addr_size = 0; - struct rxrpc_call *call = NULL; - struct rxrpc_call *ret = NULL; - u8 in_clientflag = hdr->flags & RXRPC_CLIENT_INITIATED; - - _enter(""); - switch (family) { - case AF_INET: - addr_size = sizeof(call->peer_ip.ipv4_addr); - break; - case AF_INET6: - addr_size = sizeof(call->peer_ip.ipv6_addr); - break; - default: - break; - } - - key = rxrpc_call_hashfunc(in_clientflag, hdr->cid, hdr->callNumber, - hdr->epoch, hdr->serviceId, - family, localptr, addr_size, - peer_addr); - hash_for_each_possible_rcu(rxrpc_call_hash, call, hash_node, key) { - if (call->hash_key == key && - call->call_id == hdr->callNumber && - call->cid == hdr->cid && - call->in_clientflag == in_clientflag && - call->service_id == hdr->serviceId && - call->family == family && - call->local == localptr && - memcmp(call->peer_ip.ipv6_addr, peer_addr, - addr_size) == 0 && - call->epoch == hdr->epoch) { - ret = call; - break; - } - } - _leave(" = %p", ret); - return ret; -} - /* * find an extant server call * - called in process context with IRQs enabled @@ -305,20 +168,7 @@ static struct rxrpc_call *rxrpc_alloc_client_call(struct rxrpc_sock *rx, call->socket = rx; call->rx_data_post = 1; - /* Record copies of information for hashtable lookup */ - call->family = rx->family; call->local = rx->local; - switch (call->family) { - case AF_INET: - call->peer_ip.ipv4_addr = srx->transport.sin.sin_addr.s_addr; - break; - case AF_INET6: - memcpy(call->peer_ip.ipv6_addr, - srx->transport.sin6.sin6_addr.in6_u.u6_addr8, - sizeof(call->peer_ip.ipv6_addr)); - break; - } - call->service_id = srx->srx_service; call->in_clientflag = 0; @@ -345,9 +195,6 @@ static int rxrpc_begin_client_call(struct rxrpc_call *call, call->state = RXRPC_CALL_CLIENT_SEND_REQUEST; - /* Add the new call to the hashtable */ - rxrpc_call_hash_add(call); - spin_lock(&call->conn->params.peer->lock); hlist_add_head(&call->error_link, &call->conn->params.peer->error_targets); spin_unlock(&call->conn->params.peer->lock); @@ -425,9 +272,10 @@ error: rxrpc_put_call(call); write_lock_bh(&rxrpc_call_lock); - list_del(&call->link); + list_del_init(&call->link); write_unlock_bh(&rxrpc_call_lock); + call->state = RXRPC_CALL_DEAD; rxrpc_put_call(call); _leave(" = %d", ret); return ERR_PTR(ret); @@ -439,6 +287,7 @@ error: */ found_user_ID_now_present: write_unlock(&rx->call_lock); + call->state = RXRPC_CALL_DEAD; rxrpc_put_call(call); _leave(" = -EEXIST [%p]", call); return ERR_PTR(-EEXIST); @@ -454,8 +303,7 @@ struct rxrpc_call *rxrpc_incoming_call(struct rxrpc_sock *rx, { struct rxrpc_skb_priv *sp = rxrpc_skb(skb); struct rxrpc_call *call, *candidate; - struct rb_node **p, *parent; - u32 call_id; + u32 call_id, chan; _enter(",%d", conn->debug_id); @@ -465,20 +313,23 @@ struct rxrpc_call *rxrpc_incoming_call(struct rxrpc_sock *rx, if (!candidate) return ERR_PTR(-EBUSY); + chan = sp->hdr.cid & RXRPC_CHANNELMASK; candidate->socket = rx; candidate->conn = conn; candidate->cid = sp->hdr.cid; candidate->call_id = sp->hdr.callNumber; - candidate->channel = sp->hdr.cid & RXRPC_CHANNELMASK; + candidate->channel = chan; candidate->rx_data_post = 0; candidate->state = RXRPC_CALL_SERVER_ACCEPTING; if (conn->security_ix > 0) candidate->state = RXRPC_CALL_SERVER_SECURING; - write_lock_bh(&conn->lock); + spin_lock(&conn->channel_lock); /* set the channel for this call */ - call = conn->channels[candidate->channel]; + call = rcu_dereference_protected(conn->channels[chan].call, + lockdep_is_held(&conn->channel_lock)); + _debug("channel[%u] is %p", candidate->channel, call); if (call && call->call_id == sp->hdr.callNumber) { /* already set; must've been a duplicate packet */ @@ -507,9 +358,9 @@ struct rxrpc_call *rxrpc_incoming_call(struct rxrpc_sock *rx, call->debug_id, rxrpc_call_states[call->state]); if (call->state >= RXRPC_CALL_COMPLETE) { - conn->channels[call->channel] = NULL; + __rxrpc_disconnect_call(call); } else { - write_unlock_bh(&conn->lock); + spin_unlock(&conn->channel_lock); kmem_cache_free(rxrpc_call_jar, candidate); _leave(" = -EBUSY"); return ERR_PTR(-EBUSY); @@ -519,33 +370,22 @@ struct rxrpc_call *rxrpc_incoming_call(struct rxrpc_sock *rx, /* check the call number isn't duplicate */ _debug("check dup"); call_id = sp->hdr.callNumber; - p = &conn->calls.rb_node; - parent = NULL; - while (*p) { - parent = *p; - call = rb_entry(parent, struct rxrpc_call, conn_node); - - /* The tree is sorted in order of the __be32 value without - * turning it into host order. - */ - if (call_id < call->call_id) - p = &(*p)->rb_left; - else if (call_id > call->call_id) - p = &(*p)->rb_right; - else - goto old_call; - } + + /* We just ignore calls prior to the current call ID. Terminated calls + * are handled via the connection. + */ + if (call_id <= conn->channels[chan].call_counter) + goto old_call; /* TODO: Just drop packet */ /* make the call available */ _debug("new call"); call = candidate; candidate = NULL; - rb_link_node(&call->conn_node, parent, p); - rb_insert_color(&call->conn_node, &conn->calls); - conn->channels[call->channel] = call; + conn->channels[chan].call_counter = call_id; + rcu_assign_pointer(conn->channels[chan].call, call); sock_hold(&rx->sk); rxrpc_get_connection(conn); - write_unlock_bh(&conn->lock); + spin_unlock(&conn->channel_lock); spin_lock(&conn->params.peer->lock); hlist_add_head(&call->error_link, &conn->params.peer->error_targets); @@ -555,27 +395,10 @@ struct rxrpc_call *rxrpc_incoming_call(struct rxrpc_sock *rx, list_add_tail(&call->link, &rxrpc_calls); write_unlock_bh(&rxrpc_call_lock); - /* Record copies of information for hashtable lookup */ - call->family = rx->family; call->local = conn->params.local; - switch (call->family) { - case AF_INET: - call->peer_ip.ipv4_addr = - conn->params.peer->srx.transport.sin.sin_addr.s_addr; - break; - case AF_INET6: - memcpy(call->peer_ip.ipv6_addr, - conn->params.peer->srx.transport.sin6.sin6_addr.in6_u.u6_addr8, - sizeof(call->peer_ip.ipv6_addr)); - break; - default: - break; - } call->epoch = conn->proto.epoch; call->service_id = conn->params.service_id; - call->in_clientflag = conn->proto.in_clientflag; - /* Add the new call to the hashtable */ - rxrpc_call_hash_add(call); + call->in_clientflag = RXRPC_CLIENT_INITIATED; _net("CALL incoming %d on CONN %d", call->debug_id, call->conn->debug_id); @@ -585,19 +408,19 @@ struct rxrpc_call *rxrpc_incoming_call(struct rxrpc_sock *rx, return call; extant_call: - write_unlock_bh(&conn->lock); + spin_unlock(&conn->channel_lock); kmem_cache_free(rxrpc_call_jar, candidate); _leave(" = %p {%d} [extant]", call, call ? call->debug_id : -1); return call; aborted_call: - write_unlock_bh(&conn->lock); + spin_unlock(&conn->channel_lock); kmem_cache_free(rxrpc_call_jar, candidate); _leave(" = -ECONNABORTED"); return ERR_PTR(-ECONNABORTED); old_call: - write_unlock_bh(&conn->lock); + spin_unlock(&conn->channel_lock); kmem_cache_free(rxrpc_call_jar, candidate); _leave(" = -ECONNRESET [old]"); return ERR_PTR(-ECONNRESET); @@ -626,6 +449,10 @@ void rxrpc_release_call(struct rxrpc_call *call) */ _debug("RELEASE CALL %p (%d CONN %p)", call, call->debug_id, conn); + spin_lock(&conn->params.peer->lock); + hlist_del_init(&call->error_link); + spin_unlock(&conn->params.peer->lock); + write_lock_bh(&rx->call_lock); if (!list_empty(&call->accept_link)) { _debug("unlinking once-pending call %p { e=%lx f=%lx }", @@ -641,24 +468,17 @@ void rxrpc_release_call(struct rxrpc_call *call) write_unlock_bh(&rx->call_lock); /* free up the channel for reuse */ - spin_lock(&conn->channel_lock); - write_lock_bh(&conn->lock); - write_lock(&call->state_lock); - - rxrpc_disconnect_call(call); - - spin_unlock(&conn->channel_lock); + write_lock_bh(&call->state_lock); if (call->state < RXRPC_CALL_COMPLETE && call->state != RXRPC_CALL_CLIENT_FINAL_ACK) { _debug("+++ ABORTING STATE %d +++\n", call->state); call->state = RXRPC_CALL_LOCALLY_ABORTED; call->local_abort = RX_CALL_DEAD; - set_bit(RXRPC_CALL_EV_ABORT, &call->events); - rxrpc_queue_call(call); } - write_unlock(&call->state_lock); - write_unlock_bh(&conn->lock); + write_unlock_bh(&call->state_lock); + + rxrpc_disconnect_call(call); /* clean up the Rx queue */ if (!skb_queue_empty(&call->rx_queue) || @@ -792,6 +612,17 @@ void __rxrpc_put_call(struct rxrpc_call *call) } /* + * Final call destruction under RCU. + */ +static void rxrpc_rcu_destroy_call(struct rcu_head *rcu) +{ + struct rxrpc_call *call = container_of(rcu, struct rxrpc_call, rcu); + + rxrpc_purge_queue(&call->rx_queue); + kmem_cache_free(rxrpc_call_jar, call); +} + +/* * clean up a call */ static void rxrpc_cleanup_call(struct rxrpc_call *call) @@ -815,19 +646,7 @@ static void rxrpc_cleanup_call(struct rxrpc_call *call) return; } - if (call->conn) { - spin_lock(&call->conn->params.peer->lock); - hlist_del_init(&call->error_link); - spin_unlock(&call->conn->params.peer->lock); - - write_lock_bh(&call->conn->lock); - rb_erase(&call->conn_node, &call->conn->calls); - write_unlock_bh(&call->conn->lock); - rxrpc_put_connection(call->conn); - } - - /* Remove the call from the hash */ - rxrpc_call_hash_del(call); + ASSERTCMP(call->conn, ==, NULL); if (call->acks_window) { _debug("kill Tx window %d", @@ -855,7 +674,7 @@ static void rxrpc_cleanup_call(struct rxrpc_call *call) rxrpc_purge_queue(&call->rx_queue); ASSERT(skb_queue_empty(&call->rx_oos_queue)); sock_put(&call->socket->sk); - kmem_cache_free(rxrpc_call_jar, call); + call_rcu(&call->rcu, rxrpc_rcu_destroy_call); } /* diff --git a/net/rxrpc/conn_client.c b/net/rxrpc/conn_client.c index 82488d6adb83..9e91f27b0d0f 100644 --- a/net/rxrpc/conn_client.c +++ b/net/rxrpc/conn_client.c @@ -33,7 +33,8 @@ static DEFINE_SPINLOCK(rxrpc_conn_id_lock); * client conns away from the current allocation point to try and keep the IDs * concentrated. We will also need to retire connections from an old epoch. */ -int rxrpc_get_client_connection_id(struct rxrpc_connection *conn, gfp_t gfp) +static int rxrpc_get_client_connection_id(struct rxrpc_connection *conn, + gfp_t gfp) { u32 epoch; int id; @@ -83,7 +84,7 @@ error: /* * Release a connection ID for a client connection from the global pool. */ -void rxrpc_put_client_connection_id(struct rxrpc_connection *conn) +static void rxrpc_put_client_connection_id(struct rxrpc_connection *conn) { if (test_bit(RXRPC_CONN_HAS_IDR, &conn->flags)) { spin_lock(&rxrpc_conn_id_lock); @@ -92,3 +93,280 @@ void rxrpc_put_client_connection_id(struct rxrpc_connection *conn) spin_unlock(&rxrpc_conn_id_lock); } } + +/* + * Destroy the client connection ID tree. + */ +void rxrpc_destroy_client_conn_ids(void) +{ + struct rxrpc_connection *conn; + int id; + + if (!idr_is_empty(&rxrpc_client_conn_ids)) { + idr_for_each_entry(&rxrpc_client_conn_ids, conn, id) { + pr_err("AF_RXRPC: Leaked client conn %p {%d}\n", + conn, atomic_read(&conn->usage)); + } + BUG(); + } + + idr_destroy(&rxrpc_client_conn_ids); +} + +/* + * Allocate a client connection. The caller must take care to clear any + * padding bytes in *cp. + */ +static struct rxrpc_connection * +rxrpc_alloc_client_connection(struct rxrpc_conn_parameters *cp, gfp_t gfp) +{ + struct rxrpc_connection *conn; + int ret; + + _enter(""); + + conn = rxrpc_alloc_connection(gfp); + if (!conn) { + _leave(" = -ENOMEM"); + return ERR_PTR(-ENOMEM); + } + + conn->params = *cp; + conn->out_clientflag = RXRPC_CLIENT_INITIATED; + conn->state = RXRPC_CONN_CLIENT; + + ret = rxrpc_get_client_connection_id(conn, gfp); + if (ret < 0) + goto error_0; + + ret = rxrpc_init_client_conn_security(conn); + if (ret < 0) + goto error_1; + + ret = conn->security->prime_packet_security(conn); + if (ret < 0) + goto error_2; + + write_lock(&rxrpc_connection_lock); + list_add_tail(&conn->link, &rxrpc_connections); + write_unlock(&rxrpc_connection_lock); + + /* We steal the caller's peer ref. */ + cp->peer = NULL; + rxrpc_get_local(conn->params.local); + key_get(conn->params.key); + + _leave(" = %p", conn); + return conn; + +error_2: + conn->security->clear(conn); +error_1: + rxrpc_put_client_connection_id(conn); +error_0: + kfree(conn); + _leave(" = %d", ret); + return ERR_PTR(ret); +} + +/* + * find a connection for a call + * - called in process context with IRQs enabled + */ +int rxrpc_connect_call(struct rxrpc_call *call, + struct rxrpc_conn_parameters *cp, + struct sockaddr_rxrpc *srx, + gfp_t gfp) +{ + struct rxrpc_connection *conn, *candidate = NULL; + struct rxrpc_local *local = cp->local; + struct rb_node *p, **pp, *parent; + long diff; + int chan; + + DECLARE_WAITQUEUE(myself, current); + + _enter("{%d,%lx},", call->debug_id, call->user_call_ID); + + cp->peer = rxrpc_lookup_peer(cp->local, srx, gfp); + if (!cp->peer) + return -ENOMEM; + + if (!cp->exclusive) { + /* Search for a existing client connection unless this is going + * to be a connection that's used exclusively for a single call. + */ + _debug("search 1"); + spin_lock(&local->client_conns_lock); + p = local->client_conns.rb_node; + while (p) { + conn = rb_entry(p, struct rxrpc_connection, client_node); + +#define cmp(X) ((long)conn->params.X - (long)cp->X) + diff = (cmp(peer) ?: + cmp(key) ?: + cmp(security_level)); + if (diff < 0) + p = p->rb_left; + else if (diff > 0) + p = p->rb_right; + else + goto found_extant_conn; + } + spin_unlock(&local->client_conns_lock); + } + + /* We didn't find a connection or we want an exclusive one. */ + _debug("get new conn"); + candidate = rxrpc_alloc_client_connection(cp, gfp); + if (!candidate) { + _leave(" = -ENOMEM"); + return -ENOMEM; + } + + if (cp->exclusive) { + /* Assign the call on an exclusive connection to channel 0 and + * don't add the connection to the endpoint's shareable conn + * lookup tree. + */ + _debug("exclusive chan 0"); + conn = candidate; + atomic_set(&conn->avail_chans, RXRPC_MAXCALLS - 1); + spin_lock(&conn->channel_lock); + chan = 0; + goto found_channel; + } + + /* We need to redo the search before attempting to add a new connection + * lest we race with someone else adding a conflicting instance. + */ + _debug("search 2"); + spin_lock(&local->client_conns_lock); + + pp = &local->client_conns.rb_node; + parent = NULL; + while (*pp) { + parent = *pp; + conn = rb_entry(parent, struct rxrpc_connection, client_node); + + diff = (cmp(peer) ?: + cmp(key) ?: + cmp(security_level)); + if (diff < 0) + pp = &(*pp)->rb_left; + else if (diff > 0) + pp = &(*pp)->rb_right; + else + goto found_extant_conn; + } + + /* The second search also failed; simply add the new connection with + * the new call in channel 0. Note that we need to take the channel + * lock before dropping the client conn lock. + */ + _debug("new conn"); + set_bit(RXRPC_CONN_IN_CLIENT_CONNS, &candidate->flags); + rb_link_node(&candidate->client_node, parent, pp); + rb_insert_color(&candidate->client_node, &local->client_conns); +attached: + conn = candidate; + candidate = NULL; + + atomic_set(&conn->avail_chans, RXRPC_MAXCALLS - 1); + spin_lock(&conn->channel_lock); + spin_unlock(&local->client_conns_lock); + chan = 0; + +found_channel: + _debug("found chan"); + call->conn = conn; + call->channel = chan; + call->epoch = conn->proto.epoch; + call->cid = conn->proto.cid | chan; + call->call_id = ++conn->channels[chan].call_counter; + conn->channels[chan].call_id = call->call_id; + rcu_assign_pointer(conn->channels[chan].call, call); + + _net("CONNECT call %d on conn %d", call->debug_id, conn->debug_id); + + spin_unlock(&conn->channel_lock); + rxrpc_put_peer(cp->peer); + cp->peer = NULL; + _leave(" = %p {u=%d}", conn, atomic_read(&conn->usage)); + return 0; + + /* We found a potentially suitable connection already in existence. If + * we can reuse it (ie. its usage count hasn't been reduced to 0 by the + * reaper), discard any candidate we may have allocated, and try to get + * a channel on this one, otherwise we have to replace it. + */ +found_extant_conn: + _debug("found conn"); + if (!rxrpc_get_connection_maybe(conn)) { + set_bit(RXRPC_CONN_IN_CLIENT_CONNS, &candidate->flags); + rb_replace_node(&conn->client_node, + &candidate->client_node, + &local->client_conns); + clear_bit(RXRPC_CONN_IN_CLIENT_CONNS, &conn->flags); + goto attached; + } + + spin_unlock(&local->client_conns_lock); + + rxrpc_put_connection(candidate); + + if (!atomic_add_unless(&conn->avail_chans, -1, 0)) { + if (!gfpflags_allow_blocking(gfp)) { + rxrpc_put_connection(conn); + _leave(" = -EAGAIN"); + return -EAGAIN; + } + + add_wait_queue(&conn->channel_wq, &myself); + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + if (atomic_add_unless(&conn->avail_chans, -1, 0)) + break; + if (signal_pending(current)) + goto interrupted; + schedule(); + } + remove_wait_queue(&conn->channel_wq, &myself); + __set_current_state(TASK_RUNNING); + } + + /* The connection allegedly now has a free channel and we can now + * attach the call to it. + */ + spin_lock(&conn->channel_lock); + + for (chan = 0; chan < RXRPC_MAXCALLS; chan++) + if (!conn->channels[chan].call) + goto found_channel; + BUG(); + +interrupted: + remove_wait_queue(&conn->channel_wq, &myself); + __set_current_state(TASK_RUNNING); + rxrpc_put_connection(conn); + rxrpc_put_peer(cp->peer); + cp->peer = NULL; + _leave(" = -ERESTARTSYS"); + return -ERESTARTSYS; +} + +/* + * Remove a client connection from the local endpoint's tree, thereby removing + * it as a target for reuse for new client calls. + */ +void rxrpc_unpublish_client_conn(struct rxrpc_connection *conn) +{ + struct rxrpc_local *local = conn->params.local; + + spin_lock(&local->client_conns_lock); + if (test_and_clear_bit(RXRPC_CONN_IN_CLIENT_CONNS, &conn->flags)) + rb_erase(&conn->client_node, &local->client_conns); + spin_unlock(&local->client_conns_lock); + + rxrpc_put_client_connection_id(conn); +} diff --git a/net/rxrpc/conn_event.c b/net/rxrpc/conn_event.c index bf6971555eac..cee0f35bc1cf 100644 --- a/net/rxrpc/conn_event.c +++ b/net/rxrpc/conn_event.c @@ -31,15 +31,17 @@ static void rxrpc_abort_calls(struct rxrpc_connection *conn, int state, u32 abort_code) { struct rxrpc_call *call; - struct rb_node *p; + int i; _enter("{%d},%x", conn->debug_id, abort_code); - read_lock_bh(&conn->lock); + spin_lock(&conn->channel_lock); - for (p = rb_first(&conn->calls); p; p = rb_next(p)) { - call = rb_entry(p, struct rxrpc_call, conn_node); - write_lock(&call->state_lock); + for (i = 0; i < RXRPC_MAXCALLS; i++) { + call = rcu_dereference_protected( + conn->channels[i].call, + lockdep_is_held(&conn->channel_lock)); + write_lock_bh(&call->state_lock); if (call->state <= RXRPC_CALL_COMPLETE) { call->state = state; if (state == RXRPC_CALL_LOCALLY_ABORTED) { @@ -51,10 +53,10 @@ static void rxrpc_abort_calls(struct rxrpc_connection *conn, int state, } rxrpc_queue_call(call); } - write_unlock(&call->state_lock); + write_unlock_bh(&call->state_lock); } - read_unlock_bh(&conn->lock); + spin_unlock(&conn->channel_lock); _leave(""); } @@ -188,18 +190,24 @@ static int rxrpc_process_event(struct rxrpc_connection *conn, if (ret < 0) return ret; - conn->security->prime_packet_security(conn); - read_lock_bh(&conn->lock); + ret = conn->security->prime_packet_security(conn); + if (ret < 0) + return ret; + + spin_lock(&conn->channel_lock); spin_lock(&conn->state_lock); - if (conn->state == RXRPC_CONN_SERVER_CHALLENGING) { - conn->state = RXRPC_CONN_SERVER; + if (conn->state == RXRPC_CONN_SERVICE_CHALLENGING) { + conn->state = RXRPC_CONN_SERVICE; for (loop = 0; loop < RXRPC_MAXCALLS; loop++) - rxrpc_call_is_secure(conn->channels[loop]); + rxrpc_call_is_secure( + rcu_dereference_protected( + conn->channels[loop].call, + lockdep_is_held(&conn->channel_lock))); } spin_unlock(&conn->state_lock); - read_unlock_bh(&conn->lock); + spin_unlock(&conn->channel_lock); return 0; default: @@ -263,12 +271,8 @@ void rxrpc_process_connection(struct work_struct *work) _enter("{%d}", conn->debug_id); - rxrpc_get_connection(conn); - - if (test_and_clear_bit(RXRPC_CONN_CHALLENGE, &conn->events)) { + if (test_and_clear_bit(RXRPC_CONN_EV_CHALLENGE, &conn->events)) rxrpc_secure_connection(conn); - rxrpc_put_connection(conn); - } /* go through the conn-level event packets, releasing the ref on this * connection that each one has when we've finished with it */ @@ -283,7 +287,6 @@ void rxrpc_process_connection(struct work_struct *work) goto requeue_and_leave; case -ECONNABORTED: default: - rxrpc_put_connection(conn); rxrpc_free_skb(skb); break; } @@ -301,7 +304,6 @@ requeue_and_leave: protocol_error: if (rxrpc_abort_connection(conn, -ret, abort_code) < 0) goto requeue_and_leave; - rxrpc_put_connection(conn); rxrpc_free_skb(skb); _leave(" [EPROTO]"); goto out; @@ -315,7 +317,7 @@ void rxrpc_reject_packet(struct rxrpc_local *local, struct sk_buff *skb) CHECK_SLAB_OKAY(&local->usage); skb_queue_tail(&local->reject_queue, skb); - rxrpc_queue_work(&local->processor); + rxrpc_queue_local(local); } /* diff --git a/net/rxrpc/conn_object.c b/net/rxrpc/conn_object.c index 4bfad7cf96cb..896d84493a05 100644 --- a/net/rxrpc/conn_object.c +++ b/net/rxrpc/conn_object.c @@ -15,7 +15,6 @@ #include <linux/slab.h> #include <linux/net.h> #include <linux/skbuff.h> -#include <linux/crypto.h> #include <net/sock.h> #include <net/af_rxrpc.h> #include "ar-internal.h" @@ -34,7 +33,7 @@ static DECLARE_DELAYED_WORK(rxrpc_connection_reap, rxrpc_connection_reaper); /* * allocate a new connection */ -static struct rxrpc_connection *rxrpc_alloc_connection(gfp_t gfp) +struct rxrpc_connection *rxrpc_alloc_connection(gfp_t gfp) { struct rxrpc_connection *conn; @@ -46,12 +45,13 @@ static struct rxrpc_connection *rxrpc_alloc_connection(gfp_t gfp) init_waitqueue_head(&conn->channel_wq); INIT_WORK(&conn->processor, &rxrpc_process_connection); INIT_LIST_HEAD(&conn->link); - conn->calls = RB_ROOT; skb_queue_head_init(&conn->rx_queue); conn->security = &rxrpc_no_security; - rwlock_init(&conn->lock); spin_lock_init(&conn->state_lock); - atomic_set(&conn->usage, 1); + /* We maintain an extra ref on the connection whilst it is + * on the rxrpc_connections list. + */ + atomic_set(&conn->usage, 2); conn->debug_id = atomic_inc_return(&rxrpc_debug_id); atomic_set(&conn->avail_chans, RXRPC_MAXCALLS); conn->size_align = 4; @@ -63,465 +63,118 @@ static struct rxrpc_connection *rxrpc_alloc_connection(gfp_t gfp) } /* - * add a call to a connection's call-by-ID tree - */ -static void rxrpc_add_call_ID_to_conn(struct rxrpc_connection *conn, - struct rxrpc_call *call) -{ - struct rxrpc_call *xcall; - struct rb_node *parent, **p; - __be32 call_id; - - write_lock_bh(&conn->lock); - - call_id = call->call_id; - p = &conn->calls.rb_node; - parent = NULL; - while (*p) { - parent = *p; - xcall = rb_entry(parent, struct rxrpc_call, conn_node); - - if (call_id < xcall->call_id) - p = &(*p)->rb_left; - else if (call_id > xcall->call_id) - p = &(*p)->rb_right; - else - BUG(); - } - - rb_link_node(&call->conn_node, parent, p); - rb_insert_color(&call->conn_node, &conn->calls); - - write_unlock_bh(&conn->lock); -} - -/* - * Allocate a client connection. The caller must take care to clear any - * padding bytes in *cp. + * Look up a connection in the cache by protocol parameters. + * + * If successful, a pointer to the connection is returned, but no ref is taken. + * NULL is returned if there is no match. + * + * The caller must be holding the RCU read lock. */ -static struct rxrpc_connection * -rxrpc_alloc_client_connection(struct rxrpc_conn_parameters *cp, gfp_t gfp) +struct rxrpc_connection *rxrpc_find_connection_rcu(struct rxrpc_local *local, + struct sk_buff *skb) { struct rxrpc_connection *conn; - int ret; - - _enter(""); - - conn = rxrpc_alloc_connection(gfp); - if (!conn) { - _leave(" = -ENOMEM"); - return ERR_PTR(-ENOMEM); - } - - conn->params = *cp; - conn->proto.local = cp->local; - conn->proto.epoch = rxrpc_epoch; - conn->proto.cid = 0; - conn->proto.in_clientflag = 0; - conn->proto.family = cp->peer->srx.transport.family; - conn->out_clientflag = RXRPC_CLIENT_INITIATED; - conn->state = RXRPC_CONN_CLIENT; - - switch (conn->proto.family) { - case AF_INET: - conn->proto.addr_size = sizeof(conn->proto.ipv4_addr); - conn->proto.ipv4_addr = cp->peer->srx.transport.sin.sin_addr; - conn->proto.port = cp->peer->srx.transport.sin.sin_port; - break; - } - - ret = rxrpc_get_client_connection_id(conn, gfp); - if (ret < 0) - goto error_0; - - ret = rxrpc_init_client_conn_security(conn); - if (ret < 0) - goto error_1; - - conn->security->prime_packet_security(conn); - - write_lock(&rxrpc_connection_lock); - list_add_tail(&conn->link, &rxrpc_connections); - write_unlock(&rxrpc_connection_lock); - - /* We steal the caller's peer ref. */ - cp->peer = NULL; - rxrpc_get_local(conn->params.local); - key_get(conn->params.key); - - _leave(" = %p", conn); - return conn; - -error_1: - rxrpc_put_client_connection_id(conn); -error_0: - kfree(conn); - _leave(" = %d", ret); - return ERR_PTR(ret); -} - -/* - * find a connection for a call - * - called in process context with IRQs enabled - */ -int rxrpc_connect_call(struct rxrpc_call *call, - struct rxrpc_conn_parameters *cp, - struct sockaddr_rxrpc *srx, - gfp_t gfp) -{ - struct rxrpc_connection *conn, *candidate = NULL; - struct rxrpc_local *local = cp->local; - struct rb_node *p, **pp, *parent; - long diff; - int chan; + struct rxrpc_conn_proto k; + struct rxrpc_skb_priv *sp = rxrpc_skb(skb); + struct sockaddr_rxrpc srx; + struct rxrpc_peer *peer; - DECLARE_WAITQUEUE(myself, current); + _enter(",%x", sp->hdr.cid & RXRPC_CIDMASK); - _enter("{%d,%lx},", call->debug_id, call->user_call_ID); + if (rxrpc_extract_addr_from_skb(&srx, skb) < 0) + goto not_found; - cp->peer = rxrpc_lookup_peer(cp->local, srx, gfp); - if (!cp->peer) - return -ENOMEM; + k.epoch = sp->hdr.epoch; + k.cid = sp->hdr.cid & RXRPC_CIDMASK; - if (!cp->exclusive) { - /* Search for a existing client connection unless this is going - * to be a connection that's used exclusively for a single call. - */ - _debug("search 1"); - spin_lock(&local->client_conns_lock); - p = local->client_conns.rb_node; - while (p) { - conn = rb_entry(p, struct rxrpc_connection, client_node); - -#define cmp(X) ((long)conn->params.X - (long)cp->X) - diff = (cmp(peer) ?: - cmp(key) ?: - cmp(security_level)); - if (diff < 0) - p = p->rb_left; - else if (diff > 0) - p = p->rb_right; - else - goto found_extant_conn; - } - spin_unlock(&local->client_conns_lock); + /* We may have to handle mixing IPv4 and IPv6 */ + if (srx.transport.family != local->srx.transport.family) { + pr_warn_ratelimited("AF_RXRPC: Protocol mismatch %u not %u\n", + srx.transport.family, + local->srx.transport.family); + goto not_found; } - /* We didn't find a connection or we want an exclusive one. */ - _debug("get new conn"); - candidate = rxrpc_alloc_client_connection(cp, gfp); - if (!candidate) { - _leave(" = -ENOMEM"); - return -ENOMEM; - } + k.epoch = sp->hdr.epoch; + k.cid = sp->hdr.cid & RXRPC_CIDMASK; - if (cp->exclusive) { - /* Assign the call on an exclusive connection to channel 0 and - * don't add the connection to the endpoint's shareable conn - * lookup tree. + if (sp->hdr.flags & RXRPC_CLIENT_INITIATED) { + /* We need to look up service connections by the full protocol + * parameter set. We look up the peer first as an intermediate + * step and then the connection from the peer's tree. */ - _debug("exclusive chan 0"); - conn = candidate; - atomic_set(&conn->avail_chans, RXRPC_MAXCALLS - 1); - spin_lock(&conn->channel_lock); - chan = 0; - goto found_channel; - } - - /* We need to redo the search before attempting to add a new connection - * lest we race with someone else adding a conflicting instance. - */ - _debug("search 2"); - spin_lock(&local->client_conns_lock); - - pp = &local->client_conns.rb_node; - parent = NULL; - while (*pp) { - parent = *pp; - conn = rb_entry(parent, struct rxrpc_connection, client_node); - - diff = (cmp(peer) ?: - cmp(key) ?: - cmp(security_level)); - if (diff < 0) - pp = &(*pp)->rb_left; - else if (diff > 0) - pp = &(*pp)->rb_right; - else - goto found_extant_conn; - } - - /* The second search also failed; simply add the new connection with - * the new call in channel 0. Note that we need to take the channel - * lock before dropping the client conn lock. - */ - _debug("new conn"); - conn = candidate; - candidate = NULL; - - rb_link_node(&conn->client_node, parent, pp); - rb_insert_color(&conn->client_node, &local->client_conns); - - atomic_set(&conn->avail_chans, RXRPC_MAXCALLS - 1); - spin_lock(&conn->channel_lock); - spin_unlock(&local->client_conns_lock); - chan = 0; - -found_channel: - _debug("found chan"); - call->conn = conn; - call->channel = chan; - call->epoch = conn->proto.epoch; - call->cid = conn->proto.cid | chan; - call->call_id = ++conn->call_counter; - rcu_assign_pointer(conn->channels[chan], call); - - _net("CONNECT call %d on conn %d", call->debug_id, conn->debug_id); - - rxrpc_add_call_ID_to_conn(conn, call); - spin_unlock(&conn->channel_lock); - rxrpc_put_peer(cp->peer); - cp->peer = NULL; - _leave(" = %p {u=%d}", conn, atomic_read(&conn->usage)); - return 0; - - /* We found a suitable connection already in existence. Discard any - * candidate we may have allocated, and try to get a channel on this - * one. - */ -found_extant_conn: - _debug("found conn"); - rxrpc_get_connection(conn); - spin_unlock(&local->client_conns_lock); - - rxrpc_put_connection(candidate); - - if (!atomic_add_unless(&conn->avail_chans, -1, 0)) { - if (!gfpflags_allow_blocking(gfp)) { - rxrpc_put_connection(conn); - _leave(" = -EAGAIN"); - return -EAGAIN; + peer = rxrpc_lookup_peer_rcu(local, &srx); + if (!peer) + goto not_found; + conn = rxrpc_find_service_conn_rcu(peer, skb); + if (!conn || atomic_read(&conn->usage) == 0) + goto not_found; + _leave(" = %p", conn); + return conn; + } else { + /* Look up client connections by connection ID alone as their + * IDs are unique for this machine. + */ + conn = idr_find(&rxrpc_client_conn_ids, + sp->hdr.cid >> RXRPC_CIDSHIFT); + if (!conn || atomic_read(&conn->usage) == 0) { + _debug("no conn"); + goto not_found; } - add_wait_queue(&conn->channel_wq, &myself); - for (;;) { - set_current_state(TASK_INTERRUPTIBLE); - if (atomic_add_unless(&conn->avail_chans, -1, 0)) - break; - if (signal_pending(current)) - goto interrupted; - schedule(); + if (conn->proto.epoch != k.epoch || + conn->params.local != local) + goto not_found; + + peer = conn->params.peer; + switch (srx.transport.family) { + case AF_INET: + if (peer->srx.transport.sin.sin_port != + srx.transport.sin.sin_port || + peer->srx.transport.sin.sin_addr.s_addr != + srx.transport.sin.sin_addr.s_addr) + goto not_found; + break; + default: + BUG(); } - remove_wait_queue(&conn->channel_wq, &myself); - __set_current_state(TASK_RUNNING); - } - - /* The connection allegedly now has a free channel and we can now - * attach the call to it. - */ - spin_lock(&conn->channel_lock); - - for (chan = 0; chan < RXRPC_MAXCALLS; chan++) - if (!conn->channels[chan]) - goto found_channel; - BUG(); - -interrupted: - remove_wait_queue(&conn->channel_wq, &myself); - __set_current_state(TASK_RUNNING); - rxrpc_put_connection(conn); - rxrpc_put_peer(cp->peer); - cp->peer = NULL; - _leave(" = -ERESTARTSYS"); - return -ERESTARTSYS; -} - -/* - * get a record of an incoming connection - */ -struct rxrpc_connection *rxrpc_incoming_connection(struct rxrpc_local *local, - struct rxrpc_peer *peer, - struct sk_buff *skb) -{ - struct rxrpc_connection *conn, *candidate = NULL; - struct rxrpc_skb_priv *sp = rxrpc_skb(skb); - struct rb_node *p, **pp; - const char *new = "old"; - __be32 epoch; - u32 cid; - - _enter(""); - - ASSERT(sp->hdr.flags & RXRPC_CLIENT_INITIATED); - - epoch = sp->hdr.epoch; - cid = sp->hdr.cid & RXRPC_CIDMASK; - - /* search the connection list first */ - read_lock_bh(&peer->conn_lock); - - p = peer->service_conns.rb_node; - while (p) { - conn = rb_entry(p, struct rxrpc_connection, service_node); - _debug("maybe %x", conn->proto.cid); - - if (epoch < conn->proto.epoch) - p = p->rb_left; - else if (epoch > conn->proto.epoch) - p = p->rb_right; - else if (cid < conn->proto.cid) - p = p->rb_left; - else if (cid > conn->proto.cid) - p = p->rb_right; - else - goto found_extant_connection; - } - read_unlock_bh(&peer->conn_lock); - - /* not yet present - create a candidate for a new record and then - * redo the search */ - candidate = rxrpc_alloc_connection(GFP_NOIO); - if (!candidate) { - _leave(" = -ENOMEM"); - return ERR_PTR(-ENOMEM); + _leave(" = %p", conn); + return conn; } - candidate->proto.local = local; - candidate->proto.epoch = sp->hdr.epoch; - candidate->proto.cid = sp->hdr.cid & RXRPC_CIDMASK; - candidate->proto.in_clientflag = RXRPC_CLIENT_INITIATED; - candidate->params.local = local; - candidate->params.peer = peer; - candidate->params.service_id = sp->hdr.serviceId; - candidate->security_ix = sp->hdr.securityIndex; - candidate->out_clientflag = 0; - candidate->state = RXRPC_CONN_SERVER; - if (candidate->params.service_id) - candidate->state = RXRPC_CONN_SERVER_UNSECURED; - - write_lock_bh(&peer->conn_lock); - - pp = &peer->service_conns.rb_node; - p = NULL; - while (*pp) { - p = *pp; - conn = rb_entry(p, struct rxrpc_connection, service_node); - - if (epoch < conn->proto.epoch) - pp = &(*pp)->rb_left; - else if (epoch > conn->proto.epoch) - pp = &(*pp)->rb_right; - else if (cid < conn->proto.cid) - pp = &(*pp)->rb_left; - else if (cid > conn->proto.cid) - pp = &(*pp)->rb_right; - else - goto found_extant_second; - } - - /* we can now add the new candidate to the list */ - conn = candidate; - candidate = NULL; - rb_link_node(&conn->service_node, p, pp); - rb_insert_color(&conn->service_node, &peer->service_conns); - rxrpc_get_peer(peer); - rxrpc_get_local(local); - - write_unlock_bh(&peer->conn_lock); - - write_lock(&rxrpc_connection_lock); - list_add_tail(&conn->link, &rxrpc_connections); - write_unlock(&rxrpc_connection_lock); - - new = "new"; - -success: - _net("CONNECTION %s %d {%x}", new, conn->debug_id, conn->proto.cid); - - _leave(" = %p {u=%d}", conn, atomic_read(&conn->usage)); - return conn; - - /* we found the connection in the list immediately */ -found_extant_connection: - if (sp->hdr.securityIndex != conn->security_ix) { - read_unlock_bh(&peer->conn_lock); - goto security_mismatch; - } - rxrpc_get_connection(conn); - read_unlock_bh(&peer->conn_lock); - goto success; - - /* we found the connection on the second time through the list */ -found_extant_second: - if (sp->hdr.securityIndex != conn->security_ix) { - write_unlock_bh(&peer->conn_lock); - goto security_mismatch; - } - rxrpc_get_connection(conn); - write_unlock_bh(&peer->conn_lock); - kfree(candidate); - goto success; - -security_mismatch: - kfree(candidate); - _leave(" = -EKEYREJECTED"); - return ERR_PTR(-EKEYREJECTED); +not_found: + _leave(" = NULL"); + return NULL; } /* - * find a connection based on transport and RxRPC connection ID for an incoming - * packet + * Disconnect a call and clear any channel it occupies when that call + * terminates. The caller must hold the channel_lock and must release the + * call's ref on the connection. */ -struct rxrpc_connection *rxrpc_find_connection(struct rxrpc_local *local, - struct rxrpc_peer *peer, - struct sk_buff *skb) +void __rxrpc_disconnect_call(struct rxrpc_call *call) { - struct rxrpc_connection *conn; - struct rxrpc_skb_priv *sp = rxrpc_skb(skb); - struct rb_node *p; - u32 epoch, cid; - - _enter(",{%x,%x}", sp->hdr.cid, sp->hdr.flags); + struct rxrpc_connection *conn = call->conn; + struct rxrpc_channel *chan = &conn->channels[call->channel]; - read_lock_bh(&peer->conn_lock); + _enter("%d,%d", conn->debug_id, call->channel); - cid = sp->hdr.cid & RXRPC_CIDMASK; - epoch = sp->hdr.epoch; + if (rcu_access_pointer(chan->call) == call) { + /* Save the result of the call so that we can repeat it if necessary + * through the channel, whilst disposing of the actual call record. + */ + chan->last_result = call->local_abort; + smp_wmb(); + chan->last_call = chan->call_id; + chan->call_id = chan->call_counter; - if (sp->hdr.flags & RXRPC_CLIENT_INITIATED) { - p = peer->service_conns.rb_node; - while (p) { - conn = rb_entry(p, struct rxrpc_connection, service_node); - - _debug("maybe %x", conn->proto.cid); - - if (epoch < conn->proto.epoch) - p = p->rb_left; - else if (epoch > conn->proto.epoch) - p = p->rb_right; - else if (cid < conn->proto.cid) - p = p->rb_left; - else if (cid > conn->proto.cid) - p = p->rb_right; - else - goto found; - } - } else { - conn = idr_find(&rxrpc_client_conn_ids, cid >> RXRPC_CIDSHIFT); - if (conn && conn->proto.epoch == epoch) - goto found; + rcu_assign_pointer(chan->call, NULL); + atomic_inc(&conn->avail_chans); + wake_up(&conn->channel_wq); } - read_unlock_bh(&peer->conn_lock); - _leave(" = NULL"); - return NULL; - -found: - rxrpc_get_connection(conn); - read_unlock_bh(&peer->conn_lock); - _leave(" = %p", conn); - return conn; + _leave(""); } /* @@ -531,15 +184,13 @@ found: void rxrpc_disconnect_call(struct rxrpc_call *call) { struct rxrpc_connection *conn = call->conn; - unsigned chan = call->channel; - _enter("%d,%d", conn->debug_id, call->channel); + spin_lock(&conn->channel_lock); + __rxrpc_disconnect_call(call); + spin_unlock(&conn->channel_lock); - if (conn->channels[chan] == call) { - rcu_assign_pointer(conn->channels[chan], NULL); - atomic_inc(&conn->avail_chans); - wake_up(&conn->channel_wq); - } + call->conn = NULL; + rxrpc_put_connection(conn); } /* @@ -553,10 +204,10 @@ void rxrpc_put_connection(struct rxrpc_connection *conn) _enter("%p{u=%d,d=%d}", conn, atomic_read(&conn->usage), conn->debug_id); - ASSERTCMP(atomic_read(&conn->usage), >, 0); + ASSERTCMP(atomic_read(&conn->usage), >, 1); conn->put_time = ktime_get_seconds(); - if (atomic_dec_and_test(&conn->usage)) { + if (atomic_dec_return(&conn->usage) == 1) { _debug("zombie"); rxrpc_queue_delayed_work(&rxrpc_connection_reap, 0); } @@ -567,15 +218,17 @@ void rxrpc_put_connection(struct rxrpc_connection *conn) /* * destroy a virtual connection */ -static void rxrpc_destroy_connection(struct rxrpc_connection *conn) +static void rxrpc_destroy_connection(struct rcu_head *rcu) { - _enter("%p{%d}", conn, atomic_read(&conn->usage)); + struct rxrpc_connection *conn = + container_of(rcu, struct rxrpc_connection, rcu); + + _enter("{%d,u=%d}", conn->debug_id, atomic_read(&conn->usage)); ASSERTCMP(atomic_read(&conn->usage), ==, 0); _net("DESTROY CONN %d", conn->debug_id); - ASSERT(RB_EMPTY_ROOT(&conn->calls)); rxrpc_purge_queue(&conn->rx_queue); conn->security->clear(conn); @@ -594,59 +247,41 @@ static void rxrpc_destroy_connection(struct rxrpc_connection *conn) static void rxrpc_connection_reaper(struct work_struct *work) { struct rxrpc_connection *conn, *_p; - struct rxrpc_peer *peer; - unsigned long now, earliest, reap_time; + unsigned long reap_older_than, earliest, put_time, now; LIST_HEAD(graveyard); _enter(""); now = ktime_get_seconds(); + reap_older_than = now - rxrpc_connection_expiry; earliest = ULONG_MAX; write_lock(&rxrpc_connection_lock); list_for_each_entry_safe(conn, _p, &rxrpc_connections, link) { - _debug("reap CONN %d { u=%d,t=%ld }", - conn->debug_id, atomic_read(&conn->usage), - (long) now - (long) conn->put_time); - - if (likely(atomic_read(&conn->usage) > 0)) + ASSERTCMP(atomic_read(&conn->usage), >, 0); + if (likely(atomic_read(&conn->usage) > 1)) continue; - if (rxrpc_conn_is_client(conn)) { - struct rxrpc_local *local = conn->params.local; - spin_lock(&local->client_conns_lock); - reap_time = conn->put_time + rxrpc_connection_expiry; - - if (atomic_read(&conn->usage) > 0) { - ; - } else if (reap_time <= now) { - list_move_tail(&conn->link, &graveyard); - rxrpc_put_client_connection_id(conn); - rb_erase(&conn->client_node, - &local->client_conns); - } else if (reap_time < earliest) { - earliest = reap_time; - } - - spin_unlock(&local->client_conns_lock); - } else { - peer = conn->params.peer; - write_lock_bh(&peer->conn_lock); - reap_time = conn->put_time + rxrpc_connection_expiry; - - if (atomic_read(&conn->usage) > 0) { - ; - } else if (reap_time <= now) { - list_move_tail(&conn->link, &graveyard); - rb_erase(&conn->service_node, - &peer->service_conns); - } else if (reap_time < earliest) { - earliest = reap_time; - } - - write_unlock_bh(&peer->conn_lock); + put_time = READ_ONCE(conn->put_time); + if (time_after(put_time, reap_older_than)) { + if (time_before(put_time, earliest)) + earliest = put_time; + continue; } + + /* The usage count sits at 1 whilst the object is unused on the + * list; we reduce that to 0 to make the object unavailable. + */ + if (atomic_cmpxchg(&conn->usage, 1, 0) != 1) + continue; + + if (rxrpc_conn_is_client(conn)) + rxrpc_unpublish_client_conn(conn); + else + rxrpc_unpublish_service_conn(conn); + + list_move_tail(&conn->link, &graveyard); } write_unlock(&rxrpc_connection_lock); @@ -657,14 +292,14 @@ static void rxrpc_connection_reaper(struct work_struct *work) (earliest - now) * HZ); } - /* then destroy all those pulled out */ while (!list_empty(&graveyard)) { conn = list_entry(graveyard.next, struct rxrpc_connection, link); list_del_init(&conn->link); ASSERTCMP(atomic_read(&conn->usage), ==, 0); - rxrpc_destroy_connection(conn); + skb_queue_purge(&conn->rx_queue); + call_rcu(&conn->rcu, rxrpc_destroy_connection); } _leave(""); @@ -676,11 +311,30 @@ static void rxrpc_connection_reaper(struct work_struct *work) */ void __exit rxrpc_destroy_all_connections(void) { + struct rxrpc_connection *conn, *_p; + bool leak = false; + _enter(""); rxrpc_connection_expiry = 0; cancel_delayed_work(&rxrpc_connection_reap); rxrpc_queue_delayed_work(&rxrpc_connection_reap, 0); + flush_workqueue(rxrpc_workqueue); + + write_lock(&rxrpc_connection_lock); + list_for_each_entry_safe(conn, _p, &rxrpc_connections, link) { + pr_err("AF_RXRPC: Leaked conn %p {%d}\n", + conn, atomic_read(&conn->usage)); + leak = true; + } + write_unlock(&rxrpc_connection_lock); + BUG_ON(leak); + + /* Make sure the local and peer records pinned by any dying connections + * are released. + */ + rcu_barrier(); + rxrpc_destroy_client_conn_ids(); _leave(""); } diff --git a/net/rxrpc/conn_service.c b/net/rxrpc/conn_service.c new file mode 100644 index 000000000000..7cbd612be0d7 --- /dev/null +++ b/net/rxrpc/conn_service.c @@ -0,0 +1,230 @@ +/* Service connection management + * + * Copyright (C) 2016 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#include <linux/slab.h> +#include "ar-internal.h" + +/* + * Find a service connection under RCU conditions. + * + * We could use a hash table, but that is subject to bucket stuffing by an + * attacker as the client gets to pick the epoch and cid values and would know + * the hash function. So, instead, we use a hash table for the peer and from + * that an rbtree to find the service connection. Under ordinary circumstances + * it might be slower than a large hash table, but it is at least limited in + * depth. + */ +struct rxrpc_connection *rxrpc_find_service_conn_rcu(struct rxrpc_peer *peer, + struct sk_buff *skb) +{ + struct rxrpc_connection *conn = NULL; + struct rxrpc_conn_proto k; + struct rxrpc_skb_priv *sp = rxrpc_skb(skb); + struct rb_node *p; + unsigned int seq = 0; + + k.epoch = sp->hdr.epoch; + k.cid = sp->hdr.cid & RXRPC_CIDMASK; + + do { + /* Unfortunately, rbtree walking doesn't give reliable results + * under just the RCU read lock, so we have to check for + * changes. + */ + read_seqbegin_or_lock(&peer->service_conn_lock, &seq); + + p = rcu_dereference_raw(peer->service_conns.rb_node); + while (p) { + conn = rb_entry(p, struct rxrpc_connection, service_node); + + if (conn->proto.index_key < k.index_key) + p = rcu_dereference_raw(p->rb_left); + else if (conn->proto.index_key > k.index_key) + p = rcu_dereference_raw(p->rb_right); + else + goto done; + conn = NULL; + } + } while (need_seqretry(&peer->service_conn_lock, seq)); + +done: + done_seqretry(&peer->service_conn_lock, seq); + _leave(" = %d", conn ? conn->debug_id : -1); + return conn; +} + +/* + * Insert a service connection into a peer's tree, thereby making it a target + * for incoming packets. + */ +static struct rxrpc_connection * +rxrpc_publish_service_conn(struct rxrpc_peer *peer, + struct rxrpc_connection *conn) +{ + struct rxrpc_connection *cursor = NULL; + struct rxrpc_conn_proto k = conn->proto; + struct rb_node **pp, *parent; + + write_seqlock_bh(&peer->service_conn_lock); + + pp = &peer->service_conns.rb_node; + parent = NULL; + while (*pp) { + parent = *pp; + cursor = rb_entry(parent, + struct rxrpc_connection, service_node); + + if (cursor->proto.index_key < k.index_key) + pp = &(*pp)->rb_left; + else if (cursor->proto.index_key > k.index_key) + pp = &(*pp)->rb_right; + else + goto found_extant_conn; + } + + rb_link_node_rcu(&conn->service_node, parent, pp); + rb_insert_color(&conn->service_node, &peer->service_conns); +conn_published: + set_bit(RXRPC_CONN_IN_SERVICE_CONNS, &conn->flags); + write_sequnlock_bh(&peer->service_conn_lock); + _leave(" = %d [new]", conn->debug_id); + return conn; + +found_extant_conn: + if (atomic_read(&cursor->usage) == 0) + goto replace_old_connection; + write_sequnlock_bh(&peer->service_conn_lock); + /* We should not be able to get here. rxrpc_incoming_connection() is + * called in a non-reentrant context, so there can't be a race to + * insert a new connection. + */ + BUG(); + +replace_old_connection: + /* The old connection is from an outdated epoch. */ + _debug("replace conn"); + rb_replace_node_rcu(&cursor->service_node, + &conn->service_node, + &peer->service_conns); + clear_bit(RXRPC_CONN_IN_SERVICE_CONNS, &cursor->flags); + goto conn_published; +} + +/* + * get a record of an incoming connection + */ +struct rxrpc_connection *rxrpc_incoming_connection(struct rxrpc_local *local, + struct sockaddr_rxrpc *srx, + struct sk_buff *skb) +{ + struct rxrpc_connection *conn; + struct rxrpc_skb_priv *sp = rxrpc_skb(skb); + struct rxrpc_peer *peer; + const char *new = "old"; + + _enter(""); + + peer = rxrpc_lookup_peer(local, srx, GFP_NOIO); + if (!peer) { + _debug("no peer"); + return ERR_PTR(-EBUSY); + } + + ASSERT(sp->hdr.flags & RXRPC_CLIENT_INITIATED); + + rcu_read_lock(); + peer = rxrpc_lookup_peer_rcu(local, srx); + if (peer) { + conn = rxrpc_find_service_conn_rcu(peer, skb); + if (conn) { + if (sp->hdr.securityIndex != conn->security_ix) + goto security_mismatch_rcu; + if (rxrpc_get_connection_maybe(conn)) + goto found_extant_connection_rcu; + + /* The conn has expired but we can't remove it without + * the appropriate lock, so we attempt to replace it + * when we have a new candidate. + */ + } + + if (!rxrpc_get_peer_maybe(peer)) + peer = NULL; + } + rcu_read_unlock(); + + if (!peer) { + peer = rxrpc_lookup_peer(local, srx, GFP_NOIO); + if (IS_ERR(peer)) + goto enomem; + } + + /* We don't have a matching record yet. */ + conn = rxrpc_alloc_connection(GFP_NOIO); + if (!conn) + goto enomem_peer; + + conn->proto.epoch = sp->hdr.epoch; + conn->proto.cid = sp->hdr.cid & RXRPC_CIDMASK; + conn->params.local = local; + conn->params.peer = peer; + conn->params.service_id = sp->hdr.serviceId; + conn->security_ix = sp->hdr.securityIndex; + conn->out_clientflag = 0; + conn->state = RXRPC_CONN_SERVICE; + if (conn->params.service_id) + conn->state = RXRPC_CONN_SERVICE_UNSECURED; + + rxrpc_get_local(local); + + write_lock(&rxrpc_connection_lock); + list_add_tail(&conn->link, &rxrpc_connections); + write_unlock(&rxrpc_connection_lock); + + /* Make the connection a target for incoming packets. */ + rxrpc_publish_service_conn(peer, conn); + + new = "new"; + +success: + _net("CONNECTION %s %d {%x}", new, conn->debug_id, conn->proto.cid); + _leave(" = %p {u=%d}", conn, atomic_read(&conn->usage)); + return conn; + +found_extant_connection_rcu: + rcu_read_unlock(); + goto success; + +security_mismatch_rcu: + rcu_read_unlock(); + _leave(" = -EKEYREJECTED"); + return ERR_PTR(-EKEYREJECTED); + +enomem_peer: + rxrpc_put_peer(peer); +enomem: + _leave(" = -ENOMEM"); + return ERR_PTR(-ENOMEM); +} + +/* + * Remove the service connection from the peer's tree, thereby removing it as a + * target for incoming packets. + */ +void rxrpc_unpublish_service_conn(struct rxrpc_connection *conn) +{ + struct rxrpc_peer *peer = conn->params.peer; + + write_seqlock_bh(&peer->service_conn_lock); + if (test_and_clear_bit(RXRPC_CONN_IN_SERVICE_CONNS, &conn->flags)) + rb_erase(&conn->service_node, &peer->service_conns); + write_sequnlock_bh(&peer->service_conn_lock); +} diff --git a/net/rxrpc/input.c b/net/rxrpc/input.c index f4bd57b77b93..991a20d25093 100644 --- a/net/rxrpc/input.c +++ b/net/rxrpc/input.c @@ -476,7 +476,7 @@ static void rxrpc_process_jumbo_packet(struct rxrpc_call *call, sp->hdr.seq += 1; sp->hdr.serial += 1; sp->hdr.flags = jhdr.flags; - sp->hdr._rsvd = jhdr._rsvd; + sp->hdr._rsvd = ntohs(jhdr._rsvd); _proto("Rx DATA Jumbo %%%u", sp->hdr.serial - 1); @@ -575,14 +575,13 @@ done: * post connection-level events to the connection * - this includes challenges, responses and some aborts */ -static void rxrpc_post_packet_to_conn(struct rxrpc_connection *conn, +static bool rxrpc_post_packet_to_conn(struct rxrpc_connection *conn, struct sk_buff *skb) { _enter("%p,%p", conn, skb); - rxrpc_get_connection(conn); skb_queue_tail(&conn->rx_queue, skb); - rxrpc_queue_conn(conn); + return rxrpc_queue_conn(conn); } /* @@ -595,7 +594,7 @@ static void rxrpc_post_packet_to_local(struct rxrpc_local *local, _enter("%p,%p", local, skb); skb_queue_tail(&local->event_queue, skb); - rxrpc_queue_work(&local->processor); + rxrpc_queue_local(local); } /* @@ -627,32 +626,6 @@ int rxrpc_extract_header(struct rxrpc_skb_priv *sp, struct sk_buff *skb) return 0; } -static struct rxrpc_connection *rxrpc_conn_from_local(struct rxrpc_local *local, - struct sk_buff *skb) -{ - struct rxrpc_peer *peer; - struct rxrpc_connection *conn; - struct sockaddr_rxrpc srx; - - rxrpc_get_addr_from_skb(local, skb, &srx); - rcu_read_lock(); - peer = rxrpc_lookup_peer_rcu(local, &srx); - if (!peer) - goto cant_find_peer; - - conn = rxrpc_find_connection(local, peer, skb); - rcu_read_unlock(); - if (!conn) - goto cant_find_conn; - - return conn; - -cant_find_peer: - rcu_read_unlock(); -cant_find_conn: - return NULL; -} - /* * handle data received on the local endpoint * - may be called in interrupt context @@ -663,6 +636,7 @@ cant_find_conn: */ void rxrpc_data_ready(struct sock *sk) { + struct rxrpc_connection *conn; struct rxrpc_skb_priv *sp; struct rxrpc_local *local = sk->sk_user_data; struct sk_buff *skb; @@ -726,34 +700,37 @@ void rxrpc_data_ready(struct sock *sk) (sp->hdr.callNumber == 0 || sp->hdr.seq == 0)) goto bad_message; - if (sp->hdr.callNumber == 0) { - /* This is a connection-level packet. These should be - * fairly rare, so the extra overhead of looking them up the - * old-fashioned way doesn't really hurt */ - struct rxrpc_connection *conn; + rcu_read_lock(); - conn = rxrpc_conn_from_local(local, skb); - if (!conn) - goto cant_route_call; +retry_find_conn: + conn = rxrpc_find_connection_rcu(local, skb); + if (!conn) + goto cant_route_call; + if (sp->hdr.callNumber == 0) { + /* Connection-level packet */ _debug("CONN %p {%d}", conn, conn->debug_id); - rxrpc_post_packet_to_conn(conn, skb); - rxrpc_put_connection(conn); + if (!rxrpc_post_packet_to_conn(conn, skb)) + goto retry_find_conn; } else { - struct rxrpc_call *call; + /* Call-bound packets are routed by connection channel. */ + unsigned int channel = sp->hdr.cid & RXRPC_CHANNELMASK; + struct rxrpc_channel *chan = &conn->channels[channel]; + struct rxrpc_call *call = rcu_dereference(chan->call); - call = rxrpc_find_call_hash(&sp->hdr, local, - AF_INET, &ip_hdr(skb)->saddr); - if (call) - rxrpc_post_packet_to_call(call, skb); - else + if (!call || atomic_read(&call->usage) == 0) goto cant_route_call; + + rxrpc_post_packet_to_call(call, skb); } + rcu_read_unlock(); out: return; cant_route_call: + rcu_read_unlock(); + _debug("can't route call"); if (sp->hdr.flags & RXRPC_CLIENT_INITIATED && sp->hdr.type == RXRPC_PACKET_TYPE_DATA) { diff --git a/net/rxrpc/insecure.c b/net/rxrpc/insecure.c index e571403613c1..c21ad213b337 100644 --- a/net/rxrpc/insecure.c +++ b/net/rxrpc/insecure.c @@ -17,11 +17,12 @@ static int none_init_connection_security(struct rxrpc_connection *conn) return 0; } -static void none_prime_packet_security(struct rxrpc_connection *conn) +static int none_prime_packet_security(struct rxrpc_connection *conn) { + return 0; } -static int none_secure_packet(const struct rxrpc_call *call, +static int none_secure_packet(struct rxrpc_call *call, struct sk_buff *skb, size_t data_size, void *sechdr) @@ -29,7 +30,7 @@ static int none_secure_packet(const struct rxrpc_call *call, return 0; } -static int none_verify_packet(const struct rxrpc_call *call, +static int none_verify_packet(struct rxrpc_call *call, struct sk_buff *skb, u32 *_abort_code) { diff --git a/net/rxrpc/local_object.c b/net/rxrpc/local_object.c index 3ab7764f7cd8..a753796fbe8f 100644 --- a/net/rxrpc/local_object.c +++ b/net/rxrpc/local_object.c @@ -374,14 +374,17 @@ void __exit rxrpc_destroy_all_locals(void) _enter(""); - if (list_empty(&rxrpc_local_endpoints)) - return; + flush_workqueue(rxrpc_workqueue); - mutex_lock(&rxrpc_local_mutex); - list_for_each_entry(local, &rxrpc_local_endpoints, link) { - pr_err("AF_RXRPC: Leaked local %p {%d}\n", - local, atomic_read(&local->usage)); + if (!list_empty(&rxrpc_local_endpoints)) { + mutex_lock(&rxrpc_local_mutex); + list_for_each_entry(local, &rxrpc_local_endpoints, link) { + pr_err("AF_RXRPC: Leaked local %p {%d}\n", + local, atomic_read(&local->usage)); + } + mutex_unlock(&rxrpc_local_mutex); + BUG(); } - mutex_unlock(&rxrpc_local_mutex); - BUG(); + + rcu_barrier(); } diff --git a/net/rxrpc/peer_object.c b/net/rxrpc/peer_object.c index 01d4930a11f7..538e9831c699 100644 --- a/net/rxrpc/peer_object.c +++ b/net/rxrpc/peer_object.c @@ -189,7 +189,7 @@ struct rxrpc_peer *rxrpc_alloc_peer(struct rxrpc_local *local, gfp_t gfp) INIT_WORK(&peer->error_distributor, &rxrpc_peer_error_distributor); peer->service_conns = RB_ROOT; - rwlock_init(&peer->conn_lock); + seqlock_init(&peer->service_conn_lock); spin_lock_init(&peer->lock); peer->debug_id = atomic_inc_return(&rxrpc_debug_id); } diff --git a/net/rxrpc/proc.c b/net/rxrpc/proc.c index 500cdcdc843c..ced5f07444e5 100644 --- a/net/rxrpc/proc.c +++ b/net/rxrpc/proc.c @@ -14,15 +14,15 @@ #include <net/af_rxrpc.h> #include "ar-internal.h" -static const char *const rxrpc_conn_states[] = { - [RXRPC_CONN_UNUSED] = "Unused ", - [RXRPC_CONN_CLIENT] = "Client ", - [RXRPC_CONN_SERVER_UNSECURED] = "SvUnsec ", - [RXRPC_CONN_SERVER_CHALLENGING] = "SvChall ", - [RXRPC_CONN_SERVER] = "SvSecure", - [RXRPC_CONN_REMOTELY_ABORTED] = "RmtAbort", - [RXRPC_CONN_LOCALLY_ABORTED] = "LocAbort", - [RXRPC_CONN_NETWORK_ERROR] = "NetError", +static const char *const rxrpc_conn_states[RXRPC_CONN__NR_STATES] = { + [RXRPC_CONN_UNUSED] = "Unused ", + [RXRPC_CONN_CLIENT] = "Client ", + [RXRPC_CONN_SERVICE_UNSECURED] = "SvUnsec ", + [RXRPC_CONN_SERVICE_CHALLENGING] = "SvChall ", + [RXRPC_CONN_SERVICE] = "SvSecure", + [RXRPC_CONN_REMOTELY_ABORTED] = "RmtAbort", + [RXRPC_CONN_LOCALLY_ABORTED] = "LocAbort", + [RXRPC_CONN_NETWORK_ERROR] = "NetError", }; /* @@ -137,7 +137,7 @@ static int rxrpc_connection_seq_show(struct seq_file *seq, void *v) if (v == &rxrpc_connections) { seq_puts(seq, "Proto Local Remote " - " SvID ConnID Calls End Use State Key " + " SvID ConnID End Use State Key " " Serial ISerial\n" ); return 0; @@ -154,13 +154,12 @@ static int rxrpc_connection_seq_show(struct seq_file *seq, void *v) ntohs(conn->params.peer->srx.transport.sin.sin_port)); seq_printf(seq, - "UDP %-22.22s %-22.22s %4x %08x %08x %s %3u" + "UDP %-22.22s %-22.22s %4x %08x %s %3u" " %s %08x %08x %08x\n", lbuff, rbuff, conn->params.service_id, conn->proto.cid, - conn->call_counter, rxrpc_conn_is_service(conn) ? "Svc" : "Clt", atomic_read(&conn->usage), rxrpc_conn_states[conn->state], diff --git a/net/rxrpc/rxkad.c b/net/rxrpc/rxkad.c index 23c05ec6fa28..63afa9e9cc08 100644 --- a/net/rxrpc/rxkad.c +++ b/net/rxrpc/rxkad.c @@ -103,43 +103,43 @@ error: * prime the encryption state with the invariant parts of a connection's * description */ -static void rxkad_prime_packet_security(struct rxrpc_connection *conn) +static int rxkad_prime_packet_security(struct rxrpc_connection *conn) { struct rxrpc_key_token *token; SKCIPHER_REQUEST_ON_STACK(req, conn->cipher); - struct scatterlist sg[2]; + struct scatterlist sg; struct rxrpc_crypt iv; - struct { - __be32 x[4]; - } tmpbuf __attribute__((aligned(16))); /* must all be in same page */ + __be32 *tmpbuf; + size_t tmpsize = 4 * sizeof(__be32); _enter(""); if (!conn->params.key) - return; + return 0; + + tmpbuf = kmalloc(tmpsize, GFP_KERNEL); + if (!tmpbuf) + return -ENOMEM; token = conn->params.key->payload.data[0]; memcpy(&iv, token->kad->session_key, sizeof(iv)); - tmpbuf.x[0] = htonl(conn->proto.epoch); - tmpbuf.x[1] = htonl(conn->proto.cid); - tmpbuf.x[2] = 0; - tmpbuf.x[3] = htonl(conn->security_ix); - - sg_init_one(&sg[0], &tmpbuf, sizeof(tmpbuf)); - sg_init_one(&sg[1], &tmpbuf, sizeof(tmpbuf)); + tmpbuf[0] = htonl(conn->proto.epoch); + tmpbuf[1] = htonl(conn->proto.cid); + tmpbuf[2] = 0; + tmpbuf[3] = htonl(conn->security_ix); + sg_init_one(&sg, tmpbuf, tmpsize); skcipher_request_set_tfm(req, conn->cipher); skcipher_request_set_callback(req, 0, NULL, NULL); - skcipher_request_set_crypt(req, &sg[1], &sg[0], sizeof(tmpbuf), iv.x); - + skcipher_request_set_crypt(req, &sg, &sg, tmpsize, iv.x); crypto_skcipher_encrypt(req); skcipher_request_zero(req); - memcpy(&conn->csum_iv, &tmpbuf.x[2], sizeof(conn->csum_iv)); - ASSERTCMP((u32 __force)conn->csum_iv.n[0], ==, (u32 __force)tmpbuf.x[2]); - - _leave(""); + memcpy(&conn->csum_iv, tmpbuf + 2, sizeof(conn->csum_iv)); + kfree(tmpbuf); + _leave(" = 0"); + return 0; } /* @@ -152,12 +152,9 @@ static int rxkad_secure_packet_auth(const struct rxrpc_call *call, { struct rxrpc_skb_priv *sp; SKCIPHER_REQUEST_ON_STACK(req, call->conn->cipher); + struct rxkad_level1_hdr hdr; struct rxrpc_crypt iv; - struct scatterlist sg[2]; - struct { - struct rxkad_level1_hdr hdr; - __be32 first; /* first four bytes of data and padding */ - } tmpbuf __attribute__((aligned(8))); /* must all be in same page */ + struct scatterlist sg; u16 check; sp = rxrpc_skb(skb); @@ -167,24 +164,19 @@ static int rxkad_secure_packet_auth(const struct rxrpc_call *call, check = sp->hdr.seq ^ sp->hdr.callNumber; data_size |= (u32)check << 16; - tmpbuf.hdr.data_size = htonl(data_size); - memcpy(&tmpbuf.first, sechdr + 4, sizeof(tmpbuf.first)); + hdr.data_size = htonl(data_size); + memcpy(sechdr, &hdr, sizeof(hdr)); /* start the encryption afresh */ memset(&iv, 0, sizeof(iv)); - sg_init_one(&sg[0], &tmpbuf, sizeof(tmpbuf)); - sg_init_one(&sg[1], &tmpbuf, sizeof(tmpbuf)); - + sg_init_one(&sg, sechdr, 8); skcipher_request_set_tfm(req, call->conn->cipher); skcipher_request_set_callback(req, 0, NULL, NULL); - skcipher_request_set_crypt(req, &sg[1], &sg[0], sizeof(tmpbuf), iv.x); - + skcipher_request_set_crypt(req, &sg, &sg, 8, iv.x); crypto_skcipher_encrypt(req); skcipher_request_zero(req); - memcpy(sechdr, &tmpbuf, sizeof(tmpbuf)); - _leave(" = 0"); return 0; } @@ -198,8 +190,7 @@ static int rxkad_secure_packet_encrypt(const struct rxrpc_call *call, void *sechdr) { const struct rxrpc_key_token *token; - struct rxkad_level2_hdr rxkhdr - __attribute__((aligned(8))); /* must be all on one page */ + struct rxkad_level2_hdr rxkhdr; struct rxrpc_skb_priv *sp; SKCIPHER_REQUEST_ON_STACK(req, call->conn->cipher); struct rxrpc_crypt iv; @@ -218,18 +209,16 @@ static int rxkad_secure_packet_encrypt(const struct rxrpc_call *call, rxkhdr.data_size = htonl(data_size | (u32)check << 16); rxkhdr.checksum = 0; + memcpy(sechdr, &rxkhdr, sizeof(rxkhdr)); /* encrypt from the session key */ token = call->conn->params.key->payload.data[0]; memcpy(&iv, token->kad->session_key, sizeof(iv)); sg_init_one(&sg[0], sechdr, sizeof(rxkhdr)); - sg_init_one(&sg[1], &rxkhdr, sizeof(rxkhdr)); - skcipher_request_set_tfm(req, call->conn->cipher); skcipher_request_set_callback(req, 0, NULL, NULL); - skcipher_request_set_crypt(req, &sg[1], &sg[0], sizeof(rxkhdr), iv.x); - + skcipher_request_set_crypt(req, &sg[0], &sg[0], sizeof(rxkhdr), iv.x); crypto_skcipher_encrypt(req); /* we want to encrypt the skbuff in-place */ @@ -243,9 +232,7 @@ static int rxkad_secure_packet_encrypt(const struct rxrpc_call *call, sg_init_table(sg, nsg); skb_to_sgvec(skb, sg, 0, len); - skcipher_request_set_crypt(req, sg, sg, len, iv.x); - crypto_skcipher_encrypt(req); _leave(" = 0"); @@ -259,7 +246,7 @@ out: /* * checksum an RxRPC packet header */ -static int rxkad_secure_packet(const struct rxrpc_call *call, +static int rxkad_secure_packet(struct rxrpc_call *call, struct sk_buff *skb, size_t data_size, void *sechdr) @@ -267,10 +254,7 @@ static int rxkad_secure_packet(const struct rxrpc_call *call, struct rxrpc_skb_priv *sp; SKCIPHER_REQUEST_ON_STACK(req, call->conn->cipher); struct rxrpc_crypt iv; - struct scatterlist sg[2]; - struct { - __be32 x[2]; - } tmpbuf __attribute__((aligned(8))); /* must all be in same page */ + struct scatterlist sg; u32 x, y; int ret; @@ -293,20 +277,17 @@ static int rxkad_secure_packet(const struct rxrpc_call *call, /* calculate the security checksum */ x = call->channel << (32 - RXRPC_CIDSHIFT); x |= sp->hdr.seq & 0x3fffffff; - tmpbuf.x[0] = htonl(sp->hdr.callNumber); - tmpbuf.x[1] = htonl(x); - - sg_init_one(&sg[0], &tmpbuf, sizeof(tmpbuf)); - sg_init_one(&sg[1], &tmpbuf, sizeof(tmpbuf)); + call->crypto_buf[0] = htonl(sp->hdr.callNumber); + call->crypto_buf[1] = htonl(x); + sg_init_one(&sg, call->crypto_buf, 8); skcipher_request_set_tfm(req, call->conn->cipher); skcipher_request_set_callback(req, 0, NULL, NULL); - skcipher_request_set_crypt(req, &sg[1], &sg[0], sizeof(tmpbuf), iv.x); - + skcipher_request_set_crypt(req, &sg, &sg, 8, iv.x); crypto_skcipher_encrypt(req); skcipher_request_zero(req); - y = ntohl(tmpbuf.x[1]); + y = ntohl(call->crypto_buf[1]); y = (y >> 16) & 0xffff; if (y == 0) y = 1; /* zero checksums are not permitted */ @@ -367,7 +348,6 @@ static int rxkad_verify_packet_auth(const struct rxrpc_call *call, skcipher_request_set_tfm(req, call->conn->cipher); skcipher_request_set_callback(req, 0, NULL, NULL); skcipher_request_set_crypt(req, sg, sg, 8, iv.x); - crypto_skcipher_decrypt(req); skcipher_request_zero(req); @@ -452,7 +432,6 @@ static int rxkad_verify_packet_encrypt(const struct rxrpc_call *call, skcipher_request_set_tfm(req, call->conn->cipher); skcipher_request_set_callback(req, 0, NULL, NULL); skcipher_request_set_crypt(req, sg, sg, skb->len, iv.x); - crypto_skcipher_decrypt(req); skcipher_request_zero(req); if (sg != _sg) @@ -498,17 +477,14 @@ nomem: /* * verify the security on a received packet */ -static int rxkad_verify_packet(const struct rxrpc_call *call, +static int rxkad_verify_packet(struct rxrpc_call *call, struct sk_buff *skb, u32 *_abort_code) { SKCIPHER_REQUEST_ON_STACK(req, call->conn->cipher); struct rxrpc_skb_priv *sp; struct rxrpc_crypt iv; - struct scatterlist sg[2]; - struct { - __be32 x[2]; - } tmpbuf __attribute__((aligned(8))); /* must all be in same page */ + struct scatterlist sg; u16 cksum; u32 x, y; int ret; @@ -533,20 +509,17 @@ static int rxkad_verify_packet(const struct rxrpc_call *call, /* validate the security checksum */ x = call->channel << (32 - RXRPC_CIDSHIFT); x |= sp->hdr.seq & 0x3fffffff; - tmpbuf.x[0] = htonl(call->call_id); - tmpbuf.x[1] = htonl(x); - - sg_init_one(&sg[0], &tmpbuf, sizeof(tmpbuf)); - sg_init_one(&sg[1], &tmpbuf, sizeof(tmpbuf)); + call->crypto_buf[0] = htonl(call->call_id); + call->crypto_buf[1] = htonl(x); + sg_init_one(&sg, call->crypto_buf, 8); skcipher_request_set_tfm(req, call->conn->cipher); skcipher_request_set_callback(req, 0, NULL, NULL); - skcipher_request_set_crypt(req, &sg[1], &sg[0], sizeof(tmpbuf), iv.x); - + skcipher_request_set_crypt(req, &sg, &sg, 8, iv.x); crypto_skcipher_encrypt(req); skcipher_request_zero(req); - y = ntohl(tmpbuf.x[1]); + y = ntohl(call->crypto_buf[1]); cksum = (y >> 16) & 0xffff; if (cksum == 0) cksum = 1; /* zero checksums are not permitted */ @@ -710,29 +683,6 @@ static void rxkad_calc_response_checksum(struct rxkad_response *response) } /* - * load a scatterlist with a potentially split-page buffer - */ -static void rxkad_sg_set_buf2(struct scatterlist sg[2], - void *buf, size_t buflen) -{ - int nsg = 1; - - sg_init_table(sg, 2); - - sg_set_buf(&sg[0], buf, buflen); - if (sg[0].offset + buflen > PAGE_SIZE) { - /* the buffer was split over two pages */ - sg[0].length = PAGE_SIZE - sg[0].offset; - sg_set_buf(&sg[1], buf + sg[0].length, buflen - sg[0].length); - nsg++; - } - - sg_mark_end(&sg[nsg - 1]); - - ASSERTCMP(sg[0].length + sg[1].length, ==, buflen); -} - -/* * encrypt the response packet */ static void rxkad_encrypt_response(struct rxrpc_connection *conn, @@ -741,17 +691,16 @@ static void rxkad_encrypt_response(struct rxrpc_connection *conn, { SKCIPHER_REQUEST_ON_STACK(req, conn->cipher); struct rxrpc_crypt iv; - struct scatterlist sg[2]; + struct scatterlist sg[1]; /* continue encrypting from where we left off */ memcpy(&iv, s2->session_key, sizeof(iv)); - rxkad_sg_set_buf2(sg, &resp->encrypted, sizeof(resp->encrypted)); - + sg_init_table(sg, 1); + sg_set_buf(sg, &resp->encrypted, sizeof(resp->encrypted)); skcipher_request_set_tfm(req, conn->cipher); skcipher_request_set_callback(req, 0, NULL, NULL); skcipher_request_set_crypt(req, sg, sg, sizeof(resp->encrypted), iv.x); - crypto_skcipher_encrypt(req); skcipher_request_zero(req); } @@ -818,14 +767,10 @@ static int rxkad_respond_to_challenge(struct rxrpc_connection *conn, resp.kvno = htonl(token->kad->kvno); resp.ticket_len = htonl(token->kad->ticket_len); - resp.encrypted.call_id[0] = - htonl(conn->channels[0] ? conn->channels[0]->call_id : 0); - resp.encrypted.call_id[1] = - htonl(conn->channels[1] ? conn->channels[1]->call_id : 0); - resp.encrypted.call_id[2] = - htonl(conn->channels[2] ? conn->channels[2]->call_id : 0); - resp.encrypted.call_id[3] = - htonl(conn->channels[3] ? conn->channels[3]->call_id : 0); + resp.encrypted.call_id[0] = htonl(conn->channels[0].call_counter); + resp.encrypted.call_id[1] = htonl(conn->channels[1].call_counter); + resp.encrypted.call_id[2] = htonl(conn->channels[2].call_counter); + resp.encrypted.call_id[3] = htonl(conn->channels[3].call_counter); /* calculate the response checksum and then do the encryption */ rxkad_calc_response_checksum(&resp); @@ -887,10 +832,8 @@ static int rxkad_decrypt_ticket(struct rxrpc_connection *conn, } sg_init_one(&sg[0], ticket, ticket_len); - skcipher_request_set_callback(req, 0, NULL, NULL); skcipher_request_set_crypt(req, sg, sg, ticket_len, iv.x); - crypto_skcipher_decrypt(req); skcipher_request_free(req); @@ -1001,7 +944,7 @@ static void rxkad_decrypt_response(struct rxrpc_connection *conn, const struct rxrpc_crypt *session_key) { SKCIPHER_REQUEST_ON_STACK(req, rxkad_ci); - struct scatterlist sg[2]; + struct scatterlist sg[1]; struct rxrpc_crypt iv; _enter(",,%08x%08x", @@ -1016,12 +959,11 @@ static void rxkad_decrypt_response(struct rxrpc_connection *conn, memcpy(&iv, session_key, sizeof(iv)); - rxkad_sg_set_buf2(sg, &resp->encrypted, sizeof(resp->encrypted)); - + sg_init_table(sg, 1); + sg_set_buf(sg, &resp->encrypted, sizeof(resp->encrypted)); skcipher_request_set_tfm(req, rxkad_ci); skcipher_request_set_callback(req, 0, NULL, NULL); skcipher_request_set_crypt(req, sg, sg, sizeof(resp->encrypted), iv.x); - crypto_skcipher_decrypt(req); skcipher_request_zero(req); @@ -1045,7 +987,7 @@ static int rxkad_verify_response(struct rxrpc_connection *conn, void *ticket; u32 abort_code, version, kvno, ticket_len, level; __be32 csum; - int ret; + int ret, i; _enter("{%d,%x}", conn->debug_id, key_serial(conn->server_key)); @@ -1108,11 +1050,26 @@ static int rxkad_verify_response(struct rxrpc_connection *conn, if (response.encrypted.checksum != csum) goto protocol_error_free; - if (ntohl(response.encrypted.call_id[0]) > INT_MAX || - ntohl(response.encrypted.call_id[1]) > INT_MAX || - ntohl(response.encrypted.call_id[2]) > INT_MAX || - ntohl(response.encrypted.call_id[3]) > INT_MAX) - goto protocol_error_free; + spin_lock(&conn->channel_lock); + for (i = 0; i < RXRPC_MAXCALLS; i++) { + struct rxrpc_call *call; + u32 call_id = ntohl(response.encrypted.call_id[i]); + + if (call_id > INT_MAX) + goto protocol_error_unlock; + + if (call_id < conn->channels[i].call_counter) + goto protocol_error_unlock; + if (call_id > conn->channels[i].call_counter) { + call = rcu_dereference_protected( + conn->channels[i].call, + lockdep_is_held(&conn->channel_lock)); + if (call && call->state < RXRPC_CALL_COMPLETE) + goto protocol_error_unlock; + conn->channels[i].call_counter = call_id; + } + } + spin_unlock(&conn->channel_lock); abort_code = RXKADOUTOFSEQUENCE; if (ntohl(response.encrypted.inc_nonce) != conn->security_nonce + 1) @@ -1137,6 +1094,8 @@ static int rxkad_verify_response(struct rxrpc_connection *conn, _leave(" = 0"); return 0; +protocol_error_unlock: + spin_unlock(&conn->channel_lock); protocol_error_free: kfree(ticket); protocol_error: diff --git a/net/rxrpc/utils.c b/net/rxrpc/utils.c index f28122a15a24..b88914d53ca5 100644 --- a/net/rxrpc/utils.c +++ b/net/rxrpc/utils.c @@ -10,32 +10,37 @@ */ #include <linux/ip.h> +#include <linux/ipv6.h> #include <linux/udp.h> #include "ar-internal.h" /* - * Set up an RxRPC address from a socket buffer. + * Fill out a peer address from a socket buffer containing a packet. */ -void rxrpc_get_addr_from_skb(struct rxrpc_local *local, - const struct sk_buff *skb, - struct sockaddr_rxrpc *srx) +int rxrpc_extract_addr_from_skb(struct sockaddr_rxrpc *srx, struct sk_buff *skb) { memset(srx, 0, sizeof(*srx)); - srx->transport_type = local->srx.transport_type; - srx->transport.family = local->srx.transport.family; - /* Can we see an ipv4 UDP packet on an ipv6 UDP socket? and vice - * versa? - */ - switch (srx->transport.family) { - case AF_INET: + switch (ntohs(skb->protocol)) { + case ETH_P_IP: + srx->transport_type = SOCK_DGRAM; + srx->transport_len = sizeof(srx->transport.sin); + srx->transport.sin.sin_family = AF_INET; srx->transport.sin.sin_port = udp_hdr(skb)->source; - srx->transport_len = sizeof(struct sockaddr_in); - memcpy(&srx->transport.sin.sin_addr, &ip_hdr(skb)->saddr, - sizeof(struct in_addr)); - break; + srx->transport.sin.sin_addr.s_addr = ip_hdr(skb)->saddr; + return 0; + + case ETH_P_IPV6: + srx->transport_type = SOCK_DGRAM; + srx->transport_len = sizeof(srx->transport.sin6); + srx->transport.sin6.sin6_family = AF_INET6; + srx->transport.sin6.sin6_port = udp_hdr(skb)->source; + srx->transport.sin6.sin6_addr = ipv6_hdr(skb)->saddr; + return 0; default: - BUG(); + pr_warn_ratelimited("AF_RXRPC: Unknown eth protocol %u\n", + ntohs(skb->protocol)); + return -EAFNOSUPPORT; } } |