ddp_usrreq.c revision 24203
1/*
2 * Copyright (c) 1990,1994 Regents of The University of Michigan.
3 * All Rights Reserved.  See COPYRIGHT.
4 */
5
6#include <sys/errno.h>
7#include <sys/types.h>
8#include <sys/param.h>
9#include <sys/systm.h>
10#include <sys/proc.h>
11#include <sys/mbuf.h>
12#include <sys/socket.h>
13#include <sys/socketvar.h>
14#include <sys/protosw.h>
15#include <net/if.h>
16#include <net/route.h>
17#include <netinet/in.h>
18#include <netinet/if_ether.h>
19
20#include <netatalk/at.h>
21#include <netatalk/at_var.h>
22#include <netatalk/ddp_var.h>
23#include <netatalk/aarp.h>
24#include <netatalk/endian.h>
25#include <netatalk/at_extern.h>
26
27static void at_pcbdisconnect( struct ddpcb *ddp );
28static void at_sockaddr( struct ddpcb *ddp, struct mbuf *addr );
29static int at_pcbsetaddr( struct ddpcb *ddp, struct mbuf *addr, struct proc *p);
30static int at_pcbconnect( struct ddpcb *ddp, struct mbuf *addr, struct proc *p);
31static void at_pcbdetach( struct socket *so, struct ddpcb *ddp);
32static int at_pcballoc( struct socket *so );
33
34struct ddpcb	*ddp_ports[ ATPORT_LAST ];
35struct ddpcb	*ddpcb = NULL;
36u_long		ddp_sendspace = DDP_MAXSZ; /* Max ddp size + 1 (ddp_type) */
37u_long		ddp_recvspace = 10 * ( 587 + sizeof( struct sockaddr_at ));
38
39/*ARGSUSED*/
40int
41ddp_usrreq( struct socket *so, int req, struct mbuf *m,
42		struct mbuf  *addr, struct mbuf *rights)
43{
44    struct proc *p = curproc;           /* XXX */
45    struct ddpcb	*ddp;
46    int			error = 0;
47
48    ddp = sotoddpcb( so );
49
50    if ( req == PRU_CONTROL ) {
51	return( at_control( (int) m, (caddr_t) addr,
52		(struct ifnet *) rights, (struct proc *)p ));
53    }
54
55    if ( rights && rights->m_len ) {
56	error = EINVAL;
57	goto release;
58    }
59
60    if ( ddp == NULL && req != PRU_ATTACH ) {
61	error = EINVAL;
62	goto release;
63    }
64
65    switch ( req ) {
66    case PRU_ATTACH :
67	if ( ddp != NULL ) {
68	    error = EINVAL;
69	    break;
70	}
71	if (( error = at_pcballoc( so )) != 0 ) {
72	    break;
73	}
74	error = soreserve( so, ddp_sendspace, ddp_recvspace );
75	break;
76
77    case PRU_DETACH :
78	at_pcbdetach( so, ddp );
79	break;
80
81    case PRU_BIND :
82	error = at_pcbsetaddr( ddp, addr, p );
83	break;
84
85    case PRU_SOCKADDR :
86	at_sockaddr( ddp, addr );
87	break;
88
89    case PRU_CONNECT:
90	if ( ddp->ddp_fsat.sat_port != ATADDR_ANYPORT ) {
91	    error = EISCONN;
92	    break;
93	}
94
95	error = at_pcbconnect( ddp, addr, p );
96	if ( error == 0 )
97	    soisconnected( so );
98	break;
99
100    case PRU_DISCONNECT:
101	if ( ddp->ddp_fsat.sat_addr.s_node == ATADDR_ANYNODE ) {
102	    error = ENOTCONN;
103	    break;
104	}
105	at_pcbdisconnect( ddp );
106	soisdisconnected( so );
107	break;
108
109    case PRU_SHUTDOWN:
110	socantsendmore( so );
111	break;
112
113    case PRU_SEND: {
114	int	s = 0;
115
116	if ( addr ) {
117	    if ( ddp->ddp_fsat.sat_port != ATADDR_ANYPORT ) {
118		error = EISCONN;
119		break;
120	    }
121
122	    s = splnet();
123	    error = at_pcbconnect( ddp, addr, p );
124	    if ( error ) {
125		splx( s );
126		break;
127	    }
128	} else {
129	    if ( ddp->ddp_fsat.sat_port == ATADDR_ANYPORT ) {
130		error = ENOTCONN;
131		break;
132	    }
133	}
134
135	error = ddp_output( m, so );
136	m = NULL;
137	if ( addr ) {
138	    at_pcbdisconnect( ddp );
139	    splx( s );
140	}
141	}
142	break;
143
144    case PRU_ABORT:
145	soisdisconnected( so );
146	at_pcbdetach( so, ddp );
147	break;
148
149    case PRU_LISTEN:
150    case PRU_CONNECT2:
151    case PRU_ACCEPT:
152    case PRU_SENDOOB:
153    case PRU_FASTTIMO:
154    case PRU_SLOWTIMO:
155    case PRU_PROTORCV:
156    case PRU_PROTOSEND:
157	error = EOPNOTSUPP;
158	break;
159
160    case PRU_RCVD:
161    case PRU_RCVOOB:
162	/*
163	 * Don't mfree. Good architecture...
164	 */
165	return( EOPNOTSUPP );
166
167    case PRU_SENSE:
168	/*
169	 * 1. Don't return block size.
170	 * 2. Don't mfree.
171	 */
172	return( 0 );
173
174    default:
175	error = EOPNOTSUPP;
176    }
177
178release:
179    if ( m != NULL ) {
180	m_freem( m );
181    }
182    return( error );
183}
184
185static void
186at_sockaddr( struct ddpcb *ddp, struct mbuf *addr)
187{
188    struct sockaddr_at	*sat;
189
190    addr->m_len = sizeof( struct sockaddr_at );
191    sat = mtod( addr, struct sockaddr_at *);
192    *sat = ddp->ddp_lsat;
193}
194
195static int
196at_pcbsetaddr( struct ddpcb *ddp, struct mbuf *addr, struct proc *p )
197{
198    struct sockaddr_at	lsat, *sat;
199    struct at_ifaddr	*aa;
200    struct ddpcb	*ddpp;
201
202    if ( ddp->ddp_lsat.sat_port != ATADDR_ANYPORT ) { /* shouldn't be bound */
203	return( EINVAL );
204    }
205
206    if ( addr != 0 ) {			/* validate passed address */
207	sat = mtod( addr, struct sockaddr_at *);
208	if ( addr->m_len != sizeof( *sat )) {
209	    return( EINVAL );
210	}
211	if ( sat->sat_family != AF_APPLETALK ) {
212	    return( EAFNOSUPPORT );
213	}
214
215	if ( sat->sat_addr.s_node != ATADDR_ANYNODE ||
216		sat->sat_addr.s_net != ATADDR_ANYNET ) {
217	    for ( aa = at_ifaddr; aa; aa = aa->aa_next ) {
218		if (( sat->sat_addr.s_net == AA_SAT( aa )->sat_addr.s_net ) &&
219		 ( sat->sat_addr.s_node == AA_SAT( aa )->sat_addr.s_node )) {
220		    break;
221		}
222	    }
223	    if ( !aa ) {
224		return( EADDRNOTAVAIL );
225	    }
226	}
227
228	if ( sat->sat_port != ATADDR_ANYPORT ) {
229	    if ( sat->sat_port < ATPORT_FIRST ||
230		    sat->sat_port >= ATPORT_LAST ) {
231		return( EINVAL );
232	    }
233	    if ( sat->sat_port < ATPORT_RESERVED &&
234		 suser( p->p_ucred, &p->p_acflag ) ) {
235		return( EACCES );
236	    }
237	}
238    } else {
239	bzero( (caddr_t)&lsat, sizeof( struct sockaddr_at ));
240	lsat.sat_len = sizeof(struct sockaddr_at);
241	lsat.sat_addr.s_node = ATADDR_ANYNODE;
242	lsat.sat_addr.s_net = ATADDR_ANYNET;
243	lsat.sat_family = AF_APPLETALK;
244	sat = &lsat;
245    }
246
247    if ( sat->sat_addr.s_node == ATADDR_ANYNODE &&
248	    sat->sat_addr.s_net == ATADDR_ANYNET ) {
249	if ( at_ifaddr == NULL ) {
250	    return( EADDRNOTAVAIL );
251	}
252	sat->sat_addr = AA_SAT( at_ifaddr )->sat_addr;
253    }
254    ddp->ddp_lsat = *sat;
255
256    /*
257     * Choose port.
258     */
259    if ( sat->sat_port == ATADDR_ANYPORT ) {
260	for ( sat->sat_port = ATPORT_RESERVED;
261		sat->sat_port < ATPORT_LAST; sat->sat_port++ ) {
262	    if ( ddp_ports[ sat->sat_port - 1 ] == 0 ) {
263		break;
264	    }
265	}
266	if ( sat->sat_port == ATPORT_LAST ) {
267	    return( EADDRNOTAVAIL );
268	}
269	ddp->ddp_lsat.sat_port = sat->sat_port;
270	ddp_ports[ sat->sat_port - 1 ] = ddp;
271    } else {
272	for ( ddpp = ddp_ports[ sat->sat_port - 1 ]; ddpp;
273		ddpp = ddpp->ddp_pnext ) {
274	    if ( ddpp->ddp_lsat.sat_addr.s_net == sat->sat_addr.s_net &&
275		    ddpp->ddp_lsat.sat_addr.s_node == sat->sat_addr.s_node ) {
276		break;
277	    }
278	}
279	if ( ddpp != NULL ) {
280	    return( EADDRINUSE );
281	}
282	ddp->ddp_pnext = ddp_ports[ sat->sat_port - 1 ];
283	ddp_ports[ sat->sat_port - 1 ] = ddp;
284	if ( ddp->ddp_pnext ) {
285	    ddp->ddp_pnext->ddp_pprev = ddp;
286	}
287    }
288
289    return( 0 );
290}
291
292static int
293at_pcbconnect( struct ddpcb *ddp, struct mbuf *addr, struct proc *p)
294{
295    struct sockaddr_at	*sat = mtod( addr, struct sockaddr_at *);
296    struct route	*ro;
297    struct at_ifaddr	*aa = 0;
298    struct ifnet	*ifp;
299    u_short		hintnet = 0, net;
300
301    if ( addr->m_len != sizeof( *sat ))
302	return( EINVAL );
303    if ( sat->sat_family != AF_APPLETALK ) {
304	return( EAFNOSUPPORT );
305    }
306
307    /*
308     * Under phase 2, network 0 means "the network".  We take "the
309     * network" to mean the network the control block is bound to.
310     * If the control block is not bound, there is an error.
311     */
312    if ( sat->sat_addr.s_net == ATADDR_ANYNET
313		&& sat->sat_addr.s_node != ATADDR_ANYNODE ) {
314	if ( ddp->ddp_lsat.sat_port == ATADDR_ANYPORT ) {
315	    return( EADDRNOTAVAIL );
316	}
317	hintnet = ddp->ddp_lsat.sat_addr.s_net;
318    }
319
320    ro = &ddp->ddp_route;
321    /*
322     * If we've got an old route for this pcb, check that it is valid.
323     * If we've changed our address, we may have an old "good looking"
324     * route here.  Attempt to detect it.
325     */
326    if ( ro->ro_rt ) {
327	if ( hintnet ) {
328	    net = hintnet;
329	} else {
330	    net = sat->sat_addr.s_net;
331	}
332	aa = 0;
333	if ( ifp = ro->ro_rt->rt_ifp ) {
334	    for ( aa = at_ifaddr; aa; aa = aa->aa_next ) {
335		if ( aa->aa_ifp == ifp &&
336			ntohs( net ) >= ntohs( aa->aa_firstnet ) &&
337			ntohs( net ) <= ntohs( aa->aa_lastnet )) {
338		    break;
339		}
340	    }
341	}
342	if ( aa == NULL || ( satosat( &ro->ro_dst )->sat_addr.s_net !=
343		( hintnet ? hintnet : sat->sat_addr.s_net ) ||
344		satosat( &ro->ro_dst )->sat_addr.s_node !=
345		sat->sat_addr.s_node )) {
346	    RTFREE( ro->ro_rt );
347	    ro->ro_rt = (struct rtentry *)0;
348	}
349    }
350
351    /*
352     * If we've got no route for this interface, try to find one.
353     */
354    if ( ro->ro_rt == (struct rtentry *)0 ||
355	 ro->ro_rt->rt_ifp == (struct ifnet *)0 ) {
356	ro->ro_dst.sa_len = sizeof( struct sockaddr_at );
357	ro->ro_dst.sa_family = AF_APPLETALK;
358	if ( hintnet ) {
359	    satosat( &ro->ro_dst )->sat_addr.s_net = hintnet;
360	} else {
361	    satosat( &ro->ro_dst )->sat_addr.s_net = sat->sat_addr.s_net;
362	}
363	satosat( &ro->ro_dst )->sat_addr.s_node = sat->sat_addr.s_node;
364	rtalloc( ro );
365    }
366
367    /*
368     * Make sure any route that we have has a valid interface.
369     */
370    aa = 0;
371    if ( ro->ro_rt && ( ifp = ro->ro_rt->rt_ifp )) {
372	for ( aa = at_ifaddr; aa; aa = aa->aa_next ) {
373	    if ( aa->aa_ifp == ifp ) {
374		break;
375	    }
376	}
377    }
378    if ( aa == 0 ) {
379	return( ENETUNREACH );
380    }
381
382    ddp->ddp_fsat = *sat;
383    if ( ddp->ddp_lsat.sat_port == ATADDR_ANYPORT ) {
384	return( at_pcbsetaddr( ddp, (struct mbuf *)0, p ));
385    }
386    return( 0 );
387}
388
389static void
390at_pcbdisconnect( struct ddpcb	*ddp )
391{
392    ddp->ddp_fsat.sat_addr.s_net = ATADDR_ANYNET;
393    ddp->ddp_fsat.sat_addr.s_node = ATADDR_ANYNODE;
394    ddp->ddp_fsat.sat_port = ATADDR_ANYPORT;
395}
396
397static int
398at_pcballoc( struct socket *so )
399{
400    struct ddpcb	*ddp;
401    struct mbuf		*m;
402
403    m = m_getclr( M_WAIT, MT_PCB );
404    ddp = mtod( m, struct ddpcb * );
405    ddp->ddp_lsat.sat_port = ATADDR_ANYPORT;
406
407    ddp->ddp_next = ddpcb;
408    ddp->ddp_prev = NULL;
409    ddp->ddp_pprev = NULL;
410    ddp->ddp_pnext = NULL;
411    if ( ddpcb ) {
412	ddpcb->ddp_prev = ddp;
413    }
414    ddpcb = ddp;
415
416    ddp->ddp_socket = so;
417    so->so_pcb = (caddr_t)ddp;
418    return( 0 );
419}
420
421static void
422at_pcbdetach( struct socket *so, struct ddpcb *ddp)
423{
424    soisdisconnected( so );
425    so->so_pcb = 0;
426    sofree( so );
427
428    /* remove ddp from ddp_ports list */
429    if ( ddp->ddp_lsat.sat_port != ATADDR_ANYPORT &&
430	    ddp_ports[ ddp->ddp_lsat.sat_port - 1 ] != NULL ) {
431	if ( ddp->ddp_pprev != NULL ) {
432	    ddp->ddp_pprev->ddp_pnext = ddp->ddp_pnext;
433	} else {
434	    ddp_ports[ ddp->ddp_lsat.sat_port - 1 ] = ddp->ddp_pnext;
435	}
436	if ( ddp->ddp_pnext != NULL ) {
437	    ddp->ddp_pnext->ddp_pprev = ddp->ddp_pprev;
438	}
439    }
440
441    if ( ddp->ddp_route.ro_rt ) {
442	rtfree( ddp->ddp_route.ro_rt );
443    }
444
445    if ( ddp->ddp_prev ) {
446	ddp->ddp_prev->ddp_next = ddp->ddp_next;
447    } else {
448	ddpcb = ddp->ddp_next;
449    }
450    if ( ddp->ddp_next ) {
451	ddp->ddp_next->ddp_prev = ddp->ddp_prev;
452    }
453
454    (void) m_free( dtom( ddp ));
455}
456
457/*
458 * For the moment, this just find the pcb with the correct local address.
459 * In the future, this will actually do some real searching, so we can use
460 * the sender's address to do de-multiplexing on a single port to many
461 * sockets (pcbs).
462 */
463struct ddpcb *
464ddp_search( struct sockaddr_at *from, struct sockaddr_at *to,
465			struct at_ifaddr *aa)
466{
467    struct ddpcb	*ddp;
468
469    /*
470     * Check for bad ports.
471     */
472    if ( to->sat_port < ATPORT_FIRST || to->sat_port >= ATPORT_LAST ) {
473	return( NULL );
474    }
475
476    /*
477     * Make sure the local address matches the sent address.  What about
478     * the interface?
479     */
480    for ( ddp = ddp_ports[ to->sat_port - 1 ]; ddp; ddp = ddp->ddp_pnext ) {
481	/* XXX should we handle 0.YY? */
482
483	/* XXXX.YY to socket on destination interface */
484	if ( to->sat_addr.s_net == ddp->ddp_lsat.sat_addr.s_net &&
485		to->sat_addr.s_node == ddp->ddp_lsat.sat_addr.s_node ) {
486	    break;
487	}
488
489	/* 0.255 to socket on receiving interface */
490	if ( to->sat_addr.s_node == ATADDR_BCAST && ( to->sat_addr.s_net == 0 ||
491		to->sat_addr.s_net == ddp->ddp_lsat.sat_addr.s_net ) &&
492		ddp->ddp_lsat.sat_addr.s_net == AA_SAT( aa )->sat_addr.s_net ) {
493	    break;
494	}
495
496	/* XXXX.0 to socket on destination interface */
497	if ( to->sat_addr.s_net == aa->aa_firstnet &&
498		to->sat_addr.s_node == 0 &&
499		ntohs( ddp->ddp_lsat.sat_addr.s_net ) >=
500		ntohs( aa->aa_firstnet ) &&
501		ntohs( ddp->ddp_lsat.sat_addr.s_net ) <=
502		ntohs( aa->aa_lastnet )) {
503	    break;
504	}
505    }
506    return( ddp );
507}
508
509void
510ddp_init(void )
511{
512    atintrq1.ifq_maxlen = IFQ_MAXLEN;
513    atintrq2.ifq_maxlen = IFQ_MAXLEN;
514}
515
516#if 0
517static void
518ddp_clean(void )
519{
520    struct ddpcb	*ddp;
521
522    for ( ddp = ddpcb; ddp; ddp = ddp->ddp_next ) {
523	at_pcbdetach( ddp->ddp_socket, ddp );
524    }
525}
526#endif
527