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