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