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