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