]> arthur.barton.de Git - netatalk.git/blob - etc/atalkd/config.c
AC_HEADER_STDC autoconf change
[netatalk.git] / etc / atalkd / config.c
1 /*
2  * $Id: config.c,v 1.6 2001-09-06 20:00:59 rufustfirefly Exp $
3  *
4  * Copyright (c) 1990,1993 Regents of The University of Michigan.
5  * All Rights Reserved. See COPYRIGHT.
6  */
7
8 #ifdef HAVE_CONFIG_H
9 #include "config.h"
10 #endif /* HAVE_CONFIG_H */
11
12 #include <sys/types.h>
13 #include <sys/stat.h>
14 #include <sys/socket.h>
15 #include <sys/ioctl.h>
16 #include <syslog.h>
17 #include <sys/param.h>
18 #ifdef TRU64
19 #include <sys/mbuf.h>
20 #include <net/route.h>
21 #endif /* TRU64 */
22 #include <net/if.h>
23 #include <netatalk/at.h>
24 #include <netatalk/endian.h>
25 #include <atalk/paths.h>
26 #include <atalk/util.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29
30 /* STDC check */
31 #if STDC_HEADERS
32 #include <string.h>
33 #else /* STDC_HEADERS */
34 #ifndef HAVE_STRCHR
35 #define strchr index
36 #define strrchr index
37 #endif /* HAVE_STRCHR */
38 char *strchr (), *strrchr ();
39 #ifndef HAVE_MEMCPY
40 #define memcpy(d,s,n) bcopy ((s), (d), (n))
41 #define memmove(d,s,n) bcopy ((s), (d), (n))
42 #endif /* ! HAVE_MEMCPY */
43 #endif /* STDC_HEADERS */
44
45 #include <ctype.h>
46 #ifdef HAVE_FCNTL_H
47 #include <fcntl.h>
48 #endif /* HAVE_FCNTL_H */
49 #include <errno.h>
50
51 #ifdef __svr4__
52 #include <sys/sockio.h>
53 #include <sys/stropts.h>
54 #endif /* __svr4__ */
55
56 #include "interface.h"
57 #include "multicast.h"
58 #include "rtmp.h"
59 #include "zip.h"
60 #include "list.h"
61
62 #ifndef IFF_SLAVE /* a little backward compatibility */
63 #define IFF_SLAVE 0
64 #endif /* IFF_SLAVE */
65
66 int     router(), dontroute(), seed(), phase(), net(), addr(), zone();
67
68 static struct param {
69     char        *p_name;
70     int         (*p_func)();
71 } params[] = {
72     { "router", router },
73     { "dontroute", dontroute },
74     { "seed",   seed },
75     { "phase",  phase },
76     { "net",    net },
77     { "addr",   addr },
78     { "zone",   zone },
79 };
80
81 static char **
82 parseline( line )
83     char        *line;
84 {
85     char        *p;
86     int         argc = 0;
87     static char *argv[ 128 ];
88     static char buf[ 1024 ];
89
90     if ( *line == '#' ) {
91         return( NULL );
92     }
93     argc = 0;
94
95     memset(argv, 0, sizeof(argv));
96     strcpy( buf, line );
97     p = buf;
98
99     /*
100      * This parser should be made more powerful -- it should
101      * handle various escapes, e.g. \" and \031.
102      */
103     while ( *p != '\0' ) {
104         while ( *p != '\0' && *p != '"' && isspace( *p )) {
105             p++;
106         }
107         if ( *p == '\0' ) {
108             argv[ argc ] = 0;
109             break;
110         }
111         if ( *p == '"' ) {
112             argv[ argc ] = ++p;
113             while ( *p != '\0' && *p != '"' ) {
114                 p++;
115             }
116         } else {
117             argv[ argc ] = p;
118             while ( *p != '\0' && !isspace( *p )) {
119                 p++;
120             }
121         }
122         *p++ = '\0';
123         argc++;
124     }
125     if ( argv[ 0 ] == '\0' || *argv[ 0 ] == '#' ) {
126         return( NULL );
127     }
128     return( argv );
129 }
130
131 int writeconf( cf )
132     char        *cf;
133 {
134     struct stat         st;
135     char                *path, *p, newpath[ MAXPATHLEN ], line[ 1024 ];
136     char                **argv;
137     FILE                *conf, *newconf;
138     struct interface    *iface;
139     struct list         *l;
140     int                 mode = 0644, fd;
141
142     if ( cf == NULL ) {
143         path = _PATH_ATALKDCONF;
144     } else {
145         path = cf;
146     }
147
148     /* check if old conf is writable */
149     if ( stat( path, &st ) == 0 ) {
150         if (( st.st_mode & S_IWUSR ) == 0 ) {
151             syslog( LOG_INFO, "%s not writable, won't rewrite", path );
152             return( -1 );
153         }
154          mode = st.st_mode;
155     }
156
157     if (( p = strrchr( path, '/' )) == NULL ) {
158         strcpy( newpath, _PATH_ATALKDTMP );
159     } else {
160         sprintf( newpath, "%.*s/%s", (int)(p - path), path, _PATH_ATALKDTMP );
161     }
162     if (( fd = open( newpath, O_WRONLY|O_CREAT|O_TRUNC, mode )) < 0 ) {
163         syslog( LOG_ERR, "%s: %m", newpath );
164         return( -1 );
165     }
166     if (( newconf = fdopen( fd, "w" )) == NULL ) {
167         syslog( LOG_ERR, "fdreopen %s: %m", newpath );
168         return( -1 );
169     }
170
171     if (( conf = fopen( path, "r" )) == NULL && cf ) {
172         syslog( LOG_ERR, "%s: %m", path );
173         return( -1 );
174     }
175
176     iface = interfaces->i_next;
177
178     while ( conf == NULL || fgets( line, sizeof( line ), conf ) != NULL ) {
179         if ( conf != NULL && ( argv = parseline( line )) == NULL ) {
180             if ( fputs( line, newconf ) == EOF ) {
181                 syslog( LOG_ERR, "fputs: %m" );
182                 return( -1 );
183             }
184             continue;
185         }
186
187         /* write real lines */
188         if ( iface ) {
189             fprintf( newconf, "%s", iface->i_name );
190             if ( iface->i_flags & IFACE_RSEED ) {
191                 fprintf( newconf, " -router" );
192             } else if ( iface->i_flags & IFACE_SEED ) {
193                 fprintf( newconf, " -seed" );
194             }
195             if ( iface->i_flags & IFACE_DONTROUTE) {
196                 fprintf( newconf, " -dontroute");
197             }
198
199             fprintf( newconf, " -phase %d",
200                     ( iface->i_flags & IFACE_PHASE1 ) ? 1 : 2 );
201             fprintf( newconf, " -net %d", ntohs( iface->i_rt->rt_firstnet ));
202             if ( iface->i_rt->rt_lastnet != iface->i_rt->rt_firstnet ) {
203                 fprintf( newconf, "-%d", ntohs( iface->i_rt->rt_lastnet ));
204             }
205             fprintf( newconf, " -addr %u.%u",
206                     ntohs( iface->i_addr.sat_addr.s_net ),
207                     iface->i_addr.sat_addr.s_node );
208             for ( l = iface->i_rt->rt_zt; l; l = l->l_next ) {
209                 fprintf( newconf, " -zone \"%.*s\"",
210                         ((struct ziptab *)l->l_data)->zt_len,
211                         ((struct ziptab *)l->l_data)->zt_name );
212             }
213             fprintf( newconf, "\n" );
214
215             iface = iface->i_next;
216             if ( conf == NULL && iface == NULL ) {
217                 break;
218             }
219         }
220     }
221     if ( conf != NULL ) {
222         fclose( conf );
223     }
224     fclose( newconf );
225
226     if ( rename( newpath, path ) < 0 ) {
227         syslog( LOG_ERR, "rename %s to %s: %m", newpath, path );
228         return( -1 );
229     }
230     return( 0 );
231 }
232
233 /*
234  * Read our config file. If it's not there, return -1. If it is there,
235  * but has syntax errors, exit. Format of the file is as follows:
236  *
237  *      interface [ -seed ] [ -phase number ] [ -net net-range ]
238  *      [ -addr net.node ] [ -zone zonename ]...
239  * e.g.
240  *      le0 -phase 1 -net 7938 -zone Argus
241  * or
242  *      le0 -phase 2 -net 8043-8044 -zone Argus -zone "Research Systems"
243  *      le0 -phase 1 -net 7938 -zone Argus
244  *
245  * Pretty much everything is optional. Anything that is unspecified is
246  * searched for on the network.  If -seed is not specified, the
247  * configuration is assumed to be soft, i.e. it can be overridden by
248  * another router. If -seed is specified, atalkd will exit if another
249  * router disagrees.  If the phase is unspecified, it defaults to phase
250  * 2 (the default can be overridden on the command line).  -addr can
251  * replace -net, if the network in question isn't a range.  The default
252  * zone for an interface is the first zone encountered for that
253  * interface.
254  */
255 int readconf( cf )
256     char                *cf;
257 {
258     struct ifreq        ifr;
259     struct interface    *iface, *niface;
260     char                line[ 1024 ], **argv, *p;
261     int                 i, j, s, cc;
262     FILE                *conf;
263
264     if ( cf == NULL ) {
265         p = _PATH_ATALKDCONF;
266     } else {
267         p = cf;
268     }
269     if (( conf = fopen( p, "r" )) == NULL ) {
270         return( -1 );
271     }
272
273 #ifndef __svr4__
274     if (( s = socket( AF_APPLETALK, SOCK_DGRAM, 0 )) < 0 ) {
275         perror( "socket" );
276         fclose(conf);
277         return -1;
278     }
279 #endif /* __svr4__ */
280
281     while ( fgets( line, sizeof( line ), conf ) != NULL ) {
282         if (( argv = parseline( line )) == NULL ) {
283             continue;
284         }
285
286 #ifndef __svr4__
287         /*
288          * Check that av[ 0 ] is a valid interface.
289          * Not possible under sysV.
290          */
291         strcpy( ifr.ifr_name, argv[ 0 ] );
292
293         /* for devices that don't support appletalk */
294         if ((ioctl(s, SIOCGIFADDR, &ifr) < 0) && (errno == ENODEV)) {
295           perror(argv[0]);
296           goto read_conf_err;
297         }
298
299         if ( ioctl( s, SIOCGIFFLAGS, &ifr ) < 0 ) {
300             perror( argv[ 0 ] );
301             goto read_conf_err;
302         }
303
304         if (ifr.ifr_flags & (IFF_LOOPBACK | IFF_POINTOPOINT |IFF_SLAVE)) {
305             fprintf( stderr, "%s: can't configure.\n", ifr.ifr_name );
306             goto read_conf_err;
307         }
308
309 #ifdef IFF_MULTICAST
310         if ((ifr.ifr_flags & IFF_MULTICAST) == 0)
311             fprintf(stderr, "%s: multicast may not work properly.\n",
312                     ifr.ifr_name);
313 #endif /* IFF_MULTICAST */
314
315         /* configure hw multicast for this interface. */
316         if (addmulti(ifr.ifr_name, NULL) < 0) {
317           perror(ifr.ifr_name);
318           fprintf(stderr, "Can't configure multicast.\n");
319           goto read_conf_err;
320         }
321 #endif /* __svr4__ */
322
323         if (( niface = newiface( argv[ 0 ] )) == NULL ) {
324             perror( "newiface" );
325             goto read_conf_err;
326         }
327
328         for ( i = 1; argv[ i ]; i += cc ) {
329             if ( argv[ i ][ 0 ] == '-' ) {
330                 argv[ i ]++;
331             }
332             for ( j = 0; j < sizeof( params ) / sizeof( params[ 0 ] ); j++ ) {
333                 if ( strcmp( argv[ i ], params[ j ].p_name ) == 0 ) {
334                     if ( params[ j ].p_func != NULL ) {
335                         cc = (*params[ j ].p_func)( niface, &argv[ i + 1 ] );
336                         if (cc < 0) 
337                           goto read_conf_err;
338                         break;
339                     }
340                 }
341             }
342             if ( j >= sizeof( params ) / sizeof( params[ 0 ] )) {
343                 fprintf( stderr, "%s: attribute not found.\n", argv[ i ] );
344                 goto read_conf_err;
345             }
346         }
347
348         for ( iface = interfaces; iface; iface = iface->i_next ) {
349             if ( strcmp( niface->i_name, iface->i_name ) == 0 &&
350                     ((( niface->i_flags & iface->i_flags &
351                     ( IFACE_PHASE1|IFACE_PHASE2 )) != 0 ) ||
352                     niface->i_flags == 0 || iface->i_flags == 0 )) {
353                 break;
354             }
355         }
356         if ( iface ) {  /* Already have this interface and phase */
357             fprintf( stderr, "%s already configured!\n", niface->i_name );
358             goto read_conf_err;
359         }
360
361         if ( interfaces == NULL ) {
362             interfaces = niface;
363         } else {
364             for ( iface = interfaces; iface->i_next; iface = iface->i_next )
365                 ;
366             iface->i_next = niface;
367         }
368         niface->i_next = NULL;
369     }
370
371 #ifndef __svr4__
372     close( s );
373 #endif /* __svr4__ */
374
375     fclose( conf );
376
377     /*
378      * Note: we've added lo0 to the interface list previously, so we must
379      * have configured more than one interface...
380      */
381     for ( iface = interfaces, cc = 0; iface; iface = iface->i_next, cc++ )
382         ;
383     if ( cc >= IFBASE ) {
384         return( 0 );
385     } else {
386         return( -1 );
387     }
388
389 read_conf_err:
390 #ifndef __svr4__
391     close(s);
392 #endif /* __svr4__ */
393     fclose(conf);
394     return -1;
395 }
396
397 /*ARGSUSED*/
398 int router( iface, av )
399     struct interface    *iface;
400     char                **av;
401 {
402     /* make sure "-router" and "-dontroute" aren't both on the same line. */
403     if (iface->i_flags & IFACE_DONTROUTE) {
404         fprintf( stderr, "Can't specify both -router and -dontroute.\n");
405         return -1;
406     }
407
408     /*
409      * Check to be sure "-router" is before "-zone".
410      */
411     if ( iface->i_czt ) {
412         fprintf( stderr, "Must specify -router before -zone.\n");
413         return -1;
414     }
415
416     /* -router also implies -seed */
417     iface->i_flags |= IFACE_RSEED | IFACE_SEED | IFACE_ISROUTER;
418     return( 1 );
419 }
420
421 /*ARGSUSED*/
422 int dontroute( iface, av )
423     struct interface    *iface;
424     char                **av;
425 {
426     /* make sure "-router" and "-dontroute" aren't both on the same line. */
427     if (iface->i_flags & IFACE_RSEED) {
428         fprintf( stderr, "Can't specify both -router and -dontroute.\n");
429         return -1;
430     }
431
432     iface->i_flags |= IFACE_DONTROUTE;
433     return( 1 );
434 }
435
436 /*ARGSUSED*/
437 int seed( iface, av )
438     struct interface    *iface;
439     char                **av;
440 {
441     /*
442      * Check to be sure "-seed" is before "-zone". we keep the old
443      * semantics of just ignoring this in a routerless world.
444      */
445     if ( iface->i_czt ) {
446         fprintf( stderr, "Must specify -seed before -zone(%s).\n",
447                  iface->i_czt->zt_name);
448         return -1;
449     }
450
451     iface->i_flags |= IFACE_SEED;
452     return( 1 );
453 }
454
455 int phase( iface, av )
456     struct interface    *iface;
457     char                **av;
458 {
459     int                 n;
460     char                *pnum;
461
462     if (( pnum = av[ 0 ] ) == NULL ) {
463         fprintf( stderr, "No phase.\n" );
464         return -1;
465     }
466
467     switch ( n = atoi( pnum )) {
468     case 1 :
469         iface->i_flags |= IFACE_PHASE1;
470         break;
471
472     case 2 :
473         iface->i_flags |= IFACE_PHASE2;
474         break;
475
476     default :
477         fprintf( stderr, "No phase %d.\n", n );
478         return -1;
479     }
480     return( 2 );
481 }
482
483 int net( iface, av )
484     struct interface    *iface;
485     char                **av;
486 {
487     char                *nrange;
488     char                *stop;
489     int                 net;
490
491     if (( nrange = av[ 0 ] ) == NULL ) {
492         fprintf( stderr, "No network.\n" );
493         return -1;
494     }
495
496     if (( stop = strchr( nrange, '-' )) != 0 ) {
497         stop++;
498     }
499     net = atoi( nrange );
500     if ( net < 0 || net >= 0xffff ) {
501         fprintf( stderr, "Bad network: %d\n", net );
502         return -1;
503     }
504
505     if ( iface->i_rt == NULL && ( iface->i_rt = newrt(iface)) == NULL ) {
506         perror( "newrt" );
507         return -1;
508     }
509
510     if ( iface->i_flags & IFACE_PHASE1 ) {
511         if ( stop != 0 ) {
512             fprintf( stderr, "Phase 1 doesn't use an address range.\n" );
513             return -1;
514         }
515         if ( iface->i_caddr.sat_addr.s_net != ATADDR_ANYNET &&
516                 ntohs( iface->i_caddr.sat_addr.s_net ) != net ) {
517             fprintf( stderr, "Net-range (%u) doesn't match net %u.\n",
518                     net, ntohs( iface->i_caddr.sat_addr.s_net ));
519             return -1;
520         }
521         iface->i_rt->rt_firstnet = iface->i_rt->rt_lastnet = htons( net );
522     } else if ( iface->i_flags & IFACE_PHASE2 ) {
523         iface->i_rt->rt_firstnet = htons( net );
524         if ( stop != 0 ) {
525             net = atoi( stop );
526             if ( net < 0 || net >= 0xffff ) {
527                 fprintf( stderr, "Bad network: %d\n", net );
528                 return -1;
529             }
530         }
531         iface->i_rt->rt_lastnet = htons( net );
532         if ( iface->i_caddr.sat_addr.s_net != ATADDR_ANYNET &&
533                 ( ntohs( iface->i_rt->rt_firstnet ) >
534                 ntohs( iface->i_caddr.sat_addr.s_net ) ||
535                 ntohs( iface->i_rt->rt_lastnet ) <
536                 ntohs( iface->i_caddr.sat_addr.s_net ))) {
537             fprintf( stderr, "Net-range (%u-%u) doesn't contain net (%u).\n",
538                     ntohs( iface->i_rt->rt_firstnet ),
539                     ntohs( iface->i_rt->rt_lastnet ),
540                     ntohs( iface->i_caddr.sat_addr.s_net ));
541             return -1;
542         }
543         if ( iface->i_rt->rt_firstnet != iface->i_rt->rt_lastnet ) {
544             iface->i_rt->rt_flags |= RTMPTAB_EXTENDED;
545         }
546     } else {
547         fprintf( stderr, "Must specify phase before networks.\n" );
548         return -1;
549     }
550     return( 2 );
551 }
552
553 int addr( iface, av )
554     struct interface    *iface;
555     char                **av;
556 {
557     if ( av[ 0 ] == NULL ) {
558         fprintf( stderr, "No address.\n" );
559         return -1;
560     }
561     if ( atalk_aton( av[ 0 ], &iface->i_caddr.sat_addr ) == 0 ) {
562         fprintf( stderr, "Bad address, %s\n", av[ 0 ] );
563         return -1;
564     }
565
566     if ( iface->i_rt ) {
567         if ( ntohs( iface->i_rt->rt_firstnet ) >
568                 ntohs( iface->i_caddr.sat_addr.s_net ) ||
569                 ntohs( iface->i_rt->rt_lastnet ) <
570                 ntohs( iface->i_caddr.sat_addr.s_net )) {
571             fprintf( stderr, "Net (%u) not in net-range (%u-%u).\n",
572                     ntohs( iface->i_caddr.sat_addr.s_net ),
573                     ntohs( iface->i_rt->rt_firstnet ),
574                     ntohs( iface->i_rt->rt_lastnet ));
575             return -1;
576         }
577     } else {
578         if (( iface->i_rt = newrt(iface)) == NULL ) {
579             perror( "newrt" );
580             return -1;
581         }
582         iface->i_rt->rt_firstnet = iface->i_rt->rt_lastnet =
583                 iface->i_caddr.sat_addr.s_net;
584     }
585
586     return( 2 );
587 }
588
589 int zone( iface, av )
590     struct interface    *iface;
591     char                **av;
592 {
593     struct ziptab       *zt;
594     char                *zname;
595
596     if (( zname = av[ 0 ] ) == NULL ) {
597         fprintf( stderr, "No zone.\n" );
598         return -1;
599     }
600
601     /*
602      * Only process "-zone" if this interface has "-seed".  We keep our
603      * list of configured zones in the interface structure.  Then we can
604      * check that the network has given us good zones.
605      */
606     if ( iface->i_flags & IFACE_SEED ) {
607         if ( iface->i_rt == NULL ) {
608             fprintf( stderr, "Must specify net-range before zones.\n" );
609             return -1;
610         }
611         if (( zt = newzt( strlen( zname ), zname )) == NULL ) {
612             perror( "newzt" );
613             return -1;
614         }
615         if ( iface->i_czt == NULL ) {
616             iface->i_czt = zt;
617         } else {
618             zt->zt_next = iface->i_czt->zt_next;
619             iface->i_czt->zt_next = zt;
620         }
621     }
622     return( 2 );
623 }
624
625 /*
626  * Get the configuration from the kernel. Only called if there's no
627  * configuration.
628  */
629 int getifconf()
630 {
631     struct interface    *iface, *niface;
632     struct ifreq        ifr;
633     char                **start, **list;
634     int                 s;
635
636     if (( s = socket( AF_APPLETALK, SOCK_DGRAM, 0 )) < 0 ) {
637         perror( "socket" );
638         return -1;
639     }
640
641     start = list = getifacelist();
642     while (list && *list) {
643         strncpy(ifr.ifr_name, *list, sizeof(ifr.ifr_name));
644         list++;
645
646         if (ioctl(s, SIOCGIFFLAGS, &ifr) < 0)
647           continue;
648
649         if (ifr.ifr_flags & (IFF_LOOPBACK | IFF_POINTOPOINT | IFF_SLAVE))
650           continue;
651
652         if ((ifr.ifr_flags & IFF_UP) == 0)
653           continue;
654
655         /* for devices that don't support appletalk */
656         if (ioctl(s, SIOCGIFADDR, &ifr) < 0 && (errno == ENODEV))
657           continue;
658
659         for ( iface = interfaces; iface; iface = iface->i_next ) {
660             if ( strcmp( iface->i_name, ifr.ifr_name ) == 0 ) {
661                 break;
662             }
663         }
664         if ( iface ) {  /* Already have this interface name */
665             continue;
666         }
667
668
669 #ifdef IFF_MULTICAST
670         if ((ifr.ifr_flags & IFF_MULTICAST) == 0)
671           fprintf(stderr, "%s: multicast may not work correctly.\n",
672                   ifr.ifr_name);
673 #endif /* IFF_MULTICAST */
674
675         if (addmulti(ifr.ifr_name, NULL) < 0) {
676           fprintf(stderr, "%s: disabled.\n", ifr.ifr_name);
677           continue;
678         }
679         
680         if (( niface = newiface( ifr.ifr_name )) == NULL ) {
681             perror( "newiface" );
682             close(s);
683             freeifacelist(start);
684             return -1;
685         }
686         /*
687          * Could try to get the address from the kernel...
688          */
689
690         if ( interfaces == NULL ) {
691             interfaces = niface;
692         } else {
693             for ( iface = interfaces; iface->i_next; iface = iface->i_next )
694                 ;
695             iface->i_next = niface;
696         }
697         niface->i_next = NULL;
698     }
699     freeifacelist(start);
700     (void)close( s );
701     return( 0 );
702 }
703
704 /*
705  * Allocate a new interface structure.  Centralized here so we can change
706  * the interface structure and have it updated nicely.
707  */
708
709 struct interface *newiface( name )
710     const char          *name;
711 {
712     struct interface    *niface;
713
714     if (( niface = (struct interface *)calloc(1, sizeof( struct interface )))
715             == NULL ) {
716         return( NULL );
717     }
718     strncpy( niface->i_name, name, sizeof(niface->i_name));
719 #ifdef BSD4_4
720     niface->i_addr.sat_len = sizeof( struct sockaddr_at );
721 #endif /* BSD4_4 */
722     niface->i_addr.sat_family = AF_APPLETALK;
723 #ifdef BSD4_4
724     niface->i_caddr.sat_len = sizeof( struct sockaddr_at );
725 #endif /* BSD4_4 */
726     niface->i_caddr.sat_family = AF_APPLETALK;
727     return( niface );
728 }
729
730 #ifdef __svr4__
731 int plumb()
732 {
733     struct interface    *iface;
734     char                device[ MAXPATHLEN + 1], *p;
735     int                 fd, ppa;
736
737     for ( iface = interfaces; iface != NULL; iface = iface->i_next ) {
738         if ( strcmp( iface->i_name, LOOPIFACE ) == 0 ) {
739             continue;
740         }
741
742         strcpy( device, "/dev/" );
743         strcat( device, iface->i_name );
744         if (( p = strpbrk( device, "0123456789" )) == NULL ) {
745             syslog( LOG_ERR, "plumb: invalid device: %s", device );
746             return -1;
747         }
748         ppa = atoi( p );
749         *p = '\0';
750
751         if (( fd = open( device, O_RDWR, 0 )) < 0 ) {
752             syslog( LOG_ERR, "%s: %m", device );
753             return -1;
754         }
755         if ( ioctl( fd, I_PUSH, "ddp" ) < 0 ) {
756             syslog( LOG_ERR, "I_PUSH: %m" );
757             close(fd);
758             return -1;
759         }
760         if ( ioctl( fd, IF_UNITSEL, ppa ) < 0 ) {
761             syslog( LOG_ERR, "IF_UNITSEL: %m" );
762             close(fd);
763             return -1;
764         }
765
766         /* configure multicast. */
767         if (addmulti(iface->i_name, NULL) < 0) {
768           perror(iface->i_name);
769           fprintf(stderr,"Can't configure multicast.\n");
770           close(fd);
771           return -1;
772         }
773
774         syslog( LOG_INFO, "plumbed %s%d", device, ppa );
775     }
776
777     return( 0 );
778 }
779 #endif /* __svr4__ */