2f96719a35149dddea23dfc32395927483b7b03f
[linux-block.git] / net / tipc / port.c
1 /*
2  * net/tipc/port.c: TIPC port code
3  *
4  * Copyright (c) 1992-2007, 2014, Ericsson AB
5  * Copyright (c) 2004-2008, 2010-2013, Wind River Systems
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the names of the copyright holders nor the names of its
17  *    contributors may be used to endorse or promote products derived from
18  *    this software without specific prior written permission.
19  *
20  * Alternatively, this software may be distributed under the terms of the
21  * GNU General Public License ("GPL") version 2 as published by the Free
22  * Software Foundation.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
28  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34  * POSSIBILITY OF SUCH DAMAGE.
35  */
36
37 #include "core.h"
38 #include "config.h"
39 #include "port.h"
40 #include "name_table.h"
41 #include "socket.h"
42
43 #define MAX_REJECT_SIZE 1024
44
45 DEFINE_SPINLOCK(tipc_port_list_lock);
46
47 static LIST_HEAD(ports);
48
49 /**
50  * tipc_port_peer_msg - verify message was sent by connected port's peer
51  *
52  * Handles cases where the node's network address has changed from
53  * the default of <0.0.0> to its configured setting.
54  */
55 int tipc_port_peer_msg(struct tipc_port *p_ptr, struct tipc_msg *msg)
56 {
57         u32 peernode;
58         u32 orignode;
59
60         if (msg_origport(msg) != tipc_port_peerport(p_ptr))
61                 return 0;
62
63         orignode = msg_orignode(msg);
64         peernode = tipc_port_peernode(p_ptr);
65         return (orignode == peernode) ||
66                 (!orignode && (peernode == tipc_own_addr)) ||
67                 (!peernode && (orignode == tipc_own_addr));
68 }
69
70 /* tipc_port_init - intiate TIPC port and lock it
71  *
72  * Returns obtained reference if initialization is successful, zero otherwise
73  */
74 u32 tipc_port_init(struct tipc_port *p_ptr,
75                    const unsigned int importance)
76 {
77         struct tipc_msg *msg;
78         u32 ref;
79
80         ref = tipc_ref_acquire(p_ptr, &p_ptr->lock);
81         if (!ref) {
82                 pr_warn("Port registration failed, ref. table exhausted\n");
83                 return 0;
84         }
85
86         p_ptr->max_pkt = MAX_PKT_DEFAULT;
87         p_ptr->ref = ref;
88         INIT_LIST_HEAD(&p_ptr->subscription.nodesub_list);
89         INIT_LIST_HEAD(&p_ptr->publications);
90         INIT_LIST_HEAD(&p_ptr->port_list);
91
92         /*
93          * Must hold port list lock while initializing message header template
94          * to ensure a change to node's own network address doesn't result
95          * in template containing out-dated network address information
96          */
97         spin_lock_bh(&tipc_port_list_lock);
98         msg = &p_ptr->phdr;
99         tipc_msg_init(msg, importance, TIPC_NAMED_MSG, NAMED_H_SIZE, 0);
100         msg_set_origport(msg, ref);
101         list_add_tail(&p_ptr->port_list, &ports);
102         spin_unlock_bh(&tipc_port_list_lock);
103         return ref;
104 }
105
106 void tipc_port_destroy(struct tipc_port *p_ptr)
107 {
108         struct sk_buff *buf = NULL;
109         struct tipc_msg *msg = NULL;
110         u32 peer_node;
111
112         tipc_withdraw(p_ptr, 0, NULL);
113
114         spin_lock_bh(p_ptr->lock);
115         tipc_ref_discard(p_ptr->ref);
116         spin_unlock_bh(p_ptr->lock);
117
118         k_cancel_timer(&p_ptr->timer);
119         if (p_ptr->connected) {
120                 peer_node = tipc_port_peernode(p_ptr);
121                 buf = tipc_msg_create(TIPC_CRITICAL_IMPORTANCE, TIPC_CONN_MSG,
122                                       SHORT_H_SIZE, 0, peer_node,
123                                       tipc_own_addr, tipc_port_peerport(p_ptr),
124                                       p_ptr->ref, TIPC_ERR_NO_PORT);
125                 if (buf) {
126                         msg = buf_msg(buf);
127                         tipc_link_xmit(buf, peer_node, msg_link_selector(msg));
128                 }
129                 tipc_node_remove_conn(peer_node, p_ptr->ref);
130         }
131         spin_lock_bh(&tipc_port_list_lock);
132         list_del(&p_ptr->port_list);
133         spin_unlock_bh(&tipc_port_list_lock);
134         k_term_timer(&p_ptr->timer);
135 }
136
137 static int port_print(struct tipc_port *p_ptr, char *buf, int len, int full_id)
138 {
139         struct publication *publ;
140         int ret;
141
142         if (full_id)
143                 ret = tipc_snprintf(buf, len, "<%u.%u.%u:%u>:",
144                                     tipc_zone(tipc_own_addr),
145                                     tipc_cluster(tipc_own_addr),
146                                     tipc_node(tipc_own_addr), p_ptr->ref);
147         else
148                 ret = tipc_snprintf(buf, len, "%-10u:", p_ptr->ref);
149
150         if (p_ptr->connected) {
151                 u32 dport = tipc_port_peerport(p_ptr);
152                 u32 destnode = tipc_port_peernode(p_ptr);
153
154                 ret += tipc_snprintf(buf + ret, len - ret,
155                                      " connected to <%u.%u.%u:%u>",
156                                      tipc_zone(destnode),
157                                      tipc_cluster(destnode),
158                                      tipc_node(destnode), dport);
159                 if (p_ptr->conn_type != 0)
160                         ret += tipc_snprintf(buf + ret, len - ret,
161                                              " via {%u,%u}", p_ptr->conn_type,
162                                              p_ptr->conn_instance);
163         } else if (p_ptr->published) {
164                 ret += tipc_snprintf(buf + ret, len - ret, " bound to");
165                 list_for_each_entry(publ, &p_ptr->publications, pport_list) {
166                         if (publ->lower == publ->upper)
167                                 ret += tipc_snprintf(buf + ret, len - ret,
168                                                      " {%u,%u}", publ->type,
169                                                      publ->lower);
170                         else
171                                 ret += tipc_snprintf(buf + ret, len - ret,
172                                                      " {%u,%u,%u}", publ->type,
173                                                      publ->lower, publ->upper);
174                 }
175         }
176         ret += tipc_snprintf(buf + ret, len - ret, "\n");
177         return ret;
178 }
179
180 struct sk_buff *tipc_port_get_ports(void)
181 {
182         struct sk_buff *buf;
183         struct tlv_desc *rep_tlv;
184         char *pb;
185         int pb_len;
186         struct tipc_port *p_ptr;
187         int str_len = 0;
188
189         buf = tipc_cfg_reply_alloc(TLV_SPACE(ULTRA_STRING_MAX_LEN));
190         if (!buf)
191                 return NULL;
192         rep_tlv = (struct tlv_desc *)buf->data;
193         pb = TLV_DATA(rep_tlv);
194         pb_len = ULTRA_STRING_MAX_LEN;
195
196         spin_lock_bh(&tipc_port_list_lock);
197         list_for_each_entry(p_ptr, &ports, port_list) {
198                 spin_lock_bh(p_ptr->lock);
199                 str_len += port_print(p_ptr, pb, pb_len, 0);
200                 spin_unlock_bh(p_ptr->lock);
201         }
202         spin_unlock_bh(&tipc_port_list_lock);
203         str_len += 1;   /* for "\0" */
204         skb_put(buf, TLV_SPACE(str_len));
205         TLV_SET(rep_tlv, TIPC_TLV_ULTRA_STRING, NULL, str_len);
206
207         return buf;
208 }
209
210 void tipc_port_reinit(void)
211 {
212         struct tipc_port *p_ptr;
213         struct tipc_msg *msg;
214
215         spin_lock_bh(&tipc_port_list_lock);
216         list_for_each_entry(p_ptr, &ports, port_list) {
217                 msg = &p_ptr->phdr;
218                 msg_set_prevnode(msg, tipc_own_addr);
219                 msg_set_orignode(msg, tipc_own_addr);
220         }
221         spin_unlock_bh(&tipc_port_list_lock);
222 }
223
224 void tipc_acknowledge(u32 ref, u32 ack)
225 {
226         struct tipc_port *p_ptr;
227         struct sk_buff *buf = NULL;
228         struct tipc_msg *msg;
229
230         p_ptr = tipc_port_lock(ref);
231         if (!p_ptr)
232                 return;
233         if (p_ptr->connected)
234                 buf = tipc_msg_create(CONN_MANAGER, CONN_ACK, INT_H_SIZE,
235                                       0, tipc_port_peernode(p_ptr),
236                                       tipc_own_addr, tipc_port_peerport(p_ptr),
237                                       p_ptr->ref, TIPC_OK);
238         tipc_port_unlock(p_ptr);
239         if (!buf)
240                 return;
241         msg = buf_msg(buf);
242         msg_set_msgcnt(msg, ack);
243         tipc_link_xmit(buf, msg_destnode(msg),  msg_link_selector(msg));
244 }
245
246 int tipc_publish(struct tipc_port *p_ptr, unsigned int scope,
247                  struct tipc_name_seq const *seq)
248 {
249         struct publication *publ;
250         u32 key;
251
252         if (p_ptr->connected)
253                 return -EINVAL;
254         key = p_ptr->ref + p_ptr->pub_count + 1;
255         if (key == p_ptr->ref)
256                 return -EADDRINUSE;
257
258         publ = tipc_nametbl_publish(seq->type, seq->lower, seq->upper,
259                                     scope, p_ptr->ref, key);
260         if (publ) {
261                 list_add(&publ->pport_list, &p_ptr->publications);
262                 p_ptr->pub_count++;
263                 p_ptr->published = 1;
264                 return 0;
265         }
266         return -EINVAL;
267 }
268
269 int tipc_withdraw(struct tipc_port *p_ptr, unsigned int scope,
270                   struct tipc_name_seq const *seq)
271 {
272         struct publication *publ;
273         struct publication *tpubl;
274         int res = -EINVAL;
275
276         if (!seq) {
277                 list_for_each_entry_safe(publ, tpubl,
278                                          &p_ptr->publications, pport_list) {
279                         tipc_nametbl_withdraw(publ->type, publ->lower,
280                                               publ->ref, publ->key);
281                 }
282                 res = 0;
283         } else {
284                 list_for_each_entry_safe(publ, tpubl,
285                                          &p_ptr->publications, pport_list) {
286                         if (publ->scope != scope)
287                                 continue;
288                         if (publ->type != seq->type)
289                                 continue;
290                         if (publ->lower != seq->lower)
291                                 continue;
292                         if (publ->upper != seq->upper)
293                                 break;
294                         tipc_nametbl_withdraw(publ->type, publ->lower,
295                                               publ->ref, publ->key);
296                         res = 0;
297                         break;
298                 }
299         }
300         if (list_empty(&p_ptr->publications))
301                 p_ptr->published = 0;
302         return res;
303 }