c6db2e8400a2844558531218c68f7d46cf3b3e7a
[cascardo/linux.git] / net / rxrpc / conn_service.c
1 /* Service connection management
2  *
3  * Copyright (C) 2016 Red Hat, Inc. All Rights Reserved.
4  * Written by David Howells (dhowells@redhat.com)
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public Licence
8  * as published by the Free Software Foundation; either version
9  * 2 of the Licence, or (at your option) any later version.
10  */
11
12 #include <linux/slab.h>
13 #include "ar-internal.h"
14
15 /*
16  * get a record of an incoming connection
17  */
18 struct rxrpc_connection *rxrpc_incoming_connection(struct rxrpc_local *local,
19                                                    struct sockaddr_rxrpc *srx,
20                                                    struct sk_buff *skb)
21 {
22         struct rxrpc_connection *conn, *candidate = NULL;
23         struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
24         struct rxrpc_peer *peer;
25         struct rb_node *p, **pp;
26         const char *new = "old";
27         u32 epoch, cid;
28
29         _enter("");
30
31         peer = rxrpc_lookup_peer(local, srx, GFP_NOIO);
32         if (!peer) {
33                 _debug("no peer");
34                 return ERR_PTR(-EBUSY);
35         }
36
37         ASSERT(sp->hdr.flags & RXRPC_CLIENT_INITIATED);
38
39         epoch = sp->hdr.epoch;
40         cid = sp->hdr.cid & RXRPC_CIDMASK;
41
42         /* search the connection list first */
43         read_lock_bh(&peer->conn_lock);
44
45         p = peer->service_conns.rb_node;
46         while (p) {
47                 conn = rb_entry(p, struct rxrpc_connection, service_node);
48
49                 _debug("maybe %x", conn->proto.cid);
50
51                 if (epoch < conn->proto.epoch)
52                         p = p->rb_left;
53                 else if (epoch > conn->proto.epoch)
54                         p = p->rb_right;
55                 else if (cid < conn->proto.cid)
56                         p = p->rb_left;
57                 else if (cid > conn->proto.cid)
58                         p = p->rb_right;
59                 else
60                         goto found_extant_connection;
61         }
62         read_unlock_bh(&peer->conn_lock);
63
64         /* not yet present - create a candidate for a new record and then
65          * redo the search */
66         candidate = rxrpc_alloc_connection(GFP_NOIO);
67         if (!candidate) {
68                 rxrpc_put_peer(peer);
69                 _leave(" = -ENOMEM");
70                 return ERR_PTR(-ENOMEM);
71         }
72
73         candidate->proto.epoch          = sp->hdr.epoch;
74         candidate->proto.cid            = sp->hdr.cid & RXRPC_CIDMASK;
75         candidate->params.local         = local;
76         candidate->params.peer          = peer;
77         candidate->params.service_id    = sp->hdr.serviceId;
78         candidate->security_ix          = sp->hdr.securityIndex;
79         candidate->out_clientflag       = 0;
80         candidate->state                = RXRPC_CONN_SERVICE;
81         if (candidate->params.service_id)
82                 candidate->state        = RXRPC_CONN_SERVICE_UNSECURED;
83
84         write_lock_bh(&peer->conn_lock);
85
86         pp = &peer->service_conns.rb_node;
87         p = NULL;
88         while (*pp) {
89                 p = *pp;
90                 conn = rb_entry(p, struct rxrpc_connection, service_node);
91
92                 if (epoch < conn->proto.epoch)
93                         pp = &(*pp)->rb_left;
94                 else if (epoch > conn->proto.epoch)
95                         pp = &(*pp)->rb_right;
96                 else if (cid < conn->proto.cid)
97                         pp = &(*pp)->rb_left;
98                 else if (cid > conn->proto.cid)
99                         pp = &(*pp)->rb_right;
100                 else
101                         goto found_extant_second;
102         }
103
104         /* we can now add the new candidate to the list */
105         set_bit(RXRPC_CONN_IN_SERVICE_CONNS, &candidate->flags);
106         rb_link_node(&candidate->service_node, p, pp);
107         rb_insert_color(&candidate->service_node, &peer->service_conns);
108 attached:
109         conn = candidate;
110         candidate = NULL;
111         rxrpc_get_peer(peer);
112         rxrpc_get_local(local);
113
114         write_unlock_bh(&peer->conn_lock);
115
116         write_lock(&rxrpc_connection_lock);
117         list_add_tail(&conn->link, &rxrpc_connections);
118         write_unlock(&rxrpc_connection_lock);
119
120         new = "new";
121
122 success:
123         _net("CONNECTION %s %d {%x}", new, conn->debug_id, conn->proto.cid);
124
125         rxrpc_put_peer(peer);
126         _leave(" = %p {u=%d}", conn, atomic_read(&conn->usage));
127         return conn;
128
129         /* we found the connection in the list immediately */
130 found_extant_connection:
131         if (!rxrpc_get_connection_maybe(conn)) {
132                 set_bit(RXRPC_CONN_IN_SERVICE_CONNS, &candidate->flags);
133                 rb_replace_node(&conn->service_node,
134                                 &candidate->service_node,
135                                 &peer->service_conns);
136                 clear_bit(RXRPC_CONN_IN_SERVICE_CONNS, &conn->flags);
137                 goto attached;
138         }
139
140         if (sp->hdr.securityIndex != conn->security_ix) {
141                 read_unlock_bh(&peer->conn_lock);
142                 goto security_mismatch_put;
143         }
144         read_unlock_bh(&peer->conn_lock);
145         goto success;
146
147         /* we found the connection on the second time through the list */
148 found_extant_second:
149         if (sp->hdr.securityIndex != conn->security_ix) {
150                 write_unlock_bh(&peer->conn_lock);
151                 goto security_mismatch;
152         }
153         rxrpc_get_connection(conn);
154         write_unlock_bh(&peer->conn_lock);
155         kfree(candidate);
156         goto success;
157
158 security_mismatch_put:
159         rxrpc_put_connection(conn);
160 security_mismatch:
161         kfree(candidate);
162         _leave(" = -EKEYREJECTED");
163         return ERR_PTR(-EKEYREJECTED);
164 }
165
166 /*
167  * Remove the service connection from the peer's tree, thereby removing it as a
168  * target for incoming packets.
169  */
170 void rxrpc_unpublish_service_conn(struct rxrpc_connection *conn)
171 {
172         struct rxrpc_peer *peer = conn->params.peer;
173
174         write_lock_bh(&peer->conn_lock);
175         if (test_and_clear_bit(RXRPC_CONN_IN_SERVICE_CONNS, &conn->flags))
176                 rb_erase(&conn->service_node, &peer->service_conns);
177         write_unlock_bh(&peer->conn_lock);
178 }