1/*
2 * $Id: aarp.c,v 1.4 2005-04-28 20:50:07 bfernhomberg Exp $
3 */
4
5#ifdef HAVE_CONFIG_H
6#include "config.h"
7#endif /* HAVE_CONFIG_H */
8
9#include <sys/types.h>
10#include <sys/socket.h>
11#include <sys/byteorder.h>
12#include <sys/errno.h>
13#include <sys/stream.h>
14#include <sys/ethernet.h>
15#include <sys/kmem.h>
16#include <sys/cmn_err.h>
17#include <sys/ddi.h>
18#include <netinet/arp.h>
19#include <net/if.h>
20
21#ifdef STDC_HEADERS
22#include <strings.h>
23#else
24#include <string.h>
25#endif
26
27#include <netatalk/at.h>
28#include <netatalk/aarp.h>
29#include <netatalk/phase2.h>
30
31#include "if.h"
32
33struct aarplist {
34    struct aarplist	*aal_next, *aal_prev;
35    struct at_addr	aal_addr;
36    u_char		aal_hwaddr[ ETHERADDRL ];
37    u_char		aal_age;
38    u_char		aal_flags;
39    mblk_t		*aal_m;
40};
41
42    struct aarplist *
43aarp_find( struct atif_data *aid, ushort net, unchar node )
44{
45    struct aarplist	*aal;
46
47    for ( aal = aid->aid_aarplist; aal != NULL; aal = aal->aal_next ) {
48	if ( aal->aal_addr.s_net == net && aal->aal_addr.s_node == node ) {
49	    break;
50	}
51    }
52    return( aal );
53}
54
55    struct aarplist *
56aarp_alloc( struct atif_data *aid, ushort net, unchar node )
57{
58    struct aarplist	*aal;
59
60    for ( aal = aid->aid_aarplist; aal != NULL; aal = aal->aal_next ) {
61	if ( aal->aal_addr.s_net == net && aal->aal_addr.s_node == node ) {
62	    return( aal );
63	}
64    }
65
66    if ( aid->aid_aarpflist == NULL ) {
67        if (( aal = (struct aarplist *)kmem_alloc( sizeof( struct aarplist ),
68                KM_NOSLEEP )) == NULL ) {
69            return( NULL );
70        }
71    } else {
72	aal = aid->aid_aarpflist;
73	aid->aid_aarpflist = aal->aal_next;
74	if ( aid->aid_aarpflist != NULL ) {
75	    aid->aid_aarpflist->aal_prev = NULL;
76	}
77    }
78
79    aal->aal_addr.s_net = net;
80    aal->aal_addr.s_node = node;
81    bzero( aal->aal_hwaddr, sizeof( aal->aal_hwaddr ));
82    aal->aal_age = 0;
83    aal->aal_flags = 0;
84    aal->aal_m = NULL;
85
86    aal->aal_next = aid->aid_aarplist;
87    aal->aal_prev = NULL;
88    if ( aid->aid_aarplist != NULL ) {
89	aid->aid_aarplist->aal_prev = aal;
90    }
91    aid->aid_aarplist = aal;
92
93    return( aal );
94}
95
96/*
97 * Move entry to free list.
98 */
99    void
100aarp_free( struct atif_data *aid, struct aarplist *aal )
101{
102    if ( aal->aal_next != NULL ) {
103	aal->aal_next->aal_prev = aal->aal_prev;
104    }
105    if ( aal->aal_prev != NULL ) {
106	aal->aal_prev->aal_next = aal->aal_next;
107    }
108    if ( aid->aid_aarplist == aal ) {
109	aid->aid_aarplist = aal->aal_next;
110    }
111
112    if ( aal->aal_m != NULL ) {
113	freemsg( aal->aal_m );
114	aal->aal_m = NULL;
115    }
116
117    aal->aal_prev = NULL;
118    aal->aal_next = aid->aid_aarpflist;
119    if ( aid->aid_aarpflist != NULL ) {
120	aid->aid_aarpflist->aal_prev = aal;
121    }
122    aid->aid_aarpflist = aal;
123    return;
124}
125
126    void
127aarp_timeout( void *ptr )
128{
129    struct atif_data 	*aid = (struct atif_data *) ptr;
130    struct aarplist	*aal, *p;
131
132    aid->aid_aarptimeo = qtimeout( aid->aid_q, aarp_timeout,
133	    (caddr_t)aid, 60 * hz );
134    for ( aal = aid->aid_aarplist; aal != NULL; aal = p ) {
135	p = aal->aal_next;
136	if ( ++aal->aal_age < (( aal->aal_flags ) ? 5 : 3 )) {
137	    continue;
138	}
139	aarp_free( aid, aal );
140    }
141    return;
142}
143
144    void
145aarp_init( struct atif_data *aid )
146{
147    aid->aid_aarptimeo = qtimeout( aid->aid_q, aarp_timeout,
148	    (caddr_t)aid, 60 * hz );
149    return;
150}
151
152    void
153aarp_clean( struct atif_data *aid )
154{
155    struct aarplist *aal, *p;
156
157    if ( aid->aid_aarptimeo != 0 ) {
158	quntimeout( aid->aid_q, aid->aid_aarptimeo );
159	aid->aid_aarptimeo = 0;
160    }
161
162    for ( aal = aid->aid_aarplist; aal != NULL; aal = p ) {
163	p = aal->aal_next;
164	if ( aal->aal_m != NULL ) {
165	    freemsg( aal->aal_m );
166	    aal->aal_m = NULL;
167	}
168	kmem_free( aal, sizeof( struct aarplist ));
169    }
170    aid->aid_aarplist = NULL;
171
172    for ( aal = aid->aid_aarpflist; aal != NULL; aal = p ) {
173	p = aal->aal_next;
174	if ( aal->aal_m != NULL ) {
175	    freemsg( aal->aal_m );
176	    aal->aal_m = NULL;
177	}
178	kmem_free( aal, sizeof( struct aarplist ));
179    }
180    aid->aid_aarpflist = NULL;
181
182    return;
183}
184
185    int
186aarp_rput( queue_t *q, mblk_t *m )
187{
188    struct atif_data	*aid = (struct atif_data *)q->q_ptr;
189    struct ether_aarp	*ea;
190    struct aarplist	*aal;
191    ushort		tpnet, spnet, op;
192
193    if ( m->b_wptr - m->b_rptr < sizeof( struct ether_aarp )) {
194	cmn_err( CE_NOTE, "aarp_rput short packet\n" );
195	goto done;
196    }
197
198    ea = (struct ether_aarp *)m->b_rptr;
199
200    if ( ea->aarp_hrd != htons( AARPHRD_ETHER ) ||
201	    ea->aarp_pro != htons( ETHERTYPE_AT ) ||
202	    ea->aarp_hln != sizeof( ea->aarp_sha ) ||
203	    ea->aarp_pln != sizeof( ea->aarp_spu )) {
204	cmn_err( CE_NOTE, "aarp_rput bad constants\n" );
205	goto done;
206    }
207
208    if ( bcmp( ea->aarp_sha, aid->aid_hwaddr, sizeof( ea->aarp_sha )) == 0 ) {
209	goto done;
210    }
211
212    op = ntohs( ea->aarp_op );
213    bcopy( ea->aarp_tpnet, &tpnet, sizeof( tpnet ));
214    bcopy( ea->aarp_spnet, &spnet, sizeof( spnet ));
215
216    if ( aid->aid_flags & AIDF_PROBING ) {
217	if ( tpnet == aid->aid_sat.sat_addr.s_net &&
218		ea->aarp_tpnode == aid->aid_sat.sat_addr.s_node ) {
219	    aid->aid_flags &= ~AIDF_PROBING;
220	    aid->aid_flags |= AIDF_PROBEFAILED;
221	    cmn_err( CE_NOTE, "aarp_rput probe collision %s\n", aid->aid_name );
222	}
223    } else {
224	if ( tpnet == aid->aid_sat.sat_addr.s_net &&
225		ea->aarp_tpnode == aid->aid_sat.sat_addr.s_node ) {
226	    switch ( op ) {
227	    case AARPOP_REQUEST :
228		aal = aarp_alloc( aid, spnet, ea->aarp_spnode );
229		bcopy( ea->aarp_sha, aal->aal_hwaddr, sizeof( ea->aarp_sha ));
230		aal->aal_age = 0;
231		aal->aal_flags = 1;		/* complete */
232	    case AARPOP_PROBE :
233		aarp_send( aid, AARPOP_RESPONSE, ea->aarp_sha,
234			spnet, ea->aarp_spnode );
235		break;
236
237	    case AARPOP_RESPONSE :
238		if (( aal =
239			aarp_find( aid, spnet, ea->aarp_spnode )) == NULL ) {
240		    break;
241		}
242		bcopy( ea->aarp_sha, aal->aal_hwaddr, sizeof( ea->aarp_sha ));
243		aal->aal_age = 0;
244		aal->aal_flags = 1;		/* complete */
245		if ( aal->aal_m != NULL ) {
246		    dl_unitdata_req( WR( q ), aal->aal_m, ETHERTYPE_AT,
247			    aal->aal_hwaddr );
248		    aal->aal_m = NULL;
249		}
250		break;
251
252	    default :
253		cmn_err( CE_NOTE, "aarp_rput bad op %X\n", op );
254		break;
255	    }
256	} else {
257	    switch ( op ) {
258	    case AARPOP_REQUEST :
259		break;
260	    case AARPOP_PROBE :
261		if (( aal =
262			aarp_find( aid, spnet, ea->aarp_spnode )) != NULL ) {
263		    aarp_free( aid, aal );
264		}
265		break;
266
267	    case AARPOP_RESPONSE :
268		cmn_err( CE_NOTE, "aarp_rput someone using our address\n" );
269		break;
270
271	    default :
272		cmn_err( CE_NOTE, "aarp_rput bad op %X\n", op );
273		break;
274	    }
275	}
276    }
277
278done :
279    freemsg( m );
280    return( 0 );
281}
282
283    void
284aarp_send( struct atif_data *aid, int op, caddr_t hwaddr,
285	ushort net, unchar node )
286{
287    mblk_t		*m;
288    struct ether_aarp	*ea;
289
290    if (( m = allocb( sizeof( struct ether_aarp ), BPRI_HI )) == NULL ) {
291	return;
292    }
293    m->b_wptr = m->b_rptr + sizeof( struct ether_aarp );
294    ea = (struct ether_aarp *)m->b_rptr;
295    bzero( (caddr_t)ea, sizeof( struct ether_aarp ));
296
297    ea->aarp_hrd = htons( AARPHRD_ETHER );
298    ea->aarp_pro = htons( ETHERTYPE_AT );
299    ea->aarp_hln = sizeof( ea->aarp_sha );
300    ea->aarp_pln = sizeof( ea->aarp_spu );
301    ea->aarp_op = htons( op );
302    bcopy( aid->aid_hwaddr, ea->aarp_sha, sizeof( ea->aarp_sha ));
303
304    if ( hwaddr == NULL ) {
305	bzero( ea->aarp_tha, sizeof( ea->aarp_tha ));
306    } else {
307	bcopy( hwaddr, ea->aarp_tha, sizeof( ea->aarp_tha ));
308    }
309
310    ea->aarp_tpnode = node;
311    bcopy( &aid->aid_sat.sat_addr.s_net, ea->aarp_spnet,
312	    sizeof( ea->aarp_spnet ));
313    bcopy( &net, ea->aarp_tpnet, sizeof( ea->aarp_tpnet ));
314    ea->aarp_spnode = aid->aid_sat.sat_addr.s_node;
315    ea->aarp_tpnode = node;
316
317    if ( hwaddr == NULL ) {
318	dl_unitdata_req( WR( aid->aid_q ), m, ETHERTYPE_AARP,
319		at_multicastaddr );
320    } else {
321	dl_unitdata_req( WR( aid->aid_q ), m, ETHERTYPE_AARP, hwaddr );
322    }
323    return;
324}
325
326    int
327aarp_resolve( struct atif_data *aid, mblk_t *m, struct sockaddr_at *sat )
328{
329    struct aarplist	*aal;
330
331    if ( sat->sat_addr.s_node == ATADDR_BCAST ) {
332	dl_unitdata_req( WR( aid->aid_q ), m, ETHERTYPE_AT, at_multicastaddr );
333	return( 0 );
334    }
335
336    if (( aal = aarp_alloc( aid, sat->sat_addr.s_net, sat->sat_addr.s_node )) ==
337	    NULL ) {
338	freemsg( m );
339	return( ENOMEM );
340    }
341    aal->aal_age = 0;
342
343    if ( aal->aal_flags ) {	/* complete */
344	dl_unitdata_req( WR( aid->aid_q ), m, ETHERTYPE_AT, aal->aal_hwaddr );
345    } else {
346	/* send aarp request */
347	if ( aal->aal_m != NULL ) {
348	    freemsg( aal->aal_m );
349	}
350	/* either freed above, in timeout, or sent in aarp_rput() */
351	aal->aal_m = m;
352	aarp_send( aid, AARPOP_REQUEST, NULL,
353		sat->sat_addr.s_net, sat->sat_addr.s_node );
354    }
355    return( 0 );
356}
357