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