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