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