Commit | Line | Data |
---|---|---|
b97bf3fd PL |
1 | /* |
2 | * net/tipc/port.c: TIPC port code | |
c4307285 | 3 | * |
8826cde6 | 4 | * Copyright (c) 1992-2007, 2014, Ericsson AB |
198d73b8 | 5 | * Copyright (c) 2004-2008, 2010-2013, Wind River Systems |
b97bf3fd PL |
6 | * All rights reserved. |
7 | * | |
9ea1fd3c | 8 | * Redistribution and use in source and binary forms, with or without |
b97bf3fd PL |
9 | * modification, are permitted provided that the following conditions are met: |
10 | * | |
9ea1fd3c PL |
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. | |
b97bf3fd | 19 | * |
9ea1fd3c PL |
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 | |
b97bf3fd PL |
34 | * POSSIBILITY OF SUCH DAMAGE. |
35 | */ | |
36 | ||
37 | #include "core.h" | |
38 | #include "config.h" | |
b97bf3fd | 39 | #include "port.h" |
b97bf3fd | 40 | #include "name_table.h" |
8826cde6 | 41 | #include "socket.h" |
b97bf3fd PL |
42 | |
43 | /* Connection management: */ | |
44 | #define PROBING_INTERVAL 3600000 /* [ms] => 1 h */ | |
b97bf3fd PL |
45 | |
46 | #define MAX_REJECT_SIZE 1024 | |
47 | ||
34af946a | 48 | DEFINE_SPINLOCK(tipc_port_list_lock); |
b97bf3fd | 49 | |
4323add6 | 50 | static LIST_HEAD(ports); |
b97bf3fd | 51 | |
2c53040f | 52 | /** |
f0712e86 AS |
53 | * tipc_port_peer_msg - verify message was sent by connected port's peer |
54 | * | |
55 | * Handles cases where the node's network address has changed from | |
56 | * the default of <0.0.0> to its configured setting. | |
57 | */ | |
f0712e86 AS |
58 | int tipc_port_peer_msg(struct tipc_port *p_ptr, struct tipc_msg *msg) |
59 | { | |
60 | u32 peernode; | |
61 | u32 orignode; | |
62 | ||
f9fef18c | 63 | if (msg_origport(msg) != tipc_port_peerport(p_ptr)) |
f0712e86 AS |
64 | return 0; |
65 | ||
66 | orignode = msg_orignode(msg); | |
f9fef18c | 67 | peernode = tipc_port_peernode(p_ptr); |
f0712e86 AS |
68 | return (orignode == peernode) || |
69 | (!orignode && (peernode == tipc_own_addr)) || | |
70 | (!peernode && (orignode == tipc_own_addr)); | |
71 | } | |
72 | ||
24be34b5 | 73 | /* tipc_port_init - intiate TIPC port and lock it |
c4307285 | 74 | * |
24be34b5 | 75 | * Returns obtained reference if initialization is successful, zero otherwise |
b97bf3fd | 76 | */ |
24be34b5 JPM |
77 | u32 tipc_port_init(struct tipc_port *p_ptr, |
78 | const unsigned int importance) | |
b97bf3fd | 79 | { |
b97bf3fd PL |
80 | struct tipc_msg *msg; |
81 | u32 ref; | |
82 | ||
23dd4cce | 83 | ref = tipc_ref_acquire(p_ptr, &p_ptr->lock); |
b97bf3fd | 84 | if (!ref) { |
8826cde6 | 85 | pr_warn("Port registration failed, ref. table exhausted\n"); |
24be34b5 | 86 | return 0; |
b97bf3fd PL |
87 | } |
88 | ||
23dd4cce AS |
89 | p_ptr->max_pkt = MAX_PKT_DEFAULT; |
90 | p_ptr->ref = ref; | |
b97bf3fd | 91 | INIT_LIST_HEAD(&p_ptr->subscription.nodesub_list); |
b97bf3fd PL |
92 | INIT_LIST_HEAD(&p_ptr->publications); |
93 | INIT_LIST_HEAD(&p_ptr->port_list); | |
f21536d1 AS |
94 | |
95 | /* | |
96 | * Must hold port list lock while initializing message header template | |
97 | * to ensure a change to node's own network address doesn't result | |
98 | * in template containing out-dated network address information | |
99 | */ | |
f21536d1 AS |
100 | spin_lock_bh(&tipc_port_list_lock); |
101 | msg = &p_ptr->phdr; | |
102 | tipc_msg_init(msg, importance, TIPC_NAMED_MSG, NAMED_H_SIZE, 0); | |
103 | msg_set_origport(msg, ref); | |
b97bf3fd | 104 | list_add_tail(&p_ptr->port_list, &ports); |
4323add6 | 105 | spin_unlock_bh(&tipc_port_list_lock); |
24be34b5 | 106 | return ref; |
b97bf3fd PL |
107 | } |
108 | ||
24be34b5 | 109 | void tipc_port_destroy(struct tipc_port *p_ptr) |
b97bf3fd | 110 | { |
1fc54d8f | 111 | struct sk_buff *buf = NULL; |
b786e2b0 | 112 | struct tipc_msg *msg = NULL; |
57289015 | 113 | u32 peer_node; |
b97bf3fd | 114 | |
84602761 | 115 | tipc_withdraw(p_ptr, 0, NULL); |
b97bf3fd | 116 | |
84602761 YX |
117 | spin_lock_bh(p_ptr->lock); |
118 | tipc_ref_discard(p_ptr->ref); | |
119 | spin_unlock_bh(p_ptr->lock); | |
b97bf3fd PL |
120 | |
121 | k_cancel_timer(&p_ptr->timer); | |
23dd4cce | 122 | if (p_ptr->connected) { |
57289015 JPM |
123 | peer_node = tipc_port_peernode(p_ptr); |
124 | buf = tipc_msg_create(TIPC_CRITICAL_IMPORTANCE, TIPC_CONN_MSG, | |
125 | SHORT_H_SIZE, 0, peer_node, | |
126 | tipc_own_addr, tipc_port_peerport(p_ptr), | |
127 | p_ptr->ref, TIPC_ERR_NO_PORT); | |
128 | if (buf) { | |
129 | msg = buf_msg(buf); | |
130 | tipc_link_xmit(buf, peer_node, msg_link_selector(msg)); | |
131 | } | |
132 | tipc_node_remove_conn(peer_node, p_ptr->ref); | |
b97bf3fd | 133 | } |
4323add6 | 134 | spin_lock_bh(&tipc_port_list_lock); |
b97bf3fd | 135 | list_del(&p_ptr->port_list); |
4323add6 | 136 | spin_unlock_bh(&tipc_port_list_lock); |
b97bf3fd | 137 | k_term_timer(&p_ptr->timer); |
b97bf3fd PL |
138 | } |
139 | ||
dc1aed37 | 140 | static int port_print(struct tipc_port *p_ptr, char *buf, int len, int full_id) |
b97bf3fd | 141 | { |
c4307285 | 142 | struct publication *publ; |
dc1aed37 | 143 | int ret; |
b97bf3fd PL |
144 | |
145 | if (full_id) | |
dc1aed37 EH |
146 | ret = tipc_snprintf(buf, len, "<%u.%u.%u:%u>:", |
147 | tipc_zone(tipc_own_addr), | |
148 | tipc_cluster(tipc_own_addr), | |
149 | tipc_node(tipc_own_addr), p_ptr->ref); | |
b97bf3fd | 150 | else |
dc1aed37 | 151 | ret = tipc_snprintf(buf, len, "%-10u:", p_ptr->ref); |
b97bf3fd | 152 | |
23dd4cce | 153 | if (p_ptr->connected) { |
f9fef18c JPM |
154 | u32 dport = tipc_port_peerport(p_ptr); |
155 | u32 destnode = tipc_port_peernode(p_ptr); | |
c4307285 | 156 | |
dc1aed37 EH |
157 | ret += tipc_snprintf(buf + ret, len - ret, |
158 | " connected to <%u.%u.%u:%u>", | |
159 | tipc_zone(destnode), | |
160 | tipc_cluster(destnode), | |
161 | tipc_node(destnode), dport); | |
23dd4cce | 162 | if (p_ptr->conn_type != 0) |
dc1aed37 EH |
163 | ret += tipc_snprintf(buf + ret, len - ret, |
164 | " via {%u,%u}", p_ptr->conn_type, | |
165 | p_ptr->conn_instance); | |
23dd4cce | 166 | } else if (p_ptr->published) { |
dc1aed37 | 167 | ret += tipc_snprintf(buf + ret, len - ret, " bound to"); |
c4307285 | 168 | list_for_each_entry(publ, &p_ptr->publications, pport_list) { |
b97bf3fd | 169 | if (publ->lower == publ->upper) |
dc1aed37 EH |
170 | ret += tipc_snprintf(buf + ret, len - ret, |
171 | " {%u,%u}", publ->type, | |
172 | publ->lower); | |
b97bf3fd | 173 | else |
dc1aed37 EH |
174 | ret += tipc_snprintf(buf + ret, len - ret, |
175 | " {%u,%u,%u}", publ->type, | |
176 | publ->lower, publ->upper); | |
c4307285 YH |
177 | } |
178 | } | |
dc1aed37 EH |
179 | ret += tipc_snprintf(buf + ret, len - ret, "\n"); |
180 | return ret; | |
b97bf3fd PL |
181 | } |
182 | ||
4323add6 | 183 | struct sk_buff *tipc_port_get_ports(void) |
b97bf3fd PL |
184 | { |
185 | struct sk_buff *buf; | |
186 | struct tlv_desc *rep_tlv; | |
dc1aed37 EH |
187 | char *pb; |
188 | int pb_len; | |
23dd4cce | 189 | struct tipc_port *p_ptr; |
dc1aed37 | 190 | int str_len = 0; |
b97bf3fd | 191 | |
dc1aed37 | 192 | buf = tipc_cfg_reply_alloc(TLV_SPACE(ULTRA_STRING_MAX_LEN)); |
b97bf3fd PL |
193 | if (!buf) |
194 | return NULL; | |
195 | rep_tlv = (struct tlv_desc *)buf->data; | |
dc1aed37 EH |
196 | pb = TLV_DATA(rep_tlv); |
197 | pb_len = ULTRA_STRING_MAX_LEN; | |
b97bf3fd | 198 | |
4323add6 | 199 | spin_lock_bh(&tipc_port_list_lock); |
b97bf3fd | 200 | list_for_each_entry(p_ptr, &ports, port_list) { |
23dd4cce | 201 | spin_lock_bh(p_ptr->lock); |
dc1aed37 | 202 | str_len += port_print(p_ptr, pb, pb_len, 0); |
23dd4cce | 203 | spin_unlock_bh(p_ptr->lock); |
b97bf3fd | 204 | } |
4323add6 | 205 | spin_unlock_bh(&tipc_port_list_lock); |
dc1aed37 | 206 | str_len += 1; /* for "\0" */ |
b97bf3fd PL |
207 | skb_put(buf, TLV_SPACE(str_len)); |
208 | TLV_SET(rep_tlv, TIPC_TLV_ULTRA_STRING, NULL, str_len); | |
209 | ||
210 | return buf; | |
211 | } | |
212 | ||
4323add6 | 213 | void tipc_port_reinit(void) |
b97bf3fd | 214 | { |
23dd4cce | 215 | struct tipc_port *p_ptr; |
b97bf3fd PL |
216 | struct tipc_msg *msg; |
217 | ||
4323add6 | 218 | spin_lock_bh(&tipc_port_list_lock); |
b97bf3fd | 219 | list_for_each_entry(p_ptr, &ports, port_list) { |
23dd4cce | 220 | msg = &p_ptr->phdr; |
6d4a6672 | 221 | msg_set_prevnode(msg, tipc_own_addr); |
b97bf3fd PL |
222 | msg_set_orignode(msg, tipc_own_addr); |
223 | } | |
4323add6 | 224 | spin_unlock_bh(&tipc_port_list_lock); |
b97bf3fd PL |
225 | } |
226 | ||
b97bf3fd PL |
227 | void tipc_acknowledge(u32 ref, u32 ack) |
228 | { | |
23dd4cce | 229 | struct tipc_port *p_ptr; |
1fc54d8f | 230 | struct sk_buff *buf = NULL; |
b786e2b0 | 231 | struct tipc_msg *msg; |
b97bf3fd | 232 | |
4323add6 | 233 | p_ptr = tipc_port_lock(ref); |
b97bf3fd PL |
234 | if (!p_ptr) |
235 | return; | |
60120526 | 236 | if (p_ptr->connected) |
57289015 JPM |
237 | buf = tipc_msg_create(CONN_MANAGER, CONN_ACK, INT_H_SIZE, |
238 | 0, tipc_port_peernode(p_ptr), | |
239 | tipc_own_addr, tipc_port_peerport(p_ptr), | |
240 | p_ptr->ref, TIPC_OK); | |
4323add6 | 241 | tipc_port_unlock(p_ptr); |
b786e2b0 JPM |
242 | if (!buf) |
243 | return; | |
244 | msg = buf_msg(buf); | |
57289015 | 245 | msg_set_msgcnt(msg, ack); |
9fbfb8b1 | 246 | tipc_link_xmit(buf, msg_destnode(msg), msg_link_selector(msg)); |
b97bf3fd PL |
247 | } |
248 | ||
84602761 YX |
249 | int tipc_publish(struct tipc_port *p_ptr, unsigned int scope, |
250 | struct tipc_name_seq const *seq) | |
b97bf3fd | 251 | { |
b97bf3fd PL |
252 | struct publication *publ; |
253 | u32 key; | |
b97bf3fd | 254 | |
84602761 | 255 | if (p_ptr->connected) |
d55b4c63 | 256 | return -EINVAL; |
84602761 YX |
257 | key = p_ptr->ref + p_ptr->pub_count + 1; |
258 | if (key == p_ptr->ref) | |
259 | return -EADDRINUSE; | |
d55b4c63 | 260 | |
4323add6 | 261 | publ = tipc_nametbl_publish(seq->type, seq->lower, seq->upper, |
23dd4cce | 262 | scope, p_ptr->ref, key); |
b97bf3fd PL |
263 | if (publ) { |
264 | list_add(&publ->pport_list, &p_ptr->publications); | |
265 | p_ptr->pub_count++; | |
23dd4cce | 266 | p_ptr->published = 1; |
84602761 | 267 | return 0; |
b97bf3fd | 268 | } |
84602761 | 269 | return -EINVAL; |
b97bf3fd PL |
270 | } |
271 | ||
84602761 YX |
272 | int tipc_withdraw(struct tipc_port *p_ptr, unsigned int scope, |
273 | struct tipc_name_seq const *seq) | |
b97bf3fd | 274 | { |
b97bf3fd PL |
275 | struct publication *publ; |
276 | struct publication *tpubl; | |
277 | int res = -EINVAL; | |
c4307285 | 278 | |
b97bf3fd | 279 | if (!seq) { |
c4307285 | 280 | list_for_each_entry_safe(publ, tpubl, |
b97bf3fd | 281 | &p_ptr->publications, pport_list) { |
c4307285 | 282 | tipc_nametbl_withdraw(publ->type, publ->lower, |
4323add6 | 283 | publ->ref, publ->key); |
b97bf3fd | 284 | } |
0e35fd5e | 285 | res = 0; |
b97bf3fd | 286 | } else { |
c4307285 | 287 | list_for_each_entry_safe(publ, tpubl, |
b97bf3fd PL |
288 | &p_ptr->publications, pport_list) { |
289 | if (publ->scope != scope) | |
290 | continue; | |
291 | if (publ->type != seq->type) | |
292 | continue; | |
293 | if (publ->lower != seq->lower) | |
294 | continue; | |
295 | if (publ->upper != seq->upper) | |
296 | break; | |
c4307285 | 297 | tipc_nametbl_withdraw(publ->type, publ->lower, |
4323add6 | 298 | publ->ref, publ->key); |
0e35fd5e | 299 | res = 0; |
b97bf3fd PL |
300 | break; |
301 | } | |
302 | } | |
303 | if (list_empty(&p_ptr->publications)) | |
23dd4cce | 304 | p_ptr->published = 0; |
b97bf3fd PL |
305 | return res; |
306 | } | |
307 | ||
247f0f3c | 308 | int tipc_port_connect(u32 ref, struct tipc_portid const *peer) |
b97bf3fd | 309 | { |
23dd4cce | 310 | struct tipc_port *p_ptr; |
bc879117 | 311 | int res; |
b97bf3fd | 312 | |
4323add6 | 313 | p_ptr = tipc_port_lock(ref); |
b97bf3fd PL |
314 | if (!p_ptr) |
315 | return -EINVAL; | |
247f0f3c | 316 | res = __tipc_port_connect(ref, p_ptr, peer); |
bc879117 PG |
317 | tipc_port_unlock(p_ptr); |
318 | return res; | |
319 | } | |
320 | ||
321 | /* | |
247f0f3c | 322 | * __tipc_port_connect - connect to a remote peer |
bc879117 PG |
323 | * |
324 | * Port must be locked. | |
325 | */ | |
247f0f3c | 326 | int __tipc_port_connect(u32 ref, struct tipc_port *p_ptr, |
bc879117 PG |
327 | struct tipc_portid const *peer) |
328 | { | |
329 | struct tipc_msg *msg; | |
330 | int res = -EINVAL; | |
331 | ||
23dd4cce | 332 | if (p_ptr->published || p_ptr->connected) |
b97bf3fd PL |
333 | goto exit; |
334 | if (!peer->ref) | |
335 | goto exit; | |
336 | ||
23dd4cce | 337 | msg = &p_ptr->phdr; |
b97bf3fd PL |
338 | msg_set_destnode(msg, peer->node); |
339 | msg_set_destport(msg, peer->ref); | |
b97bf3fd | 340 | msg_set_type(msg, TIPC_CONN_MSG); |
53b94364 | 341 | msg_set_lookup_scope(msg, 0); |
08c80e9a | 342 | msg_set_hdr_sz(msg, SHORT_H_SIZE); |
b97bf3fd PL |
343 | |
344 | p_ptr->probing_interval = PROBING_INTERVAL; | |
ac0074ee | 345 | p_ptr->probing_state = TIPC_CONN_OK; |
23dd4cce | 346 | p_ptr->connected = 1; |
b97bf3fd | 347 | k_start_timer(&p_ptr->timer, p_ptr->probing_interval); |
02be61a9 JPM |
348 | res = tipc_node_add_conn(tipc_port_peernode(p_ptr), p_ptr->ref, |
349 | tipc_port_peerport(p_ptr)); | |
b97bf3fd | 350 | exit: |
4ccfe5e0 | 351 | p_ptr->max_pkt = tipc_node_get_mtu(peer->node, ref); |
b97bf3fd PL |
352 | return res; |
353 | } | |
354 | ||
bc879117 PG |
355 | /* |
356 | * __tipc_disconnect - disconnect port from peer | |
0c3141e9 AS |
357 | * |
358 | * Port must be locked. | |
359 | */ | |
247f0f3c | 360 | int __tipc_port_disconnect(struct tipc_port *tp_ptr) |
0c3141e9 | 361 | { |
0c3141e9 AS |
362 | if (tp_ptr->connected) { |
363 | tp_ptr->connected = 0; | |
364 | /* let timer expire on it's own to avoid deadlock! */ | |
02be61a9 | 365 | tipc_node_remove_conn(tipc_port_peernode(tp_ptr), tp_ptr->ref); |
0cee6bbe | 366 | return 0; |
0c3141e9 | 367 | } |
0cee6bbe | 368 | |
369 | return -ENOTCONN; | |
0c3141e9 AS |
370 | } |
371 | ||
b97bf3fd | 372 | /* |
247f0f3c | 373 | * tipc_port_disconnect(): Disconnect port form peer. |
b97bf3fd PL |
374 | * This is a node local operation. |
375 | */ | |
247f0f3c | 376 | int tipc_port_disconnect(u32 ref) |
b97bf3fd | 377 | { |
23dd4cce | 378 | struct tipc_port *p_ptr; |
0c3141e9 | 379 | int res; |
b97bf3fd | 380 | |
4323add6 | 381 | p_ptr = tipc_port_lock(ref); |
b97bf3fd PL |
382 | if (!p_ptr) |
383 | return -EINVAL; | |
247f0f3c | 384 | res = __tipc_port_disconnect(p_ptr); |
4323add6 | 385 | tipc_port_unlock(p_ptr); |
b97bf3fd PL |
386 | return res; |
387 | } | |
388 | ||
389 | /* | |
247f0f3c | 390 | * tipc_port_shutdown(): Send a SHUTDOWN msg to peer and disconnect |
b97bf3fd | 391 | */ |
247f0f3c | 392 | int tipc_port_shutdown(u32 ref) |
b97bf3fd | 393 | { |
b786e2b0 | 394 | struct tipc_msg *msg; |
23dd4cce | 395 | struct tipc_port *p_ptr; |
1fc54d8f | 396 | struct sk_buff *buf = NULL; |
57289015 | 397 | u32 peer_node; |
b97bf3fd | 398 | |
4323add6 | 399 | p_ptr = tipc_port_lock(ref); |
b97bf3fd PL |
400 | if (!p_ptr) |
401 | return -EINVAL; | |
57289015 JPM |
402 | peer_node = tipc_port_peernode(p_ptr); |
403 | buf = tipc_msg_create(TIPC_CRITICAL_IMPORTANCE, TIPC_CONN_MSG, | |
404 | SHORT_H_SIZE, 0, peer_node, | |
405 | tipc_own_addr, tipc_port_peerport(p_ptr), | |
406 | p_ptr->ref, TIPC_CONN_SHUTDOWN); | |
4323add6 | 407 | tipc_port_unlock(p_ptr); |
b786e2b0 | 408 | msg = buf_msg(buf); |
57289015 | 409 | tipc_link_xmit(buf, peer_node, msg_link_selector(msg)); |
247f0f3c | 410 | return tipc_port_disconnect(ref); |
b97bf3fd | 411 | } |