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