1/* 2 * net/tipc/config.c: TIPC configuration management code 3 * 4 * Copyright (c) 2002-2006, Ericsson AB 5 * Copyright (c) 2004-2006, 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 "dbg.h" 39#include "bearer.h" 40#include "port.h" 41#include "link.h" 42#include "zone.h" 43#include "addr.h" 44#include "name_table.h" 45#include "node.h" 46#include "config.h" 47#include "discover.h" 48 49struct subscr_data { 50 char usr_handle[8]; 51 u32 domain; 52 u32 port_ref; 53 struct list_head subd_list; 54}; 55 56struct manager { 57 u32 user_ref; 58 u32 port_ref; 59 u32 subscr_ref; 60 u32 link_subscriptions; 61 struct list_head link_subscribers; 62}; 63 64static struct manager mng = { 0}; 65 66static DEFINE_SPINLOCK(config_lock); 67 68static const void *req_tlv_area; /* request message TLV area */ 69static int req_tlv_space; /* request message TLV area size */ 70static int rep_headroom; /* reply message headroom to use */ 71 72 73void tipc_cfg_link_event(u32 addr, char *name, int up) 74{ 75 /* TIPC DOESN'T HANDLE LINK EVENT SUBSCRIPTIONS AT THE MOMENT */ 76} 77 78 79struct sk_buff *tipc_cfg_reply_alloc(int payload_size) 80{ 81 struct sk_buff *buf; 82 83 buf = alloc_skb(rep_headroom + payload_size, GFP_ATOMIC); 84 if (buf) 85 skb_reserve(buf, rep_headroom); 86 return buf; 87} 88 89int tipc_cfg_append_tlv(struct sk_buff *buf, int tlv_type, 90 void *tlv_data, int tlv_data_size) 91{ 92 struct tlv_desc *tlv = (struct tlv_desc *)skb_tail_pointer(buf); 93 int new_tlv_space = TLV_SPACE(tlv_data_size); 94 95 if (skb_tailroom(buf) < new_tlv_space) { 96 dbg("tipc_cfg_append_tlv unable to append TLV\n"); 97 return 0; 98 } 99 skb_put(buf, new_tlv_space); 100 tlv->tlv_type = htons(tlv_type); 101 tlv->tlv_len = htons(TLV_LENGTH(tlv_data_size)); 102 if (tlv_data_size && tlv_data) 103 memcpy(TLV_DATA(tlv), tlv_data, tlv_data_size); 104 return 1; 105} 106 107struct sk_buff *tipc_cfg_reply_unsigned_type(u16 tlv_type, u32 value) 108{ 109 struct sk_buff *buf; 110 __be32 value_net; 111 112 buf = tipc_cfg_reply_alloc(TLV_SPACE(sizeof(value))); 113 if (buf) { 114 value_net = htonl(value); 115 tipc_cfg_append_tlv(buf, tlv_type, &value_net, 116 sizeof(value_net)); 117 } 118 return buf; 119} 120 121struct sk_buff *tipc_cfg_reply_string_type(u16 tlv_type, char *string) 122{ 123 struct sk_buff *buf; 124 int string_len = strlen(string) + 1; 125 126 buf = tipc_cfg_reply_alloc(TLV_SPACE(string_len)); 127 if (buf) 128 tipc_cfg_append_tlv(buf, tlv_type, string, string_len); 129 return buf; 130} 131 132 133 134 135 136static struct sk_buff *cfg_enable_bearer(void) 137{ 138 struct tipc_bearer_config *args; 139 140 if (!TLV_CHECK(req_tlv_area, req_tlv_space, TIPC_TLV_BEARER_CONFIG)) 141 return tipc_cfg_reply_error_string(TIPC_CFG_TLV_ERROR); 142 143 args = (struct tipc_bearer_config *)TLV_DATA(req_tlv_area); 144 if (tipc_enable_bearer(args->name, 145 ntohl(args->detect_scope), 146 ntohl(args->priority))) 147 return tipc_cfg_reply_error_string("unable to enable bearer"); 148 149 return tipc_cfg_reply_none(); 150} 151 152static struct sk_buff *cfg_disable_bearer(void) 153{ 154 if (!TLV_CHECK(req_tlv_area, req_tlv_space, TIPC_TLV_BEARER_NAME)) 155 return tipc_cfg_reply_error_string(TIPC_CFG_TLV_ERROR); 156 157 if (tipc_disable_bearer((char *)TLV_DATA(req_tlv_area))) 158 return tipc_cfg_reply_error_string("unable to disable bearer"); 159 160 return tipc_cfg_reply_none(); 161} 162 163static struct sk_buff *cfg_set_own_addr(void) 164{ 165 u32 addr; 166 167 if (!TLV_CHECK(req_tlv_area, req_tlv_space, TIPC_TLV_NET_ADDR)) 168 return tipc_cfg_reply_error_string(TIPC_CFG_TLV_ERROR); 169 170 addr = ntohl(*(__be32 *)TLV_DATA(req_tlv_area)); 171 if (addr == tipc_own_addr) 172 return tipc_cfg_reply_none(); 173 if (!tipc_addr_node_valid(addr)) 174 return tipc_cfg_reply_error_string(TIPC_CFG_INVALID_VALUE 175 " (node address)"); 176 if (tipc_mode == TIPC_NET_MODE) 177 return tipc_cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED 178 " (cannot change node address once assigned)"); 179 tipc_own_addr = addr; 180 181 /* 182 * Must release all spinlocks before calling start_net() because 183 * Linux version of TIPC calls eth_media_start() which calls 184 * register_netdevice_notifier() which may block! 185 * 186 * Temporarily releasing the lock should be harmless for non-Linux TIPC, 187 * but Linux version of eth_media_start() should really be reworked 188 * so that it can be called with spinlocks held. 189 */ 190 191 spin_unlock_bh(&config_lock); 192 tipc_core_start_net(); 193 spin_lock_bh(&config_lock); 194 return tipc_cfg_reply_none(); 195} 196 197static struct sk_buff *cfg_set_remote_mng(void) 198{ 199 u32 value; 200 201 if (!TLV_CHECK(req_tlv_area, req_tlv_space, TIPC_TLV_UNSIGNED)) 202 return tipc_cfg_reply_error_string(TIPC_CFG_TLV_ERROR); 203 204 value = ntohl(*(__be32 *)TLV_DATA(req_tlv_area)); 205 tipc_remote_management = (value != 0); 206 return tipc_cfg_reply_none(); 207} 208 209static struct sk_buff *cfg_set_max_publications(void) 210{ 211 u32 value; 212 213 if (!TLV_CHECK(req_tlv_area, req_tlv_space, TIPC_TLV_UNSIGNED)) 214 return tipc_cfg_reply_error_string(TIPC_CFG_TLV_ERROR); 215 216 value = ntohl(*(__be32 *)TLV_DATA(req_tlv_area)); 217 if (value != delimit(value, 1, 65535)) 218 return tipc_cfg_reply_error_string(TIPC_CFG_INVALID_VALUE 219 " (max publications must be 1-65535)"); 220 tipc_max_publications = value; 221 return tipc_cfg_reply_none(); 222} 223 224static struct sk_buff *cfg_set_max_subscriptions(void) 225{ 226 u32 value; 227 228 if (!TLV_CHECK(req_tlv_area, req_tlv_space, TIPC_TLV_UNSIGNED)) 229 return tipc_cfg_reply_error_string(TIPC_CFG_TLV_ERROR); 230 231 value = ntohl(*(__be32 *)TLV_DATA(req_tlv_area)); 232 if (value != delimit(value, 1, 65535)) 233 return tipc_cfg_reply_error_string(TIPC_CFG_INVALID_VALUE 234 " (max subscriptions must be 1-65535"); 235 tipc_max_subscriptions = value; 236 return tipc_cfg_reply_none(); 237} 238 239static struct sk_buff *cfg_set_max_ports(void) 240{ 241 u32 value; 242 243 if (!TLV_CHECK(req_tlv_area, req_tlv_space, TIPC_TLV_UNSIGNED)) 244 return tipc_cfg_reply_error_string(TIPC_CFG_TLV_ERROR); 245 value = ntohl(*(__be32 *)TLV_DATA(req_tlv_area)); 246 if (value == tipc_max_ports) 247 return tipc_cfg_reply_none(); 248 if (value != delimit(value, 127, 65535)) 249 return tipc_cfg_reply_error_string(TIPC_CFG_INVALID_VALUE 250 " (max ports must be 127-65535)"); 251 if (tipc_mode != TIPC_NOT_RUNNING) 252 return tipc_cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED 253 " (cannot change max ports while TIPC is active)"); 254 tipc_max_ports = value; 255 return tipc_cfg_reply_none(); 256} 257 258static struct sk_buff *cfg_set_max_zones(void) 259{ 260 u32 value; 261 262 if (!TLV_CHECK(req_tlv_area, req_tlv_space, TIPC_TLV_UNSIGNED)) 263 return tipc_cfg_reply_error_string(TIPC_CFG_TLV_ERROR); 264 value = ntohl(*(__be32 *)TLV_DATA(req_tlv_area)); 265 if (value == tipc_max_zones) 266 return tipc_cfg_reply_none(); 267 if (value != delimit(value, 1, 255)) 268 return tipc_cfg_reply_error_string(TIPC_CFG_INVALID_VALUE 269 " (max zones must be 1-255)"); 270 if (tipc_mode == TIPC_NET_MODE) 271 return tipc_cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED 272 " (cannot change max zones once TIPC has joined a network)"); 273 tipc_max_zones = value; 274 return tipc_cfg_reply_none(); 275} 276 277static struct sk_buff *cfg_set_max_clusters(void) 278{ 279 u32 value; 280 281 if (!TLV_CHECK(req_tlv_area, req_tlv_space, TIPC_TLV_UNSIGNED)) 282 return tipc_cfg_reply_error_string(TIPC_CFG_TLV_ERROR); 283 value = ntohl(*(__be32 *)TLV_DATA(req_tlv_area)); 284 if (value != delimit(value, 1, 1)) 285 return tipc_cfg_reply_error_string(TIPC_CFG_INVALID_VALUE 286 " (max clusters fixed at 1)"); 287 return tipc_cfg_reply_none(); 288} 289 290static struct sk_buff *cfg_set_max_nodes(void) 291{ 292 u32 value; 293 294 if (!TLV_CHECK(req_tlv_area, req_tlv_space, TIPC_TLV_UNSIGNED)) 295 return tipc_cfg_reply_error_string(TIPC_CFG_TLV_ERROR); 296 value = ntohl(*(__be32 *)TLV_DATA(req_tlv_area)); 297 if (value == tipc_max_nodes) 298 return tipc_cfg_reply_none(); 299 if (value != delimit(value, 8, 2047)) 300 return tipc_cfg_reply_error_string(TIPC_CFG_INVALID_VALUE 301 " (max nodes must be 8-2047)"); 302 if (tipc_mode == TIPC_NET_MODE) 303 return tipc_cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED 304 " (cannot change max nodes once TIPC has joined a network)"); 305 tipc_max_nodes = value; 306 return tipc_cfg_reply_none(); 307} 308 309static struct sk_buff *cfg_set_max_slaves(void) 310{ 311 u32 value; 312 313 if (!TLV_CHECK(req_tlv_area, req_tlv_space, TIPC_TLV_UNSIGNED)) 314 return tipc_cfg_reply_error_string(TIPC_CFG_TLV_ERROR); 315 value = ntohl(*(__be32 *)TLV_DATA(req_tlv_area)); 316 if (value != 0) 317 return tipc_cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED 318 " (max secondary nodes fixed at 0)"); 319 return tipc_cfg_reply_none(); 320} 321 322static struct sk_buff *cfg_set_netid(void) 323{ 324 u32 value; 325 326 if (!TLV_CHECK(req_tlv_area, req_tlv_space, TIPC_TLV_UNSIGNED)) 327 return tipc_cfg_reply_error_string(TIPC_CFG_TLV_ERROR); 328 value = ntohl(*(__be32 *)TLV_DATA(req_tlv_area)); 329 if (value == tipc_net_id) 330 return tipc_cfg_reply_none(); 331 if (value != delimit(value, 1, 9999)) 332 return tipc_cfg_reply_error_string(TIPC_CFG_INVALID_VALUE 333 " (network id must be 1-9999)"); 334 if (tipc_mode == TIPC_NET_MODE) 335 return tipc_cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED 336 " (cannot change network id once TIPC has joined a network)"); 337 tipc_net_id = value; 338 return tipc_cfg_reply_none(); 339} 340 341struct sk_buff *tipc_cfg_do_cmd(u32 orig_node, u16 cmd, const void *request_area, 342 int request_space, int reply_headroom) 343{ 344 struct sk_buff *rep_tlv_buf; 345 346 spin_lock_bh(&config_lock); 347 348 /* Save request and reply details in a well-known location */ 349 350 req_tlv_area = request_area; 351 req_tlv_space = request_space; 352 rep_headroom = reply_headroom; 353 354 /* Check command authorization */ 355 356 if (likely(orig_node == tipc_own_addr)) { 357 /* command is permitted */ 358 } else if (cmd >= 0x8000) { 359 rep_tlv_buf = tipc_cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED 360 " (cannot be done remotely)"); 361 goto exit; 362 } else if (!tipc_remote_management) { 363 rep_tlv_buf = tipc_cfg_reply_error_string(TIPC_CFG_NO_REMOTE); 364 goto exit; 365 } 366 else if (cmd >= 0x4000) { 367 u32 domain = 0; 368 369 if ((tipc_nametbl_translate(TIPC_ZM_SRV, 0, &domain) == 0) || 370 (domain != orig_node)) { 371 rep_tlv_buf = tipc_cfg_reply_error_string(TIPC_CFG_NOT_ZONE_MSTR); 372 goto exit; 373 } 374 } 375 376 /* Call appropriate processing routine */ 377 378 switch (cmd) { 379 case TIPC_CMD_NOOP: 380 rep_tlv_buf = tipc_cfg_reply_none(); 381 break; 382 case TIPC_CMD_GET_NODES: 383 rep_tlv_buf = tipc_node_get_nodes(req_tlv_area, req_tlv_space); 384 break; 385 case TIPC_CMD_GET_LINKS: 386 rep_tlv_buf = tipc_node_get_links(req_tlv_area, req_tlv_space); 387 break; 388 case TIPC_CMD_SHOW_LINK_STATS: 389 rep_tlv_buf = tipc_link_cmd_show_stats(req_tlv_area, req_tlv_space); 390 break; 391 case TIPC_CMD_RESET_LINK_STATS: 392 rep_tlv_buf = tipc_link_cmd_reset_stats(req_tlv_area, req_tlv_space); 393 break; 394 case TIPC_CMD_SHOW_NAME_TABLE: 395 rep_tlv_buf = tipc_nametbl_get(req_tlv_area, req_tlv_space); 396 break; 397 case TIPC_CMD_GET_BEARER_NAMES: 398 rep_tlv_buf = tipc_bearer_get_names(); 399 break; 400 case TIPC_CMD_GET_MEDIA_NAMES: 401 rep_tlv_buf = tipc_media_get_names(); 402 break; 403 case TIPC_CMD_SHOW_PORTS: 404 rep_tlv_buf = tipc_port_get_ports(); 405 break; 406 case TIPC_CMD_SET_LOG_SIZE: 407 rep_tlv_buf = tipc_log_resize(req_tlv_area, req_tlv_space); 408 break; 409 case TIPC_CMD_DUMP_LOG: 410 rep_tlv_buf = tipc_log_dump(); 411 break; 412 case TIPC_CMD_SET_LINK_TOL: 413 case TIPC_CMD_SET_LINK_PRI: 414 case TIPC_CMD_SET_LINK_WINDOW: 415 rep_tlv_buf = tipc_link_cmd_config(req_tlv_area, req_tlv_space, cmd); 416 break; 417 case TIPC_CMD_ENABLE_BEARER: 418 rep_tlv_buf = cfg_enable_bearer(); 419 break; 420 case TIPC_CMD_DISABLE_BEARER: 421 rep_tlv_buf = cfg_disable_bearer(); 422 break; 423 case TIPC_CMD_SET_NODE_ADDR: 424 rep_tlv_buf = cfg_set_own_addr(); 425 break; 426 case TIPC_CMD_SET_REMOTE_MNG: 427 rep_tlv_buf = cfg_set_remote_mng(); 428 break; 429 case TIPC_CMD_SET_MAX_PORTS: 430 rep_tlv_buf = cfg_set_max_ports(); 431 break; 432 case TIPC_CMD_SET_MAX_PUBL: 433 rep_tlv_buf = cfg_set_max_publications(); 434 break; 435 case TIPC_CMD_SET_MAX_SUBSCR: 436 rep_tlv_buf = cfg_set_max_subscriptions(); 437 break; 438 case TIPC_CMD_SET_MAX_ZONES: 439 rep_tlv_buf = cfg_set_max_zones(); 440 break; 441 case TIPC_CMD_SET_MAX_CLUSTERS: 442 rep_tlv_buf = cfg_set_max_clusters(); 443 break; 444 case TIPC_CMD_SET_MAX_NODES: 445 rep_tlv_buf = cfg_set_max_nodes(); 446 break; 447 case TIPC_CMD_SET_MAX_SLAVES: 448 rep_tlv_buf = cfg_set_max_slaves(); 449 break; 450 case TIPC_CMD_SET_NETID: 451 rep_tlv_buf = cfg_set_netid(); 452 break; 453 case TIPC_CMD_GET_REMOTE_MNG: 454 rep_tlv_buf = tipc_cfg_reply_unsigned(tipc_remote_management); 455 break; 456 case TIPC_CMD_GET_MAX_PORTS: 457 rep_tlv_buf = tipc_cfg_reply_unsigned(tipc_max_ports); 458 break; 459 case TIPC_CMD_GET_MAX_PUBL: 460 rep_tlv_buf = tipc_cfg_reply_unsigned(tipc_max_publications); 461 break; 462 case TIPC_CMD_GET_MAX_SUBSCR: 463 rep_tlv_buf = tipc_cfg_reply_unsigned(tipc_max_subscriptions); 464 break; 465 case TIPC_CMD_GET_MAX_ZONES: 466 rep_tlv_buf = tipc_cfg_reply_unsigned(tipc_max_zones); 467 break; 468 case TIPC_CMD_GET_MAX_CLUSTERS: 469 rep_tlv_buf = tipc_cfg_reply_unsigned(tipc_max_clusters); 470 break; 471 case TIPC_CMD_GET_MAX_NODES: 472 rep_tlv_buf = tipc_cfg_reply_unsigned(tipc_max_nodes); 473 break; 474 case TIPC_CMD_GET_MAX_SLAVES: 475 rep_tlv_buf = tipc_cfg_reply_unsigned(tipc_max_slaves); 476 break; 477 case TIPC_CMD_GET_NETID: 478 rep_tlv_buf = tipc_cfg_reply_unsigned(tipc_net_id); 479 break; 480 default: 481 rep_tlv_buf = tipc_cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED 482 " (unknown command)"); 483 break; 484 } 485 486 /* Return reply buffer */ 487exit: 488 spin_unlock_bh(&config_lock); 489 return rep_tlv_buf; 490} 491 492static void cfg_named_msg_event(void *userdata, 493 u32 port_ref, 494 struct sk_buff **buf, 495 const unchar *msg, 496 u32 size, 497 u32 importance, 498 struct tipc_portid const *orig, 499 struct tipc_name_seq const *dest) 500{ 501 struct tipc_cfg_msg_hdr *req_hdr; 502 struct tipc_cfg_msg_hdr *rep_hdr; 503 struct sk_buff *rep_buf; 504 505 /* Validate configuration message header (ignore invalid message) */ 506 507 req_hdr = (struct tipc_cfg_msg_hdr *)msg; 508 if ((size < sizeof(*req_hdr)) || 509 (size != TCM_ALIGN(ntohl(req_hdr->tcm_len))) || 510 (ntohs(req_hdr->tcm_flags) != TCM_F_REQUEST)) { 511 warn("Invalid configuration message discarded\n"); 512 return; 513 } 514 515 /* Generate reply for request (if can't, return request) */ 516 517 rep_buf = tipc_cfg_do_cmd(orig->node, 518 ntohs(req_hdr->tcm_type), 519 msg + sizeof(*req_hdr), 520 size - sizeof(*req_hdr), 521 BUF_HEADROOM + MAX_H_SIZE + sizeof(*rep_hdr)); 522 if (rep_buf) { 523 skb_push(rep_buf, sizeof(*rep_hdr)); 524 rep_hdr = (struct tipc_cfg_msg_hdr *)rep_buf->data; 525 memcpy(rep_hdr, req_hdr, sizeof(*rep_hdr)); 526 rep_hdr->tcm_len = htonl(rep_buf->len); 527 rep_hdr->tcm_flags &= htons(~TCM_F_REQUEST); 528 } else { 529 rep_buf = *buf; 530 *buf = NULL; 531 } 532 533 /* NEED TO ADD CODE TO HANDLE FAILED SEND (SUCH AS CONGESTION) */ 534 tipc_send_buf2port(port_ref, orig, rep_buf, rep_buf->len); 535} 536 537int tipc_cfg_init(void) 538{ 539 struct tipc_name_seq seq; 540 int res; 541 542 memset(&mng, 0, sizeof(mng)); 543 INIT_LIST_HEAD(&mng.link_subscribers); 544 545 res = tipc_attach(&mng.user_ref, NULL, NULL); 546 if (res) 547 goto failed; 548 549 res = tipc_createport(mng.user_ref, NULL, TIPC_CRITICAL_IMPORTANCE, 550 NULL, NULL, NULL, 551 NULL, cfg_named_msg_event, NULL, 552 NULL, &mng.port_ref); 553 if (res) 554 goto failed; 555 556 seq.type = TIPC_CFG_SRV; 557 seq.lower = seq.upper = tipc_own_addr; 558 res = tipc_nametbl_publish_rsv(mng.port_ref, TIPC_ZONE_SCOPE, &seq); 559 if (res) 560 goto failed; 561 562 return 0; 563 564failed: 565 err("Unable to create configuration service\n"); 566 tipc_detach(mng.user_ref); 567 mng.user_ref = 0; 568 return res; 569} 570 571void tipc_cfg_stop(void) 572{ 573 if (mng.user_ref) { 574 tipc_detach(mng.user_ref); 575 mng.user_ref = 0; 576 } 577} 578