linux/drivers/staging/lustre/lnet/lnet/config.c
<<
>>
Prefs
   1/*
   2 * GPL HEADER START
   3 *
   4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License version 2 only,
   8 * as published by the Free Software Foundation.
   9 *
  10 * This program is distributed in the hope that it will be useful, but
  11 * WITHOUT ANY WARRANTY; without even the implied warranty of
  12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  13 * General Public License version 2 for more details (a copy is included
  14 * in the LICENSE file that accompanied this code).
  15 *
  16 * You should have received a copy of the GNU General Public License
  17 * version 2 along with this program; If not, see
  18 * http://www.gnu.org/licenses/gpl-2.0.html
  19 *
  20 * GPL HEADER END
  21 */
  22/*
  23 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
  24 * Use is subject to license terms.
  25 *
  26 * Copyright (c) 2012, 2015, Intel Corporation.
  27 */
  28/*
  29 * This file is part of Lustre, http://www.lustre.org/
  30 * Lustre is a trademark of Sun Microsystems, Inc.
  31 */
  32
  33#define DEBUG_SUBSYSTEM S_LNET
  34#include "../../include/linux/lnet/lib-lnet.h"
  35
  36struct lnet_text_buf {      /* tmp struct for parsing routes */
  37        struct list_head ltb_list;      /* stash on lists */
  38        int ltb_size;   /* allocated size */
  39        char ltb_text[0];     /* text buffer */
  40};
  41
  42static int lnet_tbnob;                  /* track text buf allocation */
  43#define LNET_MAX_TEXTBUF_NOB     (64 << 10)     /* bound allocation */
  44#define LNET_SINGLE_TEXTBUF_NOB  (4 << 10)
  45
  46static void
  47lnet_syntax(char *name, char *str, int offset, int width)
  48{
  49        static char dots[LNET_SINGLE_TEXTBUF_NOB];
  50        static char dashes[LNET_SINGLE_TEXTBUF_NOB];
  51
  52        memset(dots, '.', sizeof(dots));
  53        dots[sizeof(dots) - 1] = 0;
  54        memset(dashes, '-', sizeof(dashes));
  55        dashes[sizeof(dashes) - 1] = 0;
  56
  57        LCONSOLE_ERROR_MSG(0x10f, "Error parsing '%s=\"%s\"'\n", name, str);
  58        LCONSOLE_ERROR_MSG(0x110, "here...........%.*s..%.*s|%.*s|\n",
  59                           (int)strlen(name), dots, offset, dots,
  60                            (width < 1) ? 0 : width - 1, dashes);
  61}
  62
  63static int
  64lnet_issep(char c)
  65{
  66        switch (c) {
  67        case '\n':
  68        case '\r':
  69        case ';':
  70                return 1;
  71        default:
  72                return 0;
  73        }
  74}
  75
  76int
  77lnet_net_unique(__u32 net, struct list_head *nilist)
  78{
  79        struct list_head *tmp;
  80        lnet_ni_t *ni;
  81
  82        list_for_each(tmp, nilist) {
  83                ni = list_entry(tmp, lnet_ni_t, ni_list);
  84
  85                if (LNET_NIDNET(ni->ni_nid) == net)
  86                        return 0;
  87        }
  88
  89        return 1;
  90}
  91
  92void
  93lnet_ni_free(struct lnet_ni *ni)
  94{
  95        int i;
  96
  97        if (ni->ni_refs)
  98                cfs_percpt_free(ni->ni_refs);
  99
 100        if (ni->ni_tx_queues)
 101                cfs_percpt_free(ni->ni_tx_queues);
 102
 103        if (ni->ni_cpts)
 104                cfs_expr_list_values_free(ni->ni_cpts, ni->ni_ncpts);
 105
 106        if (ni->ni_lnd_tunables)
 107                LIBCFS_FREE(ni->ni_lnd_tunables, sizeof(*ni->ni_lnd_tunables));
 108
 109        for (i = 0; i < LNET_MAX_INTERFACES && ni->ni_interfaces[i]; i++) {
 110                LIBCFS_FREE(ni->ni_interfaces[i],
 111                            strlen(ni->ni_interfaces[i]) + 1);
 112        }
 113        LIBCFS_FREE(ni, sizeof(*ni));
 114}
 115
 116lnet_ni_t *
 117lnet_ni_alloc(__u32 net, struct cfs_expr_list *el, struct list_head *nilist)
 118{
 119        struct lnet_tx_queue *tq;
 120        struct lnet_ni *ni;
 121        int rc;
 122        int i;
 123
 124        if (!lnet_net_unique(net, nilist)) {
 125                LCONSOLE_ERROR_MSG(0x111, "Duplicate network specified: %s\n",
 126                                   libcfs_net2str(net));
 127                return NULL;
 128        }
 129
 130        LIBCFS_ALLOC(ni, sizeof(*ni));
 131        if (!ni) {
 132                CERROR("Out of memory creating network %s\n",
 133                       libcfs_net2str(net));
 134                return NULL;
 135        }
 136
 137        spin_lock_init(&ni->ni_lock);
 138        INIT_LIST_HEAD(&ni->ni_cptlist);
 139        ni->ni_refs = cfs_percpt_alloc(lnet_cpt_table(),
 140                                       sizeof(*ni->ni_refs[0]));
 141        if (!ni->ni_refs)
 142                goto failed;
 143
 144        ni->ni_tx_queues = cfs_percpt_alloc(lnet_cpt_table(),
 145                                            sizeof(*ni->ni_tx_queues[0]));
 146        if (!ni->ni_tx_queues)
 147                goto failed;
 148
 149        cfs_percpt_for_each(tq, i, ni->ni_tx_queues)
 150                INIT_LIST_HEAD(&tq->tq_delayed);
 151
 152        if (!el) {
 153                ni->ni_cpts  = NULL;
 154                ni->ni_ncpts = LNET_CPT_NUMBER;
 155        } else {
 156                rc = cfs_expr_list_values(el, LNET_CPT_NUMBER, &ni->ni_cpts);
 157                if (rc <= 0) {
 158                        CERROR("Failed to set CPTs for NI %s: %d\n",
 159                               libcfs_net2str(net), rc);
 160                        goto failed;
 161                }
 162
 163                LASSERT(rc <= LNET_CPT_NUMBER);
 164                if (rc == LNET_CPT_NUMBER) {
 165                        LIBCFS_FREE(ni->ni_cpts, rc * sizeof(ni->ni_cpts[0]));
 166                        ni->ni_cpts = NULL;
 167                }
 168
 169                ni->ni_ncpts = rc;
 170        }
 171
 172        /* LND will fill in the address part of the NID */
 173        ni->ni_nid = LNET_MKNID(net, 0);
 174        ni->ni_last_alive = ktime_get_real_seconds();
 175        list_add_tail(&ni->ni_list, nilist);
 176        return ni;
 177 failed:
 178        lnet_ni_free(ni);
 179        return NULL;
 180}
 181
 182int
 183lnet_parse_networks(struct list_head *nilist, char *networks)
 184{
 185        struct cfs_expr_list *el = NULL;
 186        int tokensize;
 187        char *tokens;
 188        char *str;
 189        char *tmp;
 190        struct lnet_ni *ni;
 191        __u32 net;
 192        int nnets = 0;
 193        struct list_head *temp_node;
 194
 195        if (!networks) {
 196                CERROR("networks string is undefined\n");
 197                return -EINVAL;
 198        }
 199
 200        if (strlen(networks) > LNET_SINGLE_TEXTBUF_NOB) {
 201                /* _WAY_ conservative */
 202                LCONSOLE_ERROR_MSG(0x112,
 203                                   "Can't parse networks: string too long\n");
 204                return -EINVAL;
 205        }
 206
 207        tokensize = strlen(networks) + 1;
 208
 209        LIBCFS_ALLOC(tokens, tokensize);
 210        if (!tokens) {
 211                CERROR("Can't allocate net tokens\n");
 212                return -ENOMEM;
 213        }
 214
 215        memcpy(tokens, networks, tokensize);
 216        tmp = tokens;
 217        str = tokens;
 218
 219        while (str && *str) {
 220                char *comma = strchr(str, ',');
 221                char *bracket = strchr(str, '(');
 222                char *square = strchr(str, '[');
 223                char *iface;
 224                int niface;
 225                int rc;
 226
 227                /*
 228                 * NB we don't check interface conflicts here; it's the LNDs
 229                 * responsibility (if it cares at all)
 230                 */
 231                if (square && (!comma || square < comma)) {
 232                        /*
 233                         * i.e: o2ib0(ib0)[1,2], number between square
 234                         * brackets are CPTs this NI needs to be bond
 235                         */
 236                        if (bracket && bracket > square) {
 237                                tmp = square;
 238                                goto failed_syntax;
 239                        }
 240
 241                        tmp = strchr(square, ']');
 242                        if (!tmp) {
 243                                tmp = square;
 244                                goto failed_syntax;
 245                        }
 246
 247                        rc = cfs_expr_list_parse(square, tmp - square + 1,
 248                                                 0, LNET_CPT_NUMBER - 1, &el);
 249                        if (rc) {
 250                                tmp = square;
 251                                goto failed_syntax;
 252                        }
 253
 254                        while (square <= tmp)
 255                                *square++ = ' ';
 256                }
 257
 258                if (!bracket || (comma && comma < bracket)) {
 259                        /* no interface list specified */
 260
 261                        if (comma)
 262                                *comma++ = 0;
 263                        net = libcfs_str2net(cfs_trimwhite(str));
 264
 265                        if (net == LNET_NIDNET(LNET_NID_ANY)) {
 266                                LCONSOLE_ERROR_MSG(0x113,
 267                                                   "Unrecognised network type\n");
 268                                tmp = str;
 269                                goto failed_syntax;
 270                        }
 271
 272                        if (LNET_NETTYP(net) != LOLND && /* LO is implicit */
 273                            !lnet_ni_alloc(net, el, nilist))
 274                                goto failed;
 275
 276                        if (el) {
 277                                cfs_expr_list_free(el);
 278                                el = NULL;
 279                        }
 280
 281                        str = comma;
 282                        continue;
 283                }
 284
 285                *bracket = 0;
 286                net = libcfs_str2net(cfs_trimwhite(str));
 287                if (net == LNET_NIDNET(LNET_NID_ANY)) {
 288                        tmp = str;
 289                        goto failed_syntax;
 290                }
 291
 292                ni = lnet_ni_alloc(net, el, nilist);
 293                if (!ni)
 294                        goto failed;
 295
 296                if (el) {
 297                        cfs_expr_list_free(el);
 298                        el = NULL;
 299                }
 300
 301                niface = 0;
 302                iface = bracket + 1;
 303
 304                bracket = strchr(iface, ')');
 305                if (!bracket) {
 306                        tmp = iface;
 307                        goto failed_syntax;
 308                }
 309
 310                *bracket = 0;
 311                do {
 312                        comma = strchr(iface, ',');
 313                        if (comma)
 314                                *comma++ = 0;
 315
 316                        iface = cfs_trimwhite(iface);
 317                        if (!*iface) {
 318                                tmp = iface;
 319                                goto failed_syntax;
 320                        }
 321
 322                        if (niface == LNET_MAX_INTERFACES) {
 323                                LCONSOLE_ERROR_MSG(0x115,
 324                                                   "Too many interfaces for net %s\n",
 325                                                   libcfs_net2str(net));
 326                                goto failed;
 327                        }
 328
 329                        /*
 330                         * Allocate a separate piece of memory and copy
 331                         * into it the string, so we don't have
 332                         * a depencency on the tokens string.  This way we
 333                         * can free the tokens at the end of the function.
 334                         * The newly allocated ni_interfaces[] can be
 335                         * freed when freeing the NI
 336                         */
 337                        LIBCFS_ALLOC(ni->ni_interfaces[niface],
 338                                     strlen(iface) + 1);
 339                        if (!ni->ni_interfaces[niface]) {
 340                                CERROR("Can't allocate net interface name\n");
 341                                goto failed;
 342                        }
 343                        strncpy(ni->ni_interfaces[niface], iface,
 344                                strlen(iface));
 345                        niface++;
 346                        iface = comma;
 347                } while (iface);
 348
 349                str = bracket + 1;
 350                comma = strchr(bracket + 1, ',');
 351                if (comma) {
 352                        *comma = 0;
 353                        str = cfs_trimwhite(str);
 354                        if (*str) {
 355                                tmp = str;
 356                                goto failed_syntax;
 357                        }
 358                        str = comma + 1;
 359                        continue;
 360                }
 361
 362                str = cfs_trimwhite(str);
 363                if (*str) {
 364                        tmp = str;
 365                        goto failed_syntax;
 366                }
 367        }
 368
 369        list_for_each(temp_node, nilist)
 370                nnets++;
 371
 372        LIBCFS_FREE(tokens, tokensize);
 373        return nnets;
 374
 375 failed_syntax:
 376        lnet_syntax("networks", networks, (int)(tmp - tokens), strlen(tmp));
 377 failed:
 378        while (!list_empty(nilist)) {
 379                ni = list_entry(nilist->next, lnet_ni_t, ni_list);
 380
 381                list_del(&ni->ni_list);
 382                lnet_ni_free(ni);
 383        }
 384
 385        if (el)
 386                cfs_expr_list_free(el);
 387
 388        LIBCFS_FREE(tokens, tokensize);
 389
 390        return -EINVAL;
 391}
 392
 393static struct lnet_text_buf *
 394lnet_new_text_buf(int str_len)
 395{
 396        struct lnet_text_buf *ltb;
 397        int nob;
 398
 399        /* NB allocate space for the terminating 0 */
 400        nob = offsetof(struct lnet_text_buf, ltb_text[str_len + 1]);
 401        if (nob > LNET_SINGLE_TEXTBUF_NOB) {
 402                /* _way_ conservative for "route net gateway..." */
 403                CERROR("text buffer too big\n");
 404                return NULL;
 405        }
 406
 407        if (lnet_tbnob + nob > LNET_MAX_TEXTBUF_NOB) {
 408                CERROR("Too many text buffers\n");
 409                return NULL;
 410        }
 411
 412        LIBCFS_ALLOC(ltb, nob);
 413        if (!ltb)
 414                return NULL;
 415
 416        ltb->ltb_size = nob;
 417        ltb->ltb_text[0] = 0;
 418        lnet_tbnob += nob;
 419        return ltb;
 420}
 421
 422static void
 423lnet_free_text_buf(struct lnet_text_buf *ltb)
 424{
 425        lnet_tbnob -= ltb->ltb_size;
 426        LIBCFS_FREE(ltb, ltb->ltb_size);
 427}
 428
 429static void
 430lnet_free_text_bufs(struct list_head *tbs)
 431{
 432        struct lnet_text_buf *ltb;
 433
 434        while (!list_empty(tbs)) {
 435                ltb = list_entry(tbs->next, struct lnet_text_buf, ltb_list);
 436
 437                list_del(&ltb->ltb_list);
 438                lnet_free_text_buf(ltb);
 439        }
 440}
 441
 442static int
 443lnet_str2tbs_sep(struct list_head *tbs, char *str)
 444{
 445        struct list_head pending;
 446        char *sep;
 447        int nob;
 448        int i;
 449        struct lnet_text_buf *ltb;
 450
 451        INIT_LIST_HEAD(&pending);
 452
 453        /* Split 'str' into separate commands */
 454        for (;;) {
 455                /* skip leading whitespace */
 456                while (isspace(*str))
 457                        str++;
 458
 459                /* scan for separator or comment */
 460                for (sep = str; *sep; sep++)
 461                        if (lnet_issep(*sep) || *sep == '#')
 462                                break;
 463
 464                nob = (int)(sep - str);
 465                if (nob > 0) {
 466                        ltb = lnet_new_text_buf(nob);
 467                        if (!ltb) {
 468                                lnet_free_text_bufs(&pending);
 469                                return -ENOMEM;
 470                        }
 471
 472                        for (i = 0; i < nob; i++)
 473                                if (isspace(str[i]))
 474                                        ltb->ltb_text[i] = ' ';
 475                                else
 476                                        ltb->ltb_text[i] = str[i];
 477
 478                        ltb->ltb_text[nob] = 0;
 479
 480                        list_add_tail(&ltb->ltb_list, &pending);
 481                }
 482
 483                if (*sep == '#') {
 484                        /* scan for separator */
 485                        do {
 486                                sep++;
 487                        } while (*sep && !lnet_issep(*sep));
 488                }
 489
 490                if (!*sep)
 491                        break;
 492
 493                str = sep + 1;
 494        }
 495
 496        list_splice(&pending, tbs->prev);
 497        return 0;
 498}
 499
 500static int
 501lnet_expand1tb(struct list_head *list,
 502               char *str, char *sep1, char *sep2,
 503               char *item, int itemlen)
 504{
 505        int len1 = (int)(sep1 - str);
 506        int len2 = strlen(sep2 + 1);
 507        struct lnet_text_buf *ltb;
 508
 509        LASSERT(*sep1 == '[');
 510        LASSERT(*sep2 == ']');
 511
 512        ltb = lnet_new_text_buf(len1 + itemlen + len2);
 513        if (!ltb)
 514                return -ENOMEM;
 515
 516        memcpy(ltb->ltb_text, str, len1);
 517        memcpy(&ltb->ltb_text[len1], item, itemlen);
 518        memcpy(&ltb->ltb_text[len1 + itemlen], sep2 + 1, len2);
 519        ltb->ltb_text[len1 + itemlen + len2] = 0;
 520
 521        list_add_tail(&ltb->ltb_list, list);
 522        return 0;
 523}
 524
 525static int
 526lnet_str2tbs_expand(struct list_head *tbs, char *str)
 527{
 528        char num[16];
 529        struct list_head pending;
 530        char *sep;
 531        char *sep2;
 532        char *parsed;
 533        char *enditem;
 534        int lo;
 535        int hi;
 536        int stride;
 537        int i;
 538        int nob;
 539        int scanned;
 540
 541        INIT_LIST_HEAD(&pending);
 542
 543        sep = strchr(str, '[');
 544        if (!sep)                       /* nothing to expand */
 545                return 0;
 546
 547        sep2 = strchr(sep, ']');
 548        if (!sep2)
 549                goto failed;
 550
 551        for (parsed = sep; parsed < sep2; parsed = enditem) {
 552                enditem = ++parsed;
 553                while (enditem < sep2 && *enditem != ',')
 554                        enditem++;
 555
 556                if (enditem == parsed)          /* no empty items */
 557                        goto failed;
 558
 559                if (sscanf(parsed, "%d-%d/%d%n", &lo, &hi,
 560                           &stride, &scanned) < 3) {
 561                        if (sscanf(parsed, "%d-%d%n", &lo, &hi, &scanned) < 2) {
 562                                /* simple string enumeration */
 563                                if (lnet_expand1tb(&pending, str, sep, sep2,
 564                                                   parsed,
 565                                                   (int)(enditem - parsed))) {
 566                                        goto failed;
 567                                }
 568                                continue;
 569                        }
 570
 571                        stride = 1;
 572                }
 573
 574                /* range expansion */
 575
 576                if (enditem != parsed + scanned) /* no trailing junk */
 577                        goto failed;
 578
 579                if (hi < 0 || lo < 0 || stride < 0 || hi < lo ||
 580                    (hi - lo) % stride)
 581                        goto failed;
 582
 583                for (i = lo; i <= hi; i += stride) {
 584                        snprintf(num, sizeof(num), "%d", i);
 585                        nob = strlen(num);
 586                        if (nob + 1 == sizeof(num))
 587                                goto failed;
 588
 589                        if (lnet_expand1tb(&pending, str, sep, sep2,
 590                                           num, nob))
 591                                goto failed;
 592                }
 593        }
 594
 595        list_splice(&pending, tbs->prev);
 596        return 1;
 597
 598 failed:
 599        lnet_free_text_bufs(&pending);
 600        return -EINVAL;
 601}
 602
 603static int
 604lnet_parse_hops(char *str, unsigned int *hops)
 605{
 606        int len = strlen(str);
 607        int nob = len;
 608
 609        return (sscanf(str, "%u%n", hops, &nob) >= 1 &&
 610                nob == len &&
 611                *hops > 0 && *hops < 256);
 612}
 613
 614#define LNET_PRIORITY_SEPARATOR (':')
 615
 616static int
 617lnet_parse_priority(char *str, unsigned int *priority, char **token)
 618{
 619        int nob;
 620        char *sep;
 621        int len;
 622
 623        sep = strchr(str, LNET_PRIORITY_SEPARATOR);
 624        if (!sep) {
 625                *priority = 0;
 626                return 0;
 627        }
 628        len = strlen(sep + 1);
 629
 630        if ((sscanf((sep + 1), "%u%n", priority, &nob) < 1) || (len != nob)) {
 631                /*
 632                 * Update the caller's token pointer so it treats the found
 633                 * priority as the token to report in the error message.
 634                 */
 635                *token += sep - str + 1;
 636                return -EINVAL;
 637        }
 638
 639        CDEBUG(D_NET, "gateway %s, priority %d, nob %d\n", str, *priority, nob);
 640
 641        /*
 642         * Change priority separator to \0 to be able to parse NID
 643         */
 644        *sep = '\0';
 645        return 0;
 646}
 647
 648static int
 649lnet_parse_route(char *str, int *im_a_router)
 650{
 651        /* static scratch buffer OK (single threaded) */
 652        static char cmd[LNET_SINGLE_TEXTBUF_NOB];
 653
 654        struct list_head nets;
 655        struct list_head gateways;
 656        struct list_head *tmp1;
 657        struct list_head *tmp2;
 658        __u32 net;
 659        lnet_nid_t nid;
 660        struct lnet_text_buf *ltb;
 661        int rc;
 662        char *sep;
 663        char *token = str;
 664        int ntokens = 0;
 665        int myrc = -1;
 666        __u32 hops;
 667        int got_hops = 0;
 668        unsigned int priority = 0;
 669
 670        INIT_LIST_HEAD(&gateways);
 671        INIT_LIST_HEAD(&nets);
 672
 673        /* save a copy of the string for error messages */
 674        strncpy(cmd, str, sizeof(cmd));
 675        cmd[sizeof(cmd) - 1] = '\0';
 676
 677        sep = str;
 678        for (;;) {
 679                /* scan for token start */
 680                while (isspace(*sep))
 681                        sep++;
 682                if (!*sep) {
 683                        if (ntokens < (got_hops ? 3 : 2))
 684                                goto token_error;
 685                        break;
 686                }
 687
 688                ntokens++;
 689                token = sep++;
 690
 691                /* scan for token end */
 692                while (*sep && !isspace(*sep))
 693                        sep++;
 694                if (*sep)
 695                        *sep++ = 0;
 696
 697                if (ntokens == 1) {
 698                        tmp2 = &nets;           /* expanding nets */
 699                } else if (ntokens == 2 &&
 700                           lnet_parse_hops(token, &hops)) {
 701                        got_hops = 1;      /* got a hop count */
 702                        continue;
 703                } else {
 704                        tmp2 = &gateways;       /* expanding gateways */
 705                }
 706
 707                ltb = lnet_new_text_buf(strlen(token));
 708                if (!ltb)
 709                        goto out;
 710
 711                strcpy(ltb->ltb_text, token);
 712                tmp1 = &ltb->ltb_list;
 713                list_add_tail(tmp1, tmp2);
 714
 715                while (tmp1 != tmp2) {
 716                        ltb = list_entry(tmp1, struct lnet_text_buf, ltb_list);
 717
 718                        rc = lnet_str2tbs_expand(tmp1->next, ltb->ltb_text);
 719                        if (rc < 0)
 720                                goto token_error;
 721
 722                        tmp1 = tmp1->next;
 723
 724                        if (rc > 0) {           /* expanded! */
 725                                list_del(&ltb->ltb_list);
 726                                lnet_free_text_buf(ltb);
 727                                continue;
 728                        }
 729
 730                        if (ntokens == 1) {
 731                                net = libcfs_str2net(ltb->ltb_text);
 732                                if (net == LNET_NIDNET(LNET_NID_ANY) ||
 733                                    LNET_NETTYP(net) == LOLND)
 734                                        goto token_error;
 735                        } else {
 736                                rc = lnet_parse_priority(ltb->ltb_text,
 737                                                         &priority, &token);
 738                                if (rc < 0)
 739                                        goto token_error;
 740
 741                                nid = libcfs_str2nid(ltb->ltb_text);
 742                                if (nid == LNET_NID_ANY ||
 743                                    LNET_NETTYP(LNET_NIDNET(nid)) == LOLND)
 744                                        goto token_error;
 745                        }
 746                }
 747        }
 748
 749        /**
 750         * if there are no hops set then we want to flag this value as
 751         * unset since hops is an optional parameter
 752         */
 753        if (!got_hops)
 754                hops = LNET_UNDEFINED_HOPS;
 755
 756        LASSERT(!list_empty(&nets));
 757        LASSERT(!list_empty(&gateways));
 758
 759        list_for_each(tmp1, &nets) {
 760                ltb = list_entry(tmp1, struct lnet_text_buf, ltb_list);
 761                net = libcfs_str2net(ltb->ltb_text);
 762                LASSERT(net != LNET_NIDNET(LNET_NID_ANY));
 763
 764                list_for_each(tmp2, &gateways) {
 765                        ltb = list_entry(tmp2, struct lnet_text_buf, ltb_list);
 766                        nid = libcfs_str2nid(ltb->ltb_text);
 767                        LASSERT(nid != LNET_NID_ANY);
 768
 769                        if (lnet_islocalnid(nid)) {
 770                                *im_a_router = 1;
 771                                continue;
 772                        }
 773
 774                        rc = lnet_add_route(net, hops, nid, priority);
 775                        if (rc && rc != -EEXIST && rc != -EHOSTUNREACH) {
 776                                CERROR("Can't create route to %s via %s\n",
 777                                       libcfs_net2str(net),
 778                                       libcfs_nid2str(nid));
 779                                goto out;
 780                        }
 781                }
 782        }
 783
 784        myrc = 0;
 785        goto out;
 786
 787 token_error:
 788        lnet_syntax("routes", cmd, (int)(token - str), strlen(token));
 789 out:
 790        lnet_free_text_bufs(&nets);
 791        lnet_free_text_bufs(&gateways);
 792        return myrc;
 793}
 794
 795static int
 796lnet_parse_route_tbs(struct list_head *tbs, int *im_a_router)
 797{
 798        struct lnet_text_buf *ltb;
 799
 800        while (!list_empty(tbs)) {
 801                ltb = list_entry(tbs->next, struct lnet_text_buf, ltb_list);
 802
 803                if (lnet_parse_route(ltb->ltb_text, im_a_router) < 0) {
 804                        lnet_free_text_bufs(tbs);
 805                        return -EINVAL;
 806                }
 807
 808                list_del(&ltb->ltb_list);
 809                lnet_free_text_buf(ltb);
 810        }
 811
 812        return 0;
 813}
 814
 815int
 816lnet_parse_routes(char *routes, int *im_a_router)
 817{
 818        struct list_head tbs;
 819        int rc = 0;
 820
 821        *im_a_router = 0;
 822
 823        INIT_LIST_HEAD(&tbs);
 824
 825        if (lnet_str2tbs_sep(&tbs, routes) < 0) {
 826                CERROR("Error parsing routes\n");
 827                rc = -EINVAL;
 828        } else {
 829                rc = lnet_parse_route_tbs(&tbs, im_a_router);
 830        }
 831
 832        LASSERT(!lnet_tbnob);
 833        return rc;
 834}
 835
 836static int
 837lnet_match_network_token(char *token, int len, __u32 *ipaddrs, int nip)
 838{
 839        LIST_HEAD(list);
 840        int rc;
 841        int i;
 842
 843        rc = cfs_ip_addr_parse(token, len, &list);
 844        if (rc)
 845                return rc;
 846
 847        for (rc = i = 0; !rc && i < nip; i++)
 848                rc = cfs_ip_addr_match(ipaddrs[i], &list);
 849
 850        cfs_expr_list_free_list(&list);
 851
 852        return rc;
 853}
 854
 855static int
 856lnet_match_network_tokens(char *net_entry, __u32 *ipaddrs, int nip)
 857{
 858        static char tokens[LNET_SINGLE_TEXTBUF_NOB];
 859
 860        int matched = 0;
 861        int ntokens = 0;
 862        int len;
 863        char *net = NULL;
 864        char *sep;
 865        char *token;
 866        int rc;
 867
 868        LASSERT(strlen(net_entry) < sizeof(tokens));
 869
 870        /* work on a copy of the string */
 871        strcpy(tokens, net_entry);
 872        sep = tokens;
 873        for (;;) {
 874                /* scan for token start */
 875                while (isspace(*sep))
 876                        sep++;
 877                if (!*sep)
 878                        break;
 879
 880                token = sep++;
 881
 882                /* scan for token end */
 883                while (*sep && !isspace(*sep))
 884                        sep++;
 885                if (*sep)
 886                        *sep++ = 0;
 887
 888                if (!ntokens++) {
 889                        net = token;
 890                        continue;
 891                }
 892
 893                len = strlen(token);
 894
 895                rc = lnet_match_network_token(token, len, ipaddrs, nip);
 896                if (rc < 0) {
 897                        lnet_syntax("ip2nets", net_entry,
 898                                    (int)(token - tokens), len);
 899                        return rc;
 900                }
 901
 902                if (rc)
 903                        matched |= 1;
 904        }
 905
 906        if (!matched)
 907                return 0;
 908
 909        strcpy(net_entry, net);          /* replace with matched net */
 910        return 1;
 911}
 912
 913static __u32
 914lnet_netspec2net(char *netspec)
 915{
 916        char *bracket = strchr(netspec, '(');
 917        __u32 net;
 918
 919        if (bracket)
 920                *bracket = 0;
 921
 922        net = libcfs_str2net(netspec);
 923
 924        if (bracket)
 925                *bracket = '(';
 926
 927        return net;
 928}
 929
 930static int
 931lnet_splitnets(char *source, struct list_head *nets)
 932{
 933        int offset = 0;
 934        int offset2;
 935        int len;
 936        struct lnet_text_buf *tb;
 937        struct lnet_text_buf *tb2;
 938        struct list_head *t;
 939        char *sep;
 940        char *bracket;
 941        __u32 net;
 942
 943        LASSERT(!list_empty(nets));
 944        LASSERT(nets->next == nets->prev);     /* single entry */
 945
 946        tb = list_entry(nets->next, struct lnet_text_buf, ltb_list);
 947
 948        for (;;) {
 949                sep = strchr(tb->ltb_text, ',');
 950                bracket = strchr(tb->ltb_text, '(');
 951
 952                if (sep && bracket && bracket < sep) {
 953                        /* netspec lists interfaces... */
 954
 955                        offset2 = offset + (int)(bracket - tb->ltb_text);
 956                        len = strlen(bracket);
 957
 958                        bracket = strchr(bracket + 1, ')');
 959
 960                        if (!bracket ||
 961                            !(bracket[1] == ',' || !bracket[1])) {
 962                                lnet_syntax("ip2nets", source, offset2, len);
 963                                return -EINVAL;
 964                        }
 965
 966                        sep = !bracket[1] ? NULL : bracket + 1;
 967                }
 968
 969                if (sep)
 970                        *sep++ = 0;
 971
 972                net = lnet_netspec2net(tb->ltb_text);
 973                if (net == LNET_NIDNET(LNET_NID_ANY)) {
 974                        lnet_syntax("ip2nets", source, offset,
 975                                    strlen(tb->ltb_text));
 976                        return -EINVAL;
 977                }
 978
 979                list_for_each(t, nets) {
 980                        tb2 = list_entry(t, struct lnet_text_buf, ltb_list);
 981
 982                        if (tb2 == tb)
 983                                continue;
 984
 985                        if (net == lnet_netspec2net(tb2->ltb_text)) {
 986                                /* duplicate network */
 987                                lnet_syntax("ip2nets", source, offset,
 988                                            strlen(tb->ltb_text));
 989                                return -EINVAL;
 990                        }
 991                }
 992
 993                if (!sep)
 994                        return 0;
 995
 996                offset += (int)(sep - tb->ltb_text);
 997                len = strlen(sep);
 998                tb2 = lnet_new_text_buf(len);
 999                if (!tb2)
1000                        return -ENOMEM;
1001
1002                strncpy(tb2->ltb_text, sep, len);
1003                tb2->ltb_text[len] = '\0';
1004                list_add_tail(&tb2->ltb_list, nets);
1005
1006                tb = tb2;
1007        }
1008}
1009
1010static int
1011lnet_match_networks(char **networksp, char *ip2nets, __u32 *ipaddrs, int nip)
1012{
1013        static char networks[LNET_SINGLE_TEXTBUF_NOB];
1014        static char source[LNET_SINGLE_TEXTBUF_NOB];
1015
1016        struct list_head raw_entries;
1017        struct list_head matched_nets;
1018        struct list_head current_nets;
1019        struct list_head *t;
1020        struct list_head *t2;
1021        struct lnet_text_buf *tb;
1022        struct lnet_text_buf *temp;
1023        struct lnet_text_buf *tb2;
1024        __u32 net1;
1025        __u32 net2;
1026        int len;
1027        int count;
1028        int dup;
1029        int rc;
1030
1031        INIT_LIST_HEAD(&raw_entries);
1032        if (lnet_str2tbs_sep(&raw_entries, ip2nets) < 0) {
1033                CERROR("Error parsing ip2nets\n");
1034                LASSERT(!lnet_tbnob);
1035                return -EINVAL;
1036        }
1037
1038        INIT_LIST_HEAD(&matched_nets);
1039        INIT_LIST_HEAD(&current_nets);
1040        networks[0] = 0;
1041        count = 0;
1042        len = 0;
1043        rc = 0;
1044
1045        list_for_each_entry_safe(tb, temp, &raw_entries, ltb_list) {
1046                strncpy(source, tb->ltb_text, sizeof(source));
1047                source[sizeof(source) - 1] = '\0';
1048
1049                /* replace ltb_text with the network(s) add on match */
1050                rc = lnet_match_network_tokens(tb->ltb_text, ipaddrs, nip);
1051                if (rc < 0)
1052                        break;
1053
1054                list_del(&tb->ltb_list);
1055
1056                if (!rc) {                /* no match */
1057                        lnet_free_text_buf(tb);
1058                        continue;
1059                }
1060
1061                /* split into separate networks */
1062                INIT_LIST_HEAD(&current_nets);
1063                list_add(&tb->ltb_list, &current_nets);
1064                rc = lnet_splitnets(source, &current_nets);
1065                if (rc < 0)
1066                        break;
1067
1068                dup = 0;
1069                list_for_each(t, &current_nets) {
1070                        tb = list_entry(t, struct lnet_text_buf, ltb_list);
1071                        net1 = lnet_netspec2net(tb->ltb_text);
1072                        LASSERT(net1 != LNET_NIDNET(LNET_NID_ANY));
1073
1074                        list_for_each(t2, &matched_nets) {
1075                                tb2 = list_entry(t2, struct lnet_text_buf,
1076                                                 ltb_list);
1077                                net2 = lnet_netspec2net(tb2->ltb_text);
1078                                LASSERT(net2 != LNET_NIDNET(LNET_NID_ANY));
1079
1080                                if (net1 == net2) {
1081                                        dup = 1;
1082                                        break;
1083                                }
1084                        }
1085
1086                        if (dup)
1087                                break;
1088                }
1089
1090                if (dup) {
1091                        lnet_free_text_bufs(&current_nets);
1092                        continue;
1093                }
1094
1095                list_for_each_safe(t, t2, &current_nets) {
1096                        tb = list_entry(t, struct lnet_text_buf, ltb_list);
1097
1098                        list_del(&tb->ltb_list);
1099                        list_add_tail(&tb->ltb_list, &matched_nets);
1100
1101                        len += snprintf(networks + len, sizeof(networks) - len,
1102                                        "%s%s", !len ? "" : ",",
1103                                        tb->ltb_text);
1104
1105                        if (len >= sizeof(networks)) {
1106                                CERROR("Too many matched networks\n");
1107                                rc = -E2BIG;
1108                                goto out;
1109                        }
1110                }
1111
1112                count++;
1113        }
1114
1115 out:
1116        lnet_free_text_bufs(&raw_entries);
1117        lnet_free_text_bufs(&matched_nets);
1118        lnet_free_text_bufs(&current_nets);
1119        LASSERT(!lnet_tbnob);
1120
1121        if (rc < 0)
1122                return rc;
1123
1124        *networksp = networks;
1125        return count;
1126}
1127
1128static int
1129lnet_ipaddr_enumerate(__u32 **ipaddrsp)
1130{
1131        int up;
1132        __u32 netmask;
1133        __u32 *ipaddrs;
1134        __u32 *ipaddrs2;
1135        int nip;
1136        char **ifnames;
1137        int nif = lnet_ipif_enumerate(&ifnames);
1138        int i;
1139        int rc;
1140
1141        if (nif <= 0)
1142                return nif;
1143
1144        LIBCFS_ALLOC(ipaddrs, nif * sizeof(*ipaddrs));
1145        if (!ipaddrs) {
1146                CERROR("Can't allocate ipaddrs[%d]\n", nif);
1147                lnet_ipif_free_enumeration(ifnames, nif);
1148                return -ENOMEM;
1149        }
1150
1151        for (i = nip = 0; i < nif; i++) {
1152                if (!strcmp(ifnames[i], "lo"))
1153                        continue;
1154
1155                rc = lnet_ipif_query(ifnames[i], &up, &ipaddrs[nip], &netmask);
1156                if (rc) {
1157                        CWARN("Can't query interface %s: %d\n",
1158                              ifnames[i], rc);
1159                        continue;
1160                }
1161
1162                if (!up) {
1163                        CWARN("Ignoring interface %s: it's down\n",
1164                              ifnames[i]);
1165                        continue;
1166                }
1167
1168                nip++;
1169        }
1170
1171        lnet_ipif_free_enumeration(ifnames, nif);
1172
1173        if (nip == nif) {
1174                *ipaddrsp = ipaddrs;
1175        } else {
1176                if (nip > 0) {
1177                        LIBCFS_ALLOC(ipaddrs2, nip * sizeof(*ipaddrs2));
1178                        if (!ipaddrs2) {
1179                                CERROR("Can't allocate ipaddrs[%d]\n", nip);
1180                                nip = -ENOMEM;
1181                        } else {
1182                                memcpy(ipaddrs2, ipaddrs,
1183                                       nip * sizeof(*ipaddrs));
1184                                *ipaddrsp = ipaddrs2;
1185                                rc = nip;
1186                        }
1187                }
1188                LIBCFS_FREE(ipaddrs, nip * sizeof(*ipaddrs));
1189        }
1190        return nip;
1191}
1192
1193int
1194lnet_parse_ip2nets(char **networksp, char *ip2nets)
1195{
1196        __u32 *ipaddrs = NULL;
1197        int nip = lnet_ipaddr_enumerate(&ipaddrs);
1198        int rc;
1199
1200        if (nip < 0) {
1201                LCONSOLE_ERROR_MSG(0x117,
1202                                   "Error %d enumerating local IP interfaces for ip2nets to match\n",
1203                                   nip);
1204                return nip;
1205        }
1206
1207        if (!nip) {
1208                LCONSOLE_ERROR_MSG(0x118,
1209                                   "No local IP interfaces for ip2nets to match\n");
1210                return -ENOENT;
1211        }
1212
1213        rc = lnet_match_networks(networksp, ip2nets, ipaddrs, nip);
1214        LIBCFS_FREE(ipaddrs, nip * sizeof(*ipaddrs));
1215
1216        if (rc < 0) {
1217                LCONSOLE_ERROR_MSG(0x119, "Error %d parsing ip2nets\n", rc);
1218                return rc;
1219        }
1220
1221        if (!rc) {
1222                LCONSOLE_ERROR_MSG(0x11a,
1223                                   "ip2nets does not match any local IP interfaces\n");
1224                return -ENOENT;
1225        }
1226
1227        return 0;
1228}
1229