1/* $Id: if.c,v 1.3 2005-04-28 20:50:07 bfernhomberg Exp $
2 */
3
4#ifdef HAVE_CONFIG_H
5#include "config.h"
6#endif /* HAVE_CONFIG_H */
7
8#include <sys/types.h>
9#include <sys/socket.h>
10#include <sys/sockio.h>
11#include <sys/stream.h>
12#include <sys/kmem.h>
13#include <sys/dlpi.h>
14#include <sys/cmn_err.h>
15#include <sys/errno.h>
16#include <sys/byteorder.h>
17#include <sys/ethernet.h>
18#include <sys/ddi.h>
19#include <net/if.h>
20#include <netinet/arp.h>
21
22#ifdef STDC_HEADERS
23#include <strings.h>
24#else
25#include <string.h>
26#endif
27
28#include <netatalk/at.h>
29#include <netatalk/aarp.h>
30
31#include "if.h"
32#include "rt.h"
33#include "ioc.h"
34
35static struct atif_data	*interfaces = NULL;
36
37    struct atif_data *
38if_primary()
39{
40    return( interfaces );
41}
42
43    struct atif_data *
44if_alloc( queue_t *q )
45{
46    struct atif_data	*aid;
47
48    if (( aid = kmem_zalloc( sizeof( struct atif_data ), KM_SLEEP )) == NULL ) {
49	return( NULL );
50    }
51    aid->aid_q = q;
52    aid->aid_state = DL_UNATTACHED;
53
54    return( aid );
55}
56
57/*
58 * Name an interface, insert it in our list of interfaces.  If this is the
59 * first interface, create the loopback interface.  If it's not the first
60 * interfaces, keep the first interface the same, i.e. the first configured
61 * interface should be the primary interface.
62 */
63    int
64if_name( struct atif_data *aid, char *name, ulong ppa )
65{
66    sprintf( aid->aid_name, "%s%ld", name, ppa );
67
68    if ( interfaces == NULL ) {		/* create fake loopback */
69	if (( interfaces = if_alloc( NULL )) == NULL ) {
70	    return( ENOMEM );
71	}
72	strcpy( interfaces->aid_name, "lo0" );
73	interfaces->aid_state = DL_IDLE;
74	bzero( interfaces->aid_hwaddr, sizeof( interfaces->aid_hwaddr ));
75	interfaces->aid_flags = AIDF_LOOPBACK;
76	interfaces->aid_c.c_type = 0;
77
78	aid->aid_next = interfaces;
79	aid->aid_next->aid_prev = aid;
80	interfaces = aid;
81    } else {
82	aid->aid_next = interfaces->aid_next;
83	aid->aid_prev = interfaces;
84	aid->aid_next->aid_prev = aid;
85	interfaces->aid_next = aid;
86    }
87
88    aarp_init( aid );
89    return( 0 );
90}
91
92    void
93if_free( struct atif_data *aid )
94{
95    if ( aid->aid_c.c_type != 0 ) {
96	cmn_err( CE_NOTE, "if_free context %x\n", aid->aid_c.c_type );
97	return;
98    }
99
100    aarp_clean( aid );
101
102    if ( aid->aid_next != NULL ) {
103	aid->aid_next->aid_prev = aid->aid_prev;
104    }
105    if ( aid->aid_prev != NULL ) {
106	aid->aid_prev->aid_next = aid->aid_next;
107    }
108    if ( aid == interfaces ) {
109	interfaces = aid->aid_next;
110    }
111    kmem_free( aid, sizeof( struct atif_data ));
112
113    if ( interfaces != NULL && interfaces->aid_next == NULL ) {
114	kmem_free( interfaces, sizeof( struct atif_data ));
115	interfaces = NULL;
116    }
117    return;
118}
119
120    int
121if_getaddr( char *name, struct sockaddr_at *sat )
122{
123    struct atif_data	*aid;
124
125    for ( aid = interfaces; aid != NULL; aid = aid->aid_next ) {
126	if ( strcmp( name, aid->aid_name ) == 0 ) {
127	    break;
128	}
129    }
130    if ( aid == NULL ) {
131	return( EADDRNOTAVAIL );
132    }
133
134    bcopy( &aid->aid_sat, sat, sizeof( struct sockaddr_at ));
135    return( 0 );
136}
137
138    int
139if_addmulti( queue_t *q, mblk_t *m, char *name, struct sockaddr *sa )
140{
141    struct atif_data	*aid;
142
143    for ( aid = interfaces; aid != NULL; aid = aid->aid_next ) {
144	if ( strcmp( name, aid->aid_name ) == 0 ) {
145	    break;
146	}
147    }
148    if ( aid == NULL ) {
149	return( EADDRNOTAVAIL );
150    }
151
152    if ( aid->aid_c.c_type != 0 ) {
153	cmn_err( CE_NOTE, "if_addmulti context %x\n", aid->aid_c.c_type );
154	return( EINVAL );
155    }
156
157    aid->aid_c.c_type = SIOCADDMULTI;
158    aid->aid_c.c_u.u_multi.um_q = q;
159    aid->aid_c.c_u.u_multi.um_m = m;
160    dl_enabmulti_req( WR( aid->aid_q ), sa->sa_data );
161
162    return( 0 );
163}
164
165    void
166if_pickaddr( void *ptr )
167{
168    struct atif_data *aid = (struct atif_data*) ptr;
169
170    if ( aid->aid_c.c_type != SIOCSIFADDR ) {
171	cmn_err( CE_NOTE, "if_pickaddr context %x\n", aid->aid_c.c_type );
172	return;
173    }
174
175    if ( aid->aid_flags & AIDF_PROBEFAILED ) {
176	aid->aid_flags &= ~AIDF_PROBEFAILED;
177	/* choose new address */
178	for (;;) {
179	    if ( aid->aid_c.c_u.u_addr.ua_nodecnt == 0 ) {
180		/* if we've exausted all addresses, fail */
181		if ( aid->aid_c.c_u.u_addr.ua_netcnt == 0 ) {
182		    ioc_error_ack( aid->aid_c.c_u.u_addr.ua_q,
183			    aid->aid_c.c_u.u_addr.ua_m, EADDRINUSE );
184		    aid->aid_c.c_type = 0;
185		    aid->aid_c.c_u.u_addr.ua_q = NULL;
186		    aid->aid_c.c_u.u_addr.ua_m = NULL;
187		    aid->aid_c.c_u.u_addr.ua_probecnt = 0;
188		    aid->aid_c.c_u.u_addr.ua_netcnt = 0;
189		    aid->aid_c.c_u.u_addr.ua_nodecnt = 0;
190		} else {
191		    aid->aid_c.c_u.u_addr.ua_nodecnt = 256;
192		    aid->aid_c.c_u.u_addr.ua_netcnt--;
193		    if ( ntohs(aid->aid_sat.sat_addr.s_net) >
194			    ntohs(aid->aid_nr.nr_lastnet) ) {
195			aid->aid_sat.sat_addr.s_net = aid->aid_nr.nr_firstnet;
196		    } else
197		      aid->aid_sat.sat_addr.s_net =
198			htons(ntohs(aid->aid_sat.sat_addr.s_net) + 1);
199		}
200	    } else {
201		aid->aid_sat.sat_addr.s_node++;
202		aid->aid_c.c_u.u_addr.ua_nodecnt--;
203		if ( aid->aid_sat.sat_addr.s_node == 0 ||
204			aid->aid_sat.sat_addr.s_node == 255 ||
205			aid->aid_sat.sat_addr.s_node == 254 ) {
206		    continue;
207		}
208		break;
209	    }
210	}
211    }
212    if ( aid->aid_c.c_u.u_addr.ua_probecnt-- <= 0 ) {
213	aid->aid_flags &= ~AIDF_PROBING;
214	/* worked, send ioctl reponse */
215	ioc_ok_ack( aid->aid_c.c_u.u_addr.ua_q, aid->aid_c.c_u.u_addr.ua_m, 0 );
216	aid->aid_c.c_type = 0;
217	aid->aid_c.c_u.u_addr.ua_q = NULL;
218	aid->aid_c.c_u.u_addr.ua_m = NULL;
219	aid->aid_c.c_u.u_addr.ua_probecnt = 0;
220	aid->aid_c.c_u.u_addr.ua_netcnt = 0;
221	aid->aid_c.c_u.u_addr.ua_nodecnt = 0;
222	return;
223    }
224
225    aarp_send( aid, AARPOP_PROBE, NULL,
226	    aid->aid_sat.sat_addr.s_net, aid->aid_sat.sat_addr.s_node );
227    qtimeout( aid->aid_q, if_pickaddr, (caddr_t)aid, hz / 5 );
228}
229
230    int
231if_setaddr( queue_t *q, mblk_t *m, char *name, struct sockaddr_at *sat )
232{
233    struct atif_data	*aid;
234    struct netrange	nr;
235    ulong		time;
236
237    for ( aid = interfaces; aid != NULL; aid = aid->aid_next ) {
238	if ( strcmp( name, aid->aid_name ) == 0 ) {
239	    break;
240	}
241    }
242    if ( aid == NULL ) {
243	return( EADDRNOTAVAIL );
244    }
245
246    if ( aid->aid_c.c_type != 0 ) {
247	cmn_err( CE_NOTE, "if_setaddr context %x\n", aid->aid_c.c_type );
248	return( EINVAL );
249    }
250
251    bcopy( sat->sat_zero, &nr, sizeof( struct netrange ));
252
253    if ( aid->aid_flags & AIDF_LOOPBACK ) {
254	aid->aid_sat = *sat;
255	aid->aid_nr = nr;
256
257	/* routes? */
258
259	ioc_ok_ack( q, m, 0 );
260	return( 0 );
261    }
262
263    drv_getparm( TIME, &time );
264    if ( sat->sat_addr.s_net == ATADDR_ANYNET ) {
265	if ( nr.nr_lastnet == nr.nr_firstnet ) {
266	    sat->sat_addr.s_net = nr.nr_firstnet;
267	} else {
268	    sat->sat_addr.s_net = htons(ntohs(nr.nr_firstnet) + time %
269		    (ntohs(nr.nr_lastnet) - ntohs(nr.nr_firstnet)));
270	}
271    } else {
272	if ( ntohs( sat->sat_addr.s_net ) < ntohs( nr.nr_firstnet ) ||
273		ntohs( sat->sat_addr.s_net ) > ntohs( nr.nr_lastnet )) {
274	    return( EINVAL );
275	}
276    }
277
278    if ( sat->sat_addr.s_node == ATADDR_ANYNODE ) {
279	sat->sat_addr.s_node = time;
280    }
281
282    aid->aid_flags |= AIDF_PROBING;
283    aid->aid_sat = *sat;
284    aid->aid_nr = nr;
285
286    aid->aid_c.c_type = SIOCSIFADDR;
287    aid->aid_c.c_u.u_addr.ua_q = q;
288    aid->aid_c.c_u.u_addr.ua_m = m;
289    aid->aid_c.c_u.u_addr.ua_probecnt = 10;
290    aid->aid_c.c_u.u_addr.ua_netcnt = ntohs(nr.nr_lastnet) -
291      ntohs(nr.nr_firstnet);
292    aid->aid_c.c_u.u_addr.ua_nodecnt = 256;
293    qtimeout( aid->aid_q, if_pickaddr, (caddr_t)aid, 0 );
294    return( 0 );
295}
296
297/*
298 * These three routines are all a big mess...
299 */
300    struct atif_data *
301if_dest( struct atif_data *aid, struct sockaddr_at *sat )
302{
303    struct atif_data	*dest;
304
305    for ( dest = interfaces; dest != NULL; dest = dest->aid_next ) {
306	if ((( sat->sat_addr.s_net == 0 && aid == dest ) ||
307		( ntohs(sat->sat_addr.s_net) >=
308		  ntohs(dest->aid_nr.nr_firstnet) &&
309		ntohs(sat->sat_addr.s_net) <=
310		  ntohs(dest->aid_nr.nr_lastnet) )) &&
311		( sat->sat_addr.s_node == dest->aid_sat.sat_addr.s_node ||
312		sat->sat_addr.s_node == ATADDR_BCAST )) {
313	    break;
314	}
315    }
316    if ( dest == NULL ) {
317	return( NULL );
318    }
319    return( dest );
320}
321
322    struct atif_data *
323if_withaddr( struct sockaddr_at *sat )
324{
325    struct atif_data	*dest;
326
327    for ( dest = interfaces; dest != NULL; dest = dest->aid_next ) {
328	if ( sat->sat_addr.s_net == dest->aid_sat.sat_addr.s_net &&
329		sat->sat_addr.s_node == dest->aid_sat.sat_addr.s_node ) {
330	    break;
331	}
332    }
333    return( dest );
334}
335
336    struct atif_data *
337if_withnet( struct sockaddr_at *sat )
338{
339    struct atif_data	*dest;
340
341    for ( dest = interfaces; dest != NULL; dest = dest->aid_next ) {
342	if ( ntohs(sat->sat_addr.s_net) >= ntohs(dest->aid_nr.nr_firstnet) &&
343		ntohs(sat->sat_addr.s_net) <= ntohs(dest->aid_nr.nr_lastnet)) {
344	    break;
345	}
346    }
347    return( dest );
348}
349
350    int
351if_route( struct atif_data *aid, mblk_t *m, struct sockaddr_at *sat )
352{
353    struct sockaddr_at	gate;
354
355    if ( sat->sat_addr.s_net == 0 ) {
356	if ( sat->sat_addr.s_node == 0 ) {
357	    aid = if_withaddr( sat );
358	}
359	if ( aid == NULL ) {
360	    freemsg( m );
361	    return( ENETUNREACH );
362	}
363	gate = *sat;
364    } else {
365	if ( rt_gate( sat, &gate ) < 0 ) {	/* no route */
366	    gate = *sat;
367	}
368	if (( aid = if_withaddr( &gate )) == NULL ) {
369	    if (( aid = if_withnet( &gate )) == NULL ) {
370		freemsg( m );
371		return( ENETUNREACH );
372	    }
373	}
374    }
375
376    if ( aid->aid_flags & AIDF_LOOPBACK ) {
377	return( ddp_rput( aid, m ));
378    } else {
379	/* the aarp layer is expected to send broadcast packets appropriately */
380	return( aarp_resolve( aid, m, &gate ));
381    }
382}
383