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