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