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