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