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