Whamcloud - gitweb
Update copyrights on source files changed since 2010-02-15.
[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         cfs_list_t         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         cfs_list_t         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, cfs_list_t *nilist)
110 {
111         cfs_list_t       *tmp;
112         lnet_ni_t        *ni;
113
114         cfs_list_for_each (tmp, nilist) {
115                 ni = cfs_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, cfs_list_t *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         cfs_list_add_tail(&ni->ni_list, nilist);
151         return ni;
152 }
153
154 int
155 lnet_parse_networks(cfs_list_t *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 (!cfs_list_empty(nilist));
291         return 0;
292
293  failed:
294         while (!cfs_list_empty(nilist)) {
295                 ni = cfs_list_entry(nilist->next, lnet_ni_t, ni_list);
296                 
297                 cfs_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(cfs_list_t *tbs)
344 {
345         lnet_text_buf_t  *ltb;
346
347         while (!cfs_list_empty(tbs)) {
348                 ltb = cfs_list_entry(tbs->next, lnet_text_buf_t, ltb_list);
349
350                 cfs_list_del(&ltb->ltb_list);
351                 lnet_free_text_buf(ltb);
352         }
353 }
354
355 void
356 lnet_print_text_bufs(cfs_list_t *tbs)
357 {
358         cfs_list_t        *tmp;
359         lnet_text_buf_t   *ltb;
360
361         cfs_list_for_each (tmp, tbs) {
362                 ltb = cfs_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 (cfs_list_t *tbs, char *str) 
372 {
373         cfs_list_t        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                         cfs_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         cfs_list_splice(&pending, tbs->prev);
425         return 0;
426 }
427
428 int
429 lnet_expand1tb (cfs_list_t *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         cfs_list_add_tail(&ltb->ltb_list, list);
450         return 0;
451 }
452
453 int
454 lnet_str2tbs_expand (cfs_list_t *tbs, char *str)
455 {
456         char              num[16];
457         cfs_list_t        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         cfs_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         cfs_list_t        nets;
552         cfs_list_t        gateways;
553         cfs_list_t       *tmp1;
554         cfs_list_t       *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                 cfs_list_add_tail(tmp1, tmp2);
610                 
611                 while (tmp1 != tmp2) {
612                         ltb = cfs_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                                 cfs_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 (!cfs_list_empty(&nets));
644         LASSERT (!cfs_list_empty(&gateways));
645
646         cfs_list_for_each (tmp1, &nets) {
647                 ltb = cfs_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                 cfs_list_for_each (tmp2, &gateways) {
652                         ltb = cfs_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(cfs_list_t *tbs, int *im_a_router)
685 {
686         lnet_text_buf_t   *ltb;
687
688         while (!cfs_list_empty(tbs)) {
689                 ltb = cfs_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                 cfs_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         cfs_list_t        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(cfs_list_t *exprs)
726 {
727         cfs_list_t        *e;
728         lnet_range_expr_t *lre;
729
730         cfs_list_for_each(e, exprs) {
731                 lre = cfs_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(cfs_list_t *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         cfs_list_add(&lre->lre_list, exprs);
761         return 0;
762 }
763
764 void
765 lnet_destroy_range_exprs(cfs_list_t *exprs)
766 {
767         lnet_range_expr_t *lre;
768         
769         while (!cfs_list_empty(exprs)) {
770                 lre = cfs_list_entry(exprs->next, lnet_range_expr_t, lre_list);
771                 
772                 cfs_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(cfs_list_t *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         cfs_list_t         exprs[4];
855         cfs_list_t        *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                         cfs_list_for_each(e, &exprs[j]) {
894                                 re = cfs_list_entry(e, lnet_range_expr_t,
895                                                     lre_list);
896
897                                 if (re->lre_min <= n &&
898                                     re->lre_max >= n &&
899                                     (n - re->lre_min) % re->lre_stride == 0) {
900                                         match = 1;
901                                         break;
902                                 }
903                         }
904                 }
905         }
906         
907         rc = match ? 1 : 0;
908
909  out:
910         for (i = 0; i < 4; i++)
911                 lnet_destroy_range_exprs(&exprs[i]);
912         LASSERT (lnet_re_alloc == 0);
913         
914         return rc;
915 }
916
917 int
918 lnet_match_network_tokens(char *net_entry, __u32 *ipaddrs, int nip)
919 {
920         static char tokens[LNET_SINGLE_TEXTBUF_NOB];
921
922         int   matched = 0;
923         int   ntokens = 0;
924         int   len;
925         char *net = NULL;
926         char *sep;
927         char *token;
928         int   rc;
929
930         LASSERT (strlen(net_entry) < sizeof(tokens));
931
932         /* work on a copy of the string */
933         strcpy(tokens, net_entry);
934         sep = tokens;
935         for (;;) {
936                 /* scan for token start */
937                 while (cfs_iswhite(*sep))
938                         sep++;
939                 if (*sep == 0)
940                         break;
941                 
942                 token = sep++;
943                 
944                 /* scan for token end */
945                 while (*sep != 0 && !cfs_iswhite(*sep))
946                         sep++;
947                 if (*sep != 0)
948                         *sep++ = 0;
949                 
950                 if (ntokens++ == 0) {
951                         net = token;
952                         continue;
953                 }
954
955                 len = strlen(token);
956                 
957                 rc = lnet_match_network_token(token, ipaddrs, nip);
958                 if (rc < 0) {
959                         lnet_syntax("ip2nets", net_entry,
960                                     (int)(token - tokens), len);
961                         return rc;
962                 }
963
964                 matched |= (rc != 0);
965         }
966         
967         if (!matched)
968                 return 0;
969         
970         strcpy(net_entry, net);                 /* replace with matched net */
971         return 1;
972 }
973
974 __u32
975 lnet_netspec2net(char *netspec)
976 {
977         char   *bracket = strchr(netspec, '(');
978         __u32   net;
979
980         if (bracket != NULL)
981                 *bracket = 0;
982
983         net = libcfs_str2net(netspec);
984
985         if (bracket != NULL)
986                 *bracket = '(';
987
988         return net;
989 }
990
991 int
992 lnet_splitnets(char *source, cfs_list_t *nets)
993 {
994         int               offset = 0;
995         int               offset2;
996         int               len;
997         lnet_text_buf_t  *tb;
998         lnet_text_buf_t  *tb2;
999         cfs_list_t       *t;
1000         char             *sep;
1001         char             *bracket;
1002         __u32             net;
1003
1004         LASSERT (!cfs_list_empty(nets));
1005         LASSERT (nets->next == nets->prev);     /* single entry */
1006
1007         tb = cfs_list_entry(nets->next, lnet_text_buf_t, ltb_list);
1008
1009         for (;;) {
1010                 sep = strchr(tb->ltb_text, ',');
1011                 bracket = strchr(tb->ltb_text, '(');
1012
1013                 if (sep != NULL && 
1014                     bracket != NULL && 
1015                     bracket < sep) {
1016                         /* netspec lists interfaces... */
1017
1018                         offset2 = offset + (int)(bracket - tb->ltb_text);
1019                         len = strlen(bracket);
1020
1021                         bracket = strchr(bracket + 1, ')');
1022
1023                         if (bracket == NULL ||
1024                             !(bracket[1] == ',' || bracket[1] == 0)) {
1025                                 lnet_syntax("ip2nets", source, offset2, len);
1026                                 return -EINVAL;
1027                         }
1028
1029                         sep = (bracket[1] == 0) ? NULL : bracket + 1;
1030                 }
1031
1032                 if (sep != NULL)
1033                         *sep++ = 0;
1034
1035                 net = lnet_netspec2net(tb->ltb_text);
1036                 if (net == LNET_NIDNET(LNET_NID_ANY)) {
1037                         lnet_syntax("ip2nets", source, offset,
1038                                     strlen(tb->ltb_text));
1039                         return -EINVAL;
1040                 }
1041
1042                 cfs_list_for_each(t, nets) {
1043                         tb2 = cfs_list_entry(t, lnet_text_buf_t, ltb_list);
1044
1045                         if (tb2 == tb)
1046                                 continue;
1047                         
1048                         if (net == lnet_netspec2net(tb2->ltb_text)) {
1049                                 /* duplicate network */
1050                                 lnet_syntax("ip2nets", source, offset,
1051                                             strlen(tb->ltb_text));
1052                                 return -EINVAL;
1053                         }
1054                 }
1055
1056                 if (sep == NULL)
1057                         return 0;
1058
1059                 offset += (int)(sep - tb->ltb_text);
1060                 tb2 = lnet_new_text_buf(strlen(sep));
1061                 if (tb2 == NULL)
1062                         return -ENOMEM;
1063
1064                 strcpy(tb2->ltb_text, sep);
1065                 cfs_list_add_tail(&tb2->ltb_list, nets);
1066
1067                 tb = tb2;
1068         }
1069 }
1070
1071 int
1072 lnet_match_networks (char **networksp, char *ip2nets, __u32 *ipaddrs, int nip)
1073 {
1074         static char        networks[LNET_SINGLE_TEXTBUF_NOB];
1075         static char        source[LNET_SINGLE_TEXTBUF_NOB];
1076
1077         cfs_list_t          raw_entries;
1078         cfs_list_t          matched_nets;
1079         cfs_list_t          current_nets;
1080         cfs_list_t         *t;
1081         cfs_list_t         *t2;
1082         lnet_text_buf_t    *tb;
1083         lnet_text_buf_t    *tb2;
1084         __u32               net1;
1085         __u32               net2;
1086         int                 len;
1087         int                 count;
1088         int                 dup;
1089         int                 rc;
1090
1091         CFS_INIT_LIST_HEAD(&raw_entries);
1092         if (lnet_str2tbs_sep(&raw_entries, ip2nets) < 0) {
1093                 CERROR("Error parsing ip2nets\n");
1094                 LASSERT (lnet_tbnob == 0);
1095                 return -EINVAL;
1096         }
1097
1098         CFS_INIT_LIST_HEAD(&matched_nets);
1099         CFS_INIT_LIST_HEAD(&current_nets);
1100         networks[0] = 0;
1101         count = 0;
1102         len = 0;
1103         rc = 0;
1104
1105         while (!cfs_list_empty(&raw_entries)) {
1106                 tb = cfs_list_entry(raw_entries.next, lnet_text_buf_t,
1107                                     ltb_list);
1108
1109                 strncpy(source, tb->ltb_text, sizeof(source)-1);
1110                 source[sizeof(source)-1] = 0;
1111
1112                 /* replace ltb_text with the network(s) add on match */
1113                 rc = lnet_match_network_tokens(tb->ltb_text, ipaddrs, nip);
1114                 if (rc < 0)
1115                         break;
1116
1117                 cfs_list_del(&tb->ltb_list);
1118
1119                 if (rc == 0) {                  /* no match */
1120                         lnet_free_text_buf(tb);
1121                         continue;
1122                 }
1123
1124                 /* split into separate networks */
1125                 CFS_INIT_LIST_HEAD(&current_nets);
1126                 cfs_list_add(&tb->ltb_list, &current_nets);
1127                 rc = lnet_splitnets(source, &current_nets);
1128                 if (rc < 0)
1129                         break;
1130
1131                 dup = 0;
1132                 cfs_list_for_each (t, &current_nets) {
1133                         tb = cfs_list_entry(t, lnet_text_buf_t, ltb_list);
1134                         net1 = lnet_netspec2net(tb->ltb_text);
1135                         LASSERT (net1 != LNET_NIDNET(LNET_NID_ANY));
1136
1137                         cfs_list_for_each(t2, &matched_nets) {
1138                                 tb2 = cfs_list_entry(t2, lnet_text_buf_t,
1139                                                      ltb_list);
1140                                 net2 = lnet_netspec2net(tb2->ltb_text);
1141                                 LASSERT (net2 != LNET_NIDNET(LNET_NID_ANY));
1142
1143                                 if (net1 == net2) {
1144                                         dup = 1;
1145                                         break;
1146                                 }
1147                         }
1148
1149                         if (dup)
1150                                 break;
1151                 }
1152
1153                 if (dup) {
1154                         lnet_free_text_bufs(&current_nets);
1155                         continue;
1156                 }
1157
1158                 cfs_list_for_each_safe(t, t2, &current_nets) {
1159                         tb = cfs_list_entry(t, lnet_text_buf_t, ltb_list);
1160                         
1161                         cfs_list_del(&tb->ltb_list);
1162                         cfs_list_add_tail(&tb->ltb_list, &matched_nets);
1163
1164                         len += snprintf(networks + len, sizeof(networks) - len,
1165                                         "%s%s", (len == 0) ? "" : ",", 
1166                                         tb->ltb_text);
1167                 
1168                         if (len >= sizeof(networks)) {
1169                                 CERROR("Too many matched networks\n");
1170                                 rc = -E2BIG;
1171                                 goto out;
1172                         }
1173                 }
1174                 
1175                 count++;
1176         }
1177
1178  out:
1179         lnet_free_text_bufs(&raw_entries);
1180         lnet_free_text_bufs(&matched_nets);
1181         lnet_free_text_bufs(&current_nets);
1182         LASSERT (lnet_tbnob == 0);
1183
1184         if (rc < 0)
1185                 return rc;
1186
1187         *networksp = networks;
1188         return count;
1189 }
1190
1191 #ifdef __KERNEL__
1192 void
1193 lnet_ipaddr_free_enumeration(__u32 *ipaddrs, int nip)
1194 {
1195         LIBCFS_FREE(ipaddrs, nip * sizeof(*ipaddrs));
1196 }
1197
1198 int
1199 lnet_ipaddr_enumerate (__u32 **ipaddrsp)
1200 {
1201         int        up;
1202         __u32      netmask;
1203         __u32     *ipaddrs;
1204         __u32     *ipaddrs2;
1205         int        nip;
1206         char     **ifnames;
1207         int        nif = libcfs_ipif_enumerate(&ifnames);
1208         int        i;
1209         int        rc;
1210
1211         if (nif <= 0)
1212                 return nif;
1213
1214         LIBCFS_ALLOC(ipaddrs, nif * sizeof(*ipaddrs));
1215         if (ipaddrs == NULL) {
1216                 CERROR("Can't allocate ipaddrs[%d]\n", nif);
1217                 libcfs_ipif_free_enumeration(ifnames, nif);
1218                 return -ENOMEM;
1219         }
1220
1221         for (i = nip = 0; i < nif; i++) {
1222                 if (!strcmp(ifnames[i], "lo"))
1223                         continue;
1224
1225                 rc = libcfs_ipif_query(ifnames[i], &up,
1226                                        &ipaddrs[nip], &netmask);
1227                 if (rc != 0) {
1228                         CWARN("Can't query interface %s: %d\n",
1229                               ifnames[i], rc);
1230                         continue;
1231                 }
1232
1233                 if (!up) {
1234                         CWARN("Ignoring interface %s: it's down\n",
1235                               ifnames[i]);
1236                         continue;
1237                 }
1238
1239                 nip++;
1240         }
1241
1242         libcfs_ipif_free_enumeration(ifnames, nif);
1243
1244         if (nip == nif) {
1245                 *ipaddrsp = ipaddrs;
1246         } else {
1247                 if (nip > 0) {
1248                         LIBCFS_ALLOC(ipaddrs2, nip * sizeof(*ipaddrs2));
1249                         if (ipaddrs2 == NULL) {
1250                                 CERROR("Can't allocate ipaddrs[%d]\n", nip);
1251                                 nip = -ENOMEM;
1252                         } else {
1253                                 memcpy(ipaddrs2, ipaddrs, 
1254                                        nip * sizeof(*ipaddrs));
1255                                 *ipaddrsp = ipaddrs2;
1256                                 rc = nip;
1257                         }
1258                 }
1259                 lnet_ipaddr_free_enumeration(ipaddrs, nif);
1260         }
1261         return nip;
1262 }
1263
1264 int
1265 lnet_parse_ip2nets (char **networksp, char *ip2nets)
1266 {
1267         __u32     *ipaddrs;
1268         int        nip = lnet_ipaddr_enumerate(&ipaddrs);
1269         int        rc;
1270
1271         if (nip < 0) {
1272                 LCONSOLE_ERROR_MSG(0x117, "Error %d enumerating local IP "
1273                                    "interfaces for ip2nets to match\n", nip);
1274                 return nip;
1275         }
1276
1277         if (nip == 0) {
1278                 LCONSOLE_ERROR_MSG(0x118, "No local IP interfaces "
1279                                    "for ip2nets to match\n");
1280                 return -ENOENT;
1281         }
1282
1283         rc = lnet_match_networks(networksp, ip2nets, ipaddrs, nip);
1284         lnet_ipaddr_free_enumeration(ipaddrs, nip);
1285
1286         if (rc < 0) {
1287                 LCONSOLE_ERROR_MSG(0x119, "Error %d parsing ip2nets\n", rc);
1288                 return rc;
1289         }
1290
1291         if (rc == 0) {
1292                 LCONSOLE_ERROR_MSG(0x11a, "ip2nets does not match "
1293                                    "any local IP interfaces\n");
1294                 return -ENOENT;
1295         }
1296
1297         return 0;
1298 }
1299
1300 int
1301 lnet_set_ip_niaddr (lnet_ni_t *ni)
1302 {
1303         __u32  net = LNET_NIDNET(ni->ni_nid);
1304         char **names;
1305         int    n;
1306         __u32  ip;
1307         __u32  netmask;
1308         int    up;
1309         int    i;
1310         int    rc;
1311
1312         /* Convenience for LNDs that use the IP address of a local interface as
1313          * the local address part of their NID */
1314
1315         if (ni->ni_interfaces[0] != NULL) {
1316
1317                 CLASSERT (LNET_MAX_INTERFACES > 1);
1318
1319                 if (ni->ni_interfaces[1] != NULL) {
1320                         CERROR("Net %s doesn't support multiple interfaces\n",
1321                                libcfs_net2str(net));
1322                         return -EPERM;
1323                 }
1324
1325                 rc = libcfs_ipif_query(ni->ni_interfaces[0],
1326                                        &up, &ip, &netmask);
1327                 if (rc != 0) {
1328                         CERROR("Net %s can't query interface %s: %d\n",
1329                                libcfs_net2str(net), ni->ni_interfaces[0], rc);
1330                         return -EPERM;
1331                 }
1332
1333                 if (!up) {
1334                         CERROR("Net %s can't use interface %s: it's down\n",
1335                                libcfs_net2str(net), ni->ni_interfaces[0]);
1336                         return -ENETDOWN;
1337                 }
1338
1339                 ni->ni_nid = LNET_MKNID(net, ip);
1340                 return 0;
1341         }
1342
1343         n = libcfs_ipif_enumerate(&names);
1344         if (n <= 0) {
1345                 CERROR("Net %s can't enumerate interfaces: %d\n",
1346                        libcfs_net2str(net), n);
1347                 return 0;
1348         }
1349
1350         for (i = 0; i < n; i++) {
1351                 if (!strcmp(names[i], "lo")) /* skip the loopback IF */
1352                         continue;
1353
1354                 rc = libcfs_ipif_query(names[i], &up, &ip, &netmask);
1355
1356                 if (rc != 0) {
1357                         CWARN("Net %s can't query interface %s: %d\n",
1358                               libcfs_net2str(net), names[i], rc);
1359                         continue;
1360                 }
1361
1362                 if (!up) {
1363                         CWARN("Net %s ignoring interface %s (down)\n",
1364                               libcfs_net2str(net), names[i]);
1365                         continue;
1366                 }
1367
1368                 libcfs_ipif_free_enumeration(names, n);
1369                 ni->ni_nid = LNET_MKNID(net, ip);
1370                 return 0;
1371         }
1372
1373         CERROR("Net %s can't find any interfaces\n", libcfs_net2str(net));
1374         libcfs_ipif_free_enumeration(names, n);
1375         return -ENOENT;
1376 }
1377 EXPORT_SYMBOL(lnet_set_ip_niaddr);
1378
1379 #endif