]> arthur.barton.de Git - netatalk.git/blob - sys/solaris/if.c
Merge sf.net
[netatalk.git] / sys / solaris / if.c
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
35 static struct atif_data *interfaces = NULL;
36
37     struct atif_data *
38 if_primary()
39 {
40     return( interfaces );
41 }
42
43     struct atif_data *
44 if_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
64 if_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
93 if_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
121 if_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
139 if_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
166 if_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
231 if_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 *
301 if_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 *
323 if_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 *
337 if_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
351 if_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 }