Whamcloud - gitweb
use generic LIST_HEAD macros instead of linux specific.
[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  *  Copyright (c) 2005 Cluster File Systems, Inc.
5  *
6  *   This file is part of Lustre, http://www.sf.net/projects/lustre/
7  *
8  *   Lustre is free software; you can redistribute it and/or
9  *   modify it under the terms of version 2 of the GNU General Public
10  *   License as published by the Free Software Foundation.
11  *
12  *   Lustre is distributed in the hope that it will be useful,
13  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *   GNU General Public License for more details.
16  *
17  *   You should have received a copy of the GNU General Public License
18  *   along with Lustre; if not, write to the Free Software
19  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20  */
21
22 #define DEBUG_SUBSYSTEM S_LNET
23 #include <lnet/lib-lnet.h>
24
25 typedef struct {                                /* tmp struct for parsing routes */
26         struct list_head   ltb_list;            /* stash on lists */
27         int                ltb_size;            /* allocated size */
28         char               ltb_text[0];         /* text buffer */
29 } lnet_text_buf_t;
30
31 static int lnet_tbnob = 0;                      /* track text buf allocation */
32 #define LNET_MAX_TEXTBUF_NOB     (64<<10)       /* bound allocation */
33 #define LNET_SINGLE_TEXTBUF_NOB  (4<<10)
34
35 typedef struct {
36         struct list_head   lre_list;            /* stash in a list */
37         int                lre_min;             /* min value */
38         int                lre_max;             /* max value */
39         int                lre_stride;          /* stride */
40 } lnet_range_expr_t;
41
42 static int lnet_re_alloc = 0;                   /* track expr allocation */
43
44 void 
45 lnet_syntax(char *name, char *str, int offset, int width)
46 {
47         static char dots[LNET_SINGLE_TEXTBUF_NOB];
48         static char dashes[LNET_SINGLE_TEXTBUF_NOB];
49         
50         memset(dots, '.', sizeof(dots));
51         dots[sizeof(dots)-1] = 0;
52         memset(dashes, '-', sizeof(dashes));
53         dashes[sizeof(dashes)-1] = 0;
54         
55         LCONSOLE_ERROR_MSG(0x10f, "Error parsing '%s=\"%s\"'\n", name, str);
56         LCONSOLE_ERROR_MSG(0x110, "here...........%.*s..%.*s|%.*s|\n", 
57                            (int)strlen(name), dots, offset, dots,
58                             (width < 1) ? 0 : width - 1, dashes);
59 }
60
61 int 
62 lnet_issep (char c)
63 {
64         switch (c) {
65         case '\n':
66         case '\r':
67         case ';':
68                 return 1;
69         default:
70                 return 0;
71         }
72 }
73
74 int
75 lnet_iswhite (char c)
76 {
77         switch (c) {
78         case ' ':
79         case '\t':
80         case '\n':
81         case '\r':
82                 return 1;
83         default:
84                 return 0;
85         }
86 }
87
88 char *
89 lnet_trimwhite(char *str)
90 {
91         char *end;
92         
93         while (lnet_iswhite(*str))
94                 str++;
95         
96         end = str + strlen(str);
97         while (end > str) {
98                 if (!lnet_iswhite(end[-1]))
99                         break;
100                 end--;
101         }
102
103         *end = 0;
104         return str;
105 }
106
107 int
108 lnet_net_unique(__u32 net, struct list_head *nilist)
109 {
110         struct list_head *tmp;
111         lnet_ni_t        *ni;
112
113         list_for_each (tmp, nilist) {
114                 ni = list_entry(tmp, lnet_ni_t, ni_list);
115
116                 if (LNET_NIDNET(ni->ni_nid) == net)
117                         return 0;
118         }
119         
120         return 1;
121 }
122
123 lnet_ni_t *
124 lnet_new_ni(__u32 net, struct list_head *nilist)
125 {
126         lnet_ni_t *ni;
127
128         if (!lnet_net_unique(net, nilist)) {
129                 LCONSOLE_ERROR_MSG(0x111, "Duplicate network specified: %s\n",
130                                    libcfs_net2str(net));
131                 return NULL;
132         }
133         
134         LIBCFS_ALLOC(ni, sizeof(*ni));
135         if (ni == NULL) {
136                 CERROR("Out of memory creating network %s\n",
137                        libcfs_net2str(net));
138                 return NULL;
139         }
140         
141         /* zero counters/flags, NULL pointers... */
142         memset(ni, 0, sizeof(*ni));
143
144         /* LND will fill in the address part of the NID */
145         ni->ni_nid = LNET_MKNID(net, 0);
146         CFS_INIT_LIST_HEAD(&ni->ni_txq);
147
148         list_add_tail(&ni->ni_list, nilist);
149         return ni;
150 }
151
152 int
153 lnet_parse_networks(struct list_head *nilist, char *networks)
154 {
155         int        tokensize = strlen(networks) + 1;
156         char      *tokens;
157         char      *str;
158         lnet_ni_t *ni;
159         __u32      net;
160         int        nnets = 0;
161
162         if (strlen(networks) > LNET_SINGLE_TEXTBUF_NOB) {
163                 /* _WAY_ conservative */
164                 LCONSOLE_ERROR_MSG(0x112, "Can't parse networks: string too "
165                                    "long\n");
166                 return -EINVAL;
167         }
168
169         LIBCFS_ALLOC(tokens, tokensize);
170         if (tokens == NULL) {
171                 CERROR("Can't allocate net tokens\n");
172                 return -ENOMEM;
173         }
174
175         the_lnet.ln_network_tokens = tokens;
176         the_lnet.ln_network_tokens_nob = tokensize;
177         memcpy (tokens, networks, tokensize);
178         str = tokens;
179         
180         /* Add in the loopback network */
181         ni = lnet_new_ni(LNET_MKNET(LOLND, 0), nilist);
182         if (ni == NULL)
183                 goto failed;
184         
185         while (str != NULL && *str != 0) {
186                 char      *comma = strchr(str, ',');
187                 char      *bracket = strchr(str, '(');
188                 int        niface;
189                 char      *iface;
190
191                 /* NB we don't check interface conflicts here; it's the LNDs
192                  * responsibility (if it cares at all) */
193
194                 if (bracket == NULL ||
195                     (comma != NULL && comma < bracket)) {
196
197                         /* no interface list specified */
198
199                         if (comma != NULL)
200                                 *comma++ = 0;
201                         net = libcfs_str2net(lnet_trimwhite(str));
202                         
203                         if (net == LNET_NIDNET(LNET_NID_ANY)) {
204                                 lnet_syntax("networks", networks, 
205                                             str - tokens, strlen(str));
206                                 LCONSOLE_ERROR_MSG(0x113, "Unrecognised network"
207                                                    " type\n");
208                                 goto failed;
209                         }
210
211                         if (LNET_NETTYP(net) != LOLND && /* loopback is implicit */
212                             lnet_new_ni(net, nilist) == NULL)
213                                 goto failed;
214
215                         str = comma;
216                         continue;
217                 }
218
219                 *bracket = 0;
220                 net = libcfs_str2net(lnet_trimwhite(str));
221                 if (net == LNET_NIDNET(LNET_NID_ANY)) {
222                         lnet_syntax("networks", networks,
223                                     str - tokens, strlen(str));
224                         goto failed;
225                 } 
226
227                 if (nnets > 0 &&
228                     the_lnet.ln_ptlcompat > 0) {
229                         LCONSOLE_ERROR_MSG(0x114, "Only 1 network supported when"
230                                            " 'portals_compatible' is set\n");
231                         goto failed;
232                 }
233
234                 nnets++;
235                 ni = lnet_new_ni(net, nilist);
236                 if (ni == NULL)
237                         goto failed;
238
239                 niface = 0;
240                 iface = bracket + 1;
241
242                 bracket = strchr(iface, ')');
243                 if (bracket == NULL) {
244                         lnet_syntax("networks", networks,
245                                     iface - tokens, strlen(iface));
246                         goto failed;
247                 }
248
249                 *bracket = 0;
250                 do {
251                         comma = strchr(iface, ',');
252                         if (comma != NULL)
253                                 *comma++ = 0;
254                         
255                         iface = lnet_trimwhite(iface);
256                         if (*iface == 0) {
257                                 lnet_syntax("networks", networks, 
258                                             iface - tokens, strlen(iface));
259                                 goto failed;
260                         }
261
262                         if (niface == LNET_MAX_INTERFACES) {
263                                 LCONSOLE_ERROR_MSG(0x115, "Too many interfaces "
264                                                    "for net %s\n",
265                                                    libcfs_net2str(net));
266                                 goto failed;
267                         }
268
269                         ni->ni_interfaces[niface++] = iface;
270                         iface = comma;
271                 } while (iface != NULL);
272
273                 str = bracket + 1;
274                 comma = strchr(bracket + 1, ',');
275                 if (comma != NULL) {
276                         *comma = 0;
277                         str = lnet_trimwhite(str);
278                         if (*str != 0) {
279                                 lnet_syntax("networks", networks,
280                                             str - tokens, strlen(str));
281                                 goto failed;
282                         }
283                         str = comma + 1;
284                         continue;
285                 }
286                 
287                 str = lnet_trimwhite(str);
288                 if (*str != 0) {
289                         lnet_syntax("networks", networks,
290                                     str - tokens, strlen(str));
291                         goto failed;
292                 }
293         }
294
295         LASSERT (!list_empty(nilist));
296         return 0;
297
298  failed:
299         while (!list_empty(nilist)) {
300                 ni = list_entry(nilist->next, lnet_ni_t, ni_list);
301                 
302                 list_del(&ni->ni_list);
303                 LIBCFS_FREE(ni, sizeof(*ni));
304         }
305         LIBCFS_FREE(tokens, tokensize);
306         the_lnet.ln_network_tokens = NULL;
307
308         return -EINVAL;
309 }
310
311 lnet_text_buf_t *
312 lnet_new_text_buf (int str_len) 
313 {
314         lnet_text_buf_t *ltb;
315         int              nob;
316
317         /* NB allocate space for the terminating 0 */
318         nob = offsetof(lnet_text_buf_t, ltb_text[str_len + 1]);
319         if (nob > LNET_SINGLE_TEXTBUF_NOB) {
320                 /* _way_ conservative for "route net gateway..." */
321                 CERROR("text buffer too big\n");
322                 return NULL;
323         }
324
325         if (lnet_tbnob + nob > LNET_MAX_TEXTBUF_NOB) {
326                 CERROR("Too many text buffers\n");
327                 return NULL;
328         }
329         
330         LIBCFS_ALLOC(ltb, nob);
331         if (ltb == NULL)
332                 return NULL;
333
334         ltb->ltb_size = nob;
335         ltb->ltb_text[0] = 0;
336         lnet_tbnob += nob;
337         return ltb;
338 }
339
340 void
341 lnet_free_text_buf (lnet_text_buf_t *ltb)
342 {
343         lnet_tbnob -= ltb->ltb_size;
344         LIBCFS_FREE(ltb, ltb->ltb_size);
345 }
346
347 void
348 lnet_free_text_bufs(struct list_head *tbs)
349 {
350         lnet_text_buf_t  *ltb;
351         
352         while (!list_empty(tbs)) {
353                 ltb = list_entry(tbs->next, lnet_text_buf_t, ltb_list);
354                 
355                 list_del(&ltb->ltb_list);
356                 lnet_free_text_buf(ltb);
357         }
358 }
359
360 void
361 lnet_print_text_bufs(struct list_head *tbs)
362 {
363         struct list_head  *tmp;
364         lnet_text_buf_t   *ltb;
365
366         list_for_each (tmp, tbs) {
367                 ltb = list_entry(tmp, lnet_text_buf_t, ltb_list);
368
369                 CDEBUG(D_WARNING, "%s\n", ltb->ltb_text);
370         }
371
372         CDEBUG(D_WARNING, "%d allocated\n", lnet_tbnob);
373 }
374
375 int
376 lnet_str2tbs_sep (struct list_head *tbs, char *str) 
377 {
378         struct list_head  pending;
379         char             *sep;
380         int               nob;
381         int               i;
382         lnet_text_buf_t  *ltb;
383
384         CFS_INIT_LIST_HEAD(&pending);
385
386         /* Split 'str' into separate commands */
387         for (;;) {
388                 /* skip leading whitespace */
389                 while (lnet_iswhite(*str))
390                         str++;
391                 
392                 /* scan for separator or comment */
393                 for (sep = str; *sep != 0; sep++)
394                         if (lnet_issep(*sep) || *sep == '#')
395                                 break;
396
397                 nob = sep - str;
398                 if (nob > 0) {
399                         ltb = lnet_new_text_buf(nob);
400                         if (ltb == NULL) {
401                                 lnet_free_text_bufs(&pending);
402                                 return -1;
403                         }
404                         
405                         for (i = 0; i < nob; i++)
406                                 if (lnet_iswhite(str[i]))
407                                         ltb->ltb_text[i] = ' ';
408                                 else
409                                         ltb->ltb_text[i] = str[i];
410
411                         ltb->ltb_text[nob] = 0;
412
413                         list_add_tail(&ltb->ltb_list, &pending);
414                 }
415
416                 if (*sep == '#') {
417                         /* scan for separator */
418                         do {
419                                 sep++;
420                         } while (*sep != 0 && !lnet_issep(*sep));
421                 }
422                 
423                 if (*sep == 0)
424                         break;
425
426                 str = sep + 1;
427         }
428
429         list_splice(&pending, tbs->prev);
430         return 0;
431 }
432
433 int
434 lnet_expand1tb (struct list_head *list, 
435                char *str, char *sep1, char *sep2, 
436                char *item, int itemlen)
437 {
438         int              len1 = sep1 - str;
439         int              len2 = strlen(sep2 + 1);
440         lnet_text_buf_t *ltb;
441
442         LASSERT (*sep1 == '[');
443         LASSERT (*sep2 == ']');
444
445         ltb = lnet_new_text_buf(len1 + itemlen + len2);
446         if (ltb == NULL)
447                 return -ENOMEM;
448         
449         memcpy(ltb->ltb_text, str, len1);
450         memcpy(&ltb->ltb_text[len1], item, itemlen);
451         memcpy(&ltb->ltb_text[len1+itemlen], sep2 + 1, len2);
452         ltb->ltb_text[len1 + itemlen + len2] = 0;
453         
454         list_add_tail(&ltb->ltb_list, list);
455         return 0;
456 }
457
458 int
459 lnet_str2tbs_expand (struct list_head *tbs, char *str)
460 {
461         char              num[16];
462         struct list_head  pending;
463         char             *sep;
464         char             *sep2;
465         char             *parsed;
466         char             *enditem;
467         int               lo;
468         int               hi;
469         int               stride;
470         int               i;
471         int               nob;
472         int               scanned;
473
474         CFS_INIT_LIST_HEAD(&pending);
475         
476         sep = strchr(str, '[');
477         if (sep == NULL)                        /* nothing to expand */
478                 return 0;
479
480         sep2 = strchr(sep, ']');
481         if (sep2 == NULL)
482                 goto failed;
483
484         for (parsed = sep; parsed < sep2; parsed = enditem) {
485
486                 enditem = ++parsed;
487                 while (enditem < sep2 && *enditem != ',')
488                         enditem++;
489
490                 if (enditem == parsed)          /* no empty items */
491                         goto failed;
492
493                 if (sscanf(parsed, "%d-%d/%d%n", &lo, &hi, &stride, &scanned) < 3) {
494
495                         if (sscanf(parsed, "%d-%d%n", &lo, &hi, &scanned) < 2) {
496
497                                 /* simple string enumeration */
498                                 if (lnet_expand1tb(&pending, str, sep, sep2,
499                                                    parsed, enditem - parsed) != 0)
500                                         goto failed;
501                                 
502                                 continue;
503                         }
504                         
505                         stride = 1;
506                 }
507
508                 /* range expansion */
509
510                 if (enditem != parsed + scanned) /* no trailing junk */
511                         goto failed;
512                         
513                 if (hi < 0 || lo < 0 || stride < 0 || hi < lo || 
514                     (hi - lo) % stride != 0)
515                         goto failed;
516                         
517                 for (i = lo; i <= hi; i += stride) {
518
519                         snprintf(num, sizeof(num), "%d", i);
520                         nob = strlen(num);
521                         if (nob + 1 == sizeof(num))
522                                 goto failed;
523                         
524                         if (lnet_expand1tb(&pending, str, sep, sep2, 
525                                            num, nob) != 0)
526                                 goto failed;
527                 }
528         }
529                 
530         list_splice(&pending, tbs->prev);
531         return 1;
532         
533  failed:
534         lnet_free_text_bufs(&pending);
535         return -1;
536 }
537
538 int
539 lnet_parse_hops (char *str, unsigned int *hops)
540 {
541         int     len = strlen(str);
542         int     nob = len;
543         
544         return (sscanf(str, "%u%n", hops, &nob) >= 1 &&
545                 nob == len &&
546                 *hops > 0 && *hops < 256);
547 }
548
549
550 int
551 lnet_parse_route (char *str, int *im_a_router)
552 {
553         /* static scratch buffer OK (single threaded) */
554         static char       cmd[LNET_SINGLE_TEXTBUF_NOB];
555
556         struct list_head  nets;
557         struct list_head  gateways;
558         struct list_head *tmp1;
559         struct list_head *tmp2;
560         __u32             net;
561         lnet_nid_t        nid;
562         lnet_text_buf_t  *ltb;
563         int               rc;
564         char             *sep;
565         char             *token = str;
566         int               ntokens = 0;
567         int               myrc = -1;
568         unsigned int      hops;
569         int               got_hops = 0;
570
571         CFS_INIT_LIST_HEAD(&gateways);
572         CFS_INIT_LIST_HEAD(&nets);
573
574         /* save a copy of the string for error messages */
575         strncpy(cmd, str, sizeof(cmd) - 1);
576         cmd[sizeof(cmd) - 1] = 0;
577
578         sep = str;
579         for (;;) {
580                 /* scan for token start */
581                 while (lnet_iswhite(*sep))
582                         sep++;
583                 if (*sep == 0) {
584                         if (ntokens < (got_hops ? 3 : 2))
585                                 goto token_error;
586                         break;
587                 }
588
589                 ntokens++;
590                 token = sep++;
591
592                 /* scan for token end */
593                 while (*sep != 0 && !lnet_iswhite(*sep))
594                         sep++;
595                 if (*sep != 0)
596                         *sep++ = 0;
597                 
598                 if (ntokens == 1) {
599                         tmp2 = &nets;           /* expanding nets */
600                 } else if (ntokens == 2 &&
601                            lnet_parse_hops(token, &hops)) {
602                         got_hops = 1;           /* got a hop count */
603                         continue;
604                 } else {
605                         tmp2 = &gateways;       /* expanding gateways */
606                 }
607                 
608                 ltb = lnet_new_text_buf(strlen(token));
609                 if (ltb == NULL)
610                         goto out;
611
612                 strcpy(ltb->ltb_text, token);
613                 tmp1 = &ltb->ltb_list;
614                 list_add_tail(tmp1, tmp2);
615                 
616                 while (tmp1 != tmp2) {
617                         ltb = list_entry(tmp1, lnet_text_buf_t, ltb_list);
618
619                         rc = lnet_str2tbs_expand(tmp1->next, ltb->ltb_text);
620                         if (rc < 0)
621                                 goto token_error;
622
623                         tmp1 = tmp1->next;
624                         
625                         if (rc > 0) {           /* expanded! */
626                                 list_del(&ltb->ltb_list);
627                                 lnet_free_text_buf(ltb);
628                                 continue;
629                         }
630
631                         if (ntokens == 1) {
632                                 net = libcfs_str2net(ltb->ltb_text);
633                                 if (net == LNET_NIDNET(LNET_NID_ANY) ||
634                                     LNET_NETTYP(net) == LOLND)
635                                         goto token_error;
636                         } else {
637                                 nid = libcfs_str2nid(ltb->ltb_text);
638                                 if (nid == LNET_NID_ANY ||
639                                     LNET_NETTYP(LNET_NIDNET(nid)) == LOLND)
640                                         goto token_error;
641                         }
642                 }
643         }
644
645         if (!got_hops)
646                 hops = 1;
647
648         LASSERT (!list_empty(&nets));
649         LASSERT (!list_empty(&gateways));
650
651         list_for_each (tmp1, &nets) {
652                 ltb = list_entry(tmp1, lnet_text_buf_t, ltb_list);
653                 net = libcfs_str2net(ltb->ltb_text);
654                 LASSERT (net != LNET_NIDNET(LNET_NID_ANY));
655
656                 list_for_each (tmp2, &gateways) {
657                         ltb = list_entry(tmp2, lnet_text_buf_t, ltb_list);
658                         nid = libcfs_str2nid(ltb->ltb_text);
659                         LASSERT (nid != LNET_NID_ANY);
660
661                         if (lnet_islocalnid(nid)) {
662                                 *im_a_router = 1;
663                                 continue;
664                         }
665                         
666                         rc = lnet_add_route (net, hops, nid);
667                         if (rc != 0) {
668                                 CERROR("Can't create route "
669                                        "to %s via %s\n",
670                                        libcfs_net2str(net),
671                                        libcfs_nid2str(nid));
672                                 goto out;
673                         }
674                 }
675         }
676
677         myrc = 0;
678         goto out;
679         
680  token_error:
681         lnet_syntax("routes", cmd, token - str, strlen(token));
682  out:
683         lnet_free_text_bufs(&nets);
684         lnet_free_text_bufs(&gateways);
685         return myrc;
686 }
687
688 int
689 lnet_parse_route_tbs(struct list_head *tbs, int *im_a_router)
690 {
691         lnet_text_buf_t   *ltb;
692
693         while (!list_empty(tbs)) {
694                 ltb = list_entry(tbs->next, lnet_text_buf_t, ltb_list);
695
696                 if (lnet_parse_route(ltb->ltb_text, im_a_router) < 0) {
697                         lnet_free_text_bufs(tbs);
698                         return -EINVAL;
699                 }
700
701                 list_del(&ltb->ltb_list);
702                 lnet_free_text_buf(ltb);
703         }
704
705         return 0;
706 }
707
708 int
709 lnet_parse_routes (char *routes, int *im_a_router)
710 {
711         struct list_head  tbs;
712         int               rc = 0;
713
714         *im_a_router = 0;
715
716         if (the_lnet.ln_ptlcompat > 0 && 
717             routes[0] != 0) {
718                 /* Can't route when running in compatibility mode */
719                 LCONSOLE_ERROR_MSG(0x116, "Route tables are not supported when "
720                                    "'portals_compatible' is set\n");
721                 return -EINVAL;
722         }
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