5390b52ca5f62694ccfe36db0ee5cf4563ad83c7
[dragonfly.git] / sys / netproto / atalk / ddp_usrreq.c
1 /*
2  * Copyright (c) 1990,1994 Regents of The University of Michigan.
3  * All Rights Reserved.  See COPYRIGHT.
4  *
5  * $DragonFly: src/sys/netproto/atalk/ddp_usrreq.c,v 1.14 2008/09/24 14:26:39 sephe Exp $
6  */
7
8 #include <sys/param.h>
9 #include <sys/systm.h>
10 #include <sys/proc.h>
11 #include <sys/priv.h>
12 #include <sys/malloc.h>
13 #include <sys/mbuf.h>
14 #include <sys/socket.h>
15 #include <sys/socketvar.h>
16 #include <sys/protosw.h>
17 #include <sys/thread2.h>
18 #include <net/if.h>
19 #include <net/netisr.h>
20 #include <net/route.h>
21
22 #include "at.h"
23 #include "at_var.h"
24 #include "ddp_var.h"
25 #include "at_extern.h"
26
27 static void at_pcbdisconnect( struct ddpcb *ddp );
28 static void at_sockaddr(struct ddpcb *ddp, struct sockaddr **addr);
29 static int at_pcbsetaddr(struct ddpcb *ddp, struct sockaddr *addr,
30                           struct thread *td);
31 static int at_pcbconnect(struct ddpcb *ddp, struct sockaddr *addr, 
32                          struct thread *td);
33 static void at_pcbdetach(struct socket *so, struct ddpcb *ddp);
34 static int at_pcballoc(struct socket *so);
35
36 struct ddpcb    *ddp_ports[ ATPORT_LAST ];
37 struct ddpcb    *ddpcb = NULL;
38 static u_long   ddp_sendspace = DDP_MAXSZ; /* Max ddp size + 1 (ddp_type) */
39 static u_long   ddp_recvspace = 10 * ( 587 + sizeof( struct sockaddr_at ));
40
41 static int
42 ddp_attach(struct socket *so, int proto, struct pru_attach_info *ai)
43 {
44         struct ddpcb    *ddp;
45         int             error = 0;
46         
47
48         ddp = sotoddpcb( so );
49         if ( ddp != NULL ) {
50             return( EINVAL);
51         }
52
53         crit_enter();
54         error = at_pcballoc( so );
55         crit_exit();
56         if (error) {
57             return (error);
58         }
59         return (soreserve( so, ddp_sendspace, ddp_recvspace, ai->sb_rlimit ));
60 }
61
62 static int
63 ddp_detach(struct socket *so)
64 {
65         struct ddpcb    *ddp;
66         
67         ddp = sotoddpcb( so );
68         if ( ddp == NULL ) {
69             return( EINVAL);
70         }
71         crit_enter();
72         at_pcbdetach( so, ddp );
73         crit_exit();
74         return(0);
75 }
76
77 static int      
78 ddp_bind(struct socket *so, struct sockaddr *nam, struct thread *td)
79 {
80         struct ddpcb    *ddp;
81         int             error = 0;
82         
83         ddp = sotoddpcb( so );
84         if ( ddp == NULL ) {
85             return( EINVAL);
86         }
87         crit_enter();
88         error = at_pcbsetaddr(ddp, nam, td);
89         crit_exit();
90         return (error);
91 }
92     
93 static int
94 ddp_connect(struct socket *so, struct sockaddr *nam, struct thread *td)
95 {
96         struct ddpcb    *ddp;
97         int             error = 0;
98         
99         ddp = sotoddpcb( so );
100         if ( ddp == NULL ) {
101             return( EINVAL);
102         }
103
104         if ( ddp->ddp_fsat.sat_port != ATADDR_ANYPORT ) {
105             return(EISCONN);
106         }
107
108         crit_enter();
109         error = at_pcbconnect( ddp, nam, td );
110         crit_exit();
111         if ( error == 0 )
112             soisconnected( so );
113         return(error);
114 }
115
116 static int
117 ddp_disconnect(struct socket *so)
118 {
119
120         struct ddpcb    *ddp;
121         
122         ddp = sotoddpcb( so );
123         if ( ddp == NULL ) {
124             return( EINVAL);
125         }
126         if ( ddp->ddp_fsat.sat_addr.s_node == ATADDR_ANYNODE ) {
127             return(ENOTCONN);
128         }
129
130         crit_enter();
131         at_pcbdisconnect( ddp );
132         ddp->ddp_fsat.sat_addr.s_node = ATADDR_ANYNODE;
133         crit_exit();
134         soisdisconnected( so );
135         return(0);
136 }
137
138 static int
139 ddp_shutdown(struct socket *so)
140 {
141         struct ddpcb    *ddp;
142
143         ddp = sotoddpcb( so );
144         if ( ddp == NULL ) {
145                 return( EINVAL);
146         }
147         socantsendmore( so );
148         return(0);
149 }
150
151 static int
152 ddp_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr,
153             struct mbuf *control, struct thread *td)
154 {
155         struct ddpcb    *ddp;
156         int             error = 0;
157         
158         ddp = sotoddpcb( so );
159         if ( ddp == NULL ) {
160                 return(EINVAL);
161         }
162
163         if ( control && control->m_len ) {
164                 return(EINVAL);
165         }
166
167         if ( addr ) {
168                 if ( ddp->ddp_fsat.sat_port != ATADDR_ANYPORT ) {
169                         return(EISCONN);
170                 }
171
172                 crit_enter();
173                 error = at_pcbconnect(ddp, addr, td);
174                 crit_exit();
175                 if ( error ) {
176                         return(error);
177                 }
178         } else {
179                 if ( ddp->ddp_fsat.sat_port == ATADDR_ANYPORT ) {
180                         return(ENOTCONN);
181                 }
182         }
183
184         crit_enter();
185         error = ddp_output( m, so );
186         if ( addr ) {
187             at_pcbdisconnect( ddp );
188         }
189         crit_exit();
190         return(error);
191 }
192
193 static int
194 ddp_abort(struct socket *so)
195 {
196         struct ddpcb    *ddp;
197         
198         ddp = sotoddpcb( so );
199         if ( ddp == NULL ) {
200                 return(EINVAL);
201         }
202         soisdisconnected( so );
203         crit_enter();
204         at_pcbdetach( so, ddp );
205         crit_exit();
206         return(0);
207 }
208
209
210 static void
211 at_sockaddr(struct ddpcb *ddp, struct sockaddr **addr)
212 {
213     *addr = dup_sockaddr((struct sockaddr *)&ddp->ddp_lsat);
214 }
215
216 static int 
217 at_pcbsetaddr(struct ddpcb *ddp, struct sockaddr *addr, struct thread *td)
218 {
219     struct sockaddr_at  lsat, *sat;
220     struct at_ifaddr    *aa;
221     struct ddpcb        *ddpp;
222
223     if ( ddp->ddp_lsat.sat_port != ATADDR_ANYPORT ) { /* shouldn't be bound */
224         return( EINVAL );
225     }
226
227     if (addr != 0) {                    /* validate passed address */
228         sat = (struct sockaddr_at *)addr;
229         if (sat->sat_family != AF_APPLETALK) {
230             return(EAFNOSUPPORT);
231         }
232
233         if ( sat->sat_addr.s_node != ATADDR_ANYNODE ||
234                 sat->sat_addr.s_net != ATADDR_ANYNET ) {
235             for ( aa = at_ifaddr; aa; aa = aa->aa_next ) {
236                 if (( sat->sat_addr.s_net == AA_SAT( aa )->sat_addr.s_net ) &&
237                  ( sat->sat_addr.s_node == AA_SAT( aa )->sat_addr.s_node )) {
238                     break;
239                 }
240             }
241             if ( !aa ) {
242                 return( EADDRNOTAVAIL );
243             }
244         }
245
246         if ( sat->sat_port != ATADDR_ANYPORT ) {
247             if ( sat->sat_port < ATPORT_FIRST ||
248                     sat->sat_port >= ATPORT_LAST ) {
249                 return( EINVAL );
250             }
251             if ( sat->sat_port < ATPORT_RESERVED &&
252                  priv_check(td, PRIV_ROOT) ) {
253                 return( EACCES );
254             }
255         }
256     } else {
257         bzero( (caddr_t)&lsat, sizeof( struct sockaddr_at ));
258         lsat.sat_len = sizeof(struct sockaddr_at);
259         lsat.sat_addr.s_node = ATADDR_ANYNODE;
260         lsat.sat_addr.s_net = ATADDR_ANYNET;
261         lsat.sat_family = AF_APPLETALK;
262         sat = &lsat;
263     }
264
265     if ( sat->sat_addr.s_node == ATADDR_ANYNODE &&
266             sat->sat_addr.s_net == ATADDR_ANYNET ) {
267         if ( at_ifaddr == NULL ) {
268             return( EADDRNOTAVAIL );
269         }
270         sat->sat_addr = AA_SAT( at_ifaddr )->sat_addr;
271     }
272     ddp->ddp_lsat = *sat;
273
274     /*
275      * Choose port.
276      */
277     if ( sat->sat_port == ATADDR_ANYPORT ) {
278         for ( sat->sat_port = ATPORT_RESERVED;
279                 sat->sat_port < ATPORT_LAST; sat->sat_port++ ) {
280             if ( ddp_ports[ sat->sat_port - 1 ] == 0 ) {
281                 break;
282             }
283         }
284         if ( sat->sat_port == ATPORT_LAST ) {
285             return( EADDRNOTAVAIL );
286         }
287         ddp->ddp_lsat.sat_port = sat->sat_port;
288         ddp_ports[ sat->sat_port - 1 ] = ddp;
289     } else {
290         for ( ddpp = ddp_ports[ sat->sat_port - 1 ]; ddpp;
291                 ddpp = ddpp->ddp_pnext ) {
292             if ( ddpp->ddp_lsat.sat_addr.s_net == sat->sat_addr.s_net &&
293                     ddpp->ddp_lsat.sat_addr.s_node == sat->sat_addr.s_node ) {
294                 break;
295             }
296         }
297         if ( ddpp != NULL ) {
298             return( EADDRINUSE );
299         }
300         ddp->ddp_pnext = ddp_ports[ sat->sat_port - 1 ];
301         ddp_ports[ sat->sat_port - 1 ] = ddp;
302         if ( ddp->ddp_pnext ) {
303             ddp->ddp_pnext->ddp_pprev = ddp;
304         }
305     }
306
307     return( 0 );
308 }
309
310 static int
311 at_pcbconnect(struct ddpcb *ddp, struct sockaddr *addr, struct thread *td)
312 {
313     struct sockaddr_at  *sat = (struct sockaddr_at *)addr;
314     struct route        *ro;
315     struct at_ifaddr    *aa = 0;
316     struct ifnet        *ifp;
317     u_short             hintnet = 0, net;
318
319     if (sat->sat_family != AF_APPLETALK) {
320         return(EAFNOSUPPORT);
321     }
322
323     /*
324      * Under phase 2, network 0 means "the network".  We take "the
325      * network" to mean the network the control block is bound to.
326      * If the control block is not bound, there is an error.
327      */
328     if ( sat->sat_addr.s_net == ATADDR_ANYNET
329                 && sat->sat_addr.s_node != ATADDR_ANYNODE ) {
330         if ( ddp->ddp_lsat.sat_port == ATADDR_ANYPORT ) {
331             return( EADDRNOTAVAIL );
332         }
333         hintnet = ddp->ddp_lsat.sat_addr.s_net;
334     }
335
336     ro = &ddp->ddp_route;
337     /*
338      * If we've got an old route for this pcb, check that it is valid.
339      * If we've changed our address, we may have an old "good looking"
340      * route here.  Attempt to detect it.
341      */
342     if ( ro->ro_rt ) {
343         if ( hintnet ) {
344             net = hintnet;
345         } else {
346             net = sat->sat_addr.s_net;
347         }
348         aa = 0;
349         if ((ifp = ro->ro_rt->rt_ifp) != NULL) {
350             for ( aa = at_ifaddr; aa; aa = aa->aa_next ) {
351                 if ( aa->aa_ifp == ifp &&
352                         ntohs( net ) >= ntohs( aa->aa_firstnet ) &&
353                         ntohs( net ) <= ntohs( aa->aa_lastnet )) {
354                     break;
355                 }
356             }
357         }
358         if ( aa == NULL || ( satosat( &ro->ro_dst )->sat_addr.s_net !=
359                 ( hintnet ? hintnet : sat->sat_addr.s_net ) ||
360                 satosat( &ro->ro_dst )->sat_addr.s_node !=
361                 sat->sat_addr.s_node )) {
362             RTFREE( ro->ro_rt );
363             ro->ro_rt = (struct rtentry *)0;
364         }
365     }
366
367     /*
368      * If we've got no route for this interface, try to find one.
369      */
370     if ( ro->ro_rt == (struct rtentry *)0 ||
371          ro->ro_rt->rt_ifp == (struct ifnet *)0 ) {
372         ro->ro_dst.sa_len = sizeof( struct sockaddr_at );
373         ro->ro_dst.sa_family = AF_APPLETALK;
374         if ( hintnet ) {
375             satosat( &ro->ro_dst )->sat_addr.s_net = hintnet;
376         } else {
377             satosat( &ro->ro_dst )->sat_addr.s_net = sat->sat_addr.s_net;
378         }
379         satosat( &ro->ro_dst )->sat_addr.s_node = sat->sat_addr.s_node;
380         rtalloc( ro );
381     }
382
383     /*
384      * Make sure any route that we have has a valid interface.
385      */
386     aa = 0;
387     if ( ro->ro_rt && ( ifp = ro->ro_rt->rt_ifp )) {
388         for ( aa = at_ifaddr; aa; aa = aa->aa_next ) {
389             if ( aa->aa_ifp == ifp ) {
390                 break;
391             }
392         }
393     }
394     if ( aa == 0 ) {
395         return( ENETUNREACH );
396     }
397
398     ddp->ddp_fsat = *sat;
399     if ( ddp->ddp_lsat.sat_port == ATADDR_ANYPORT ) {
400         return(at_pcbsetaddr(ddp, (struct sockaddr *)0, td));
401     }
402     return( 0 );
403 }
404
405 static void 
406 at_pcbdisconnect( struct ddpcb  *ddp )
407 {
408     ddp->ddp_fsat.sat_addr.s_net = ATADDR_ANYNET;
409     ddp->ddp_fsat.sat_addr.s_node = ATADDR_ANYNODE;
410     ddp->ddp_fsat.sat_port = ATADDR_ANYPORT;
411 }
412
413 static int
414 at_pcballoc( struct socket *so )
415 {
416         struct ddpcb    *ddp;
417
418         MALLOC(ddp, struct ddpcb *, sizeof *ddp, M_PCB, M_WAITOK | M_ZERO);
419         ddp->ddp_lsat.sat_port = ATADDR_ANYPORT;
420
421         ddp->ddp_next = ddpcb;
422         ddp->ddp_prev = NULL;
423         ddp->ddp_pprev = NULL;
424         ddp->ddp_pnext = NULL;
425         if (ddpcb) {
426                 ddpcb->ddp_prev = ddp;
427         }
428         ddpcb = ddp;
429
430         ddp->ddp_socket = so;
431         so->so_pcb = (caddr_t)ddp;
432         return(0);
433 }
434
435 static void
436 at_pcbdetach( struct socket *so, struct ddpcb *ddp)
437 {
438     soisdisconnected( so );
439     so->so_pcb = 0;
440     sofree( so );
441
442     /* remove ddp from ddp_ports list */
443     if ( ddp->ddp_lsat.sat_port != ATADDR_ANYPORT &&
444             ddp_ports[ ddp->ddp_lsat.sat_port - 1 ] != NULL ) {
445         if ( ddp->ddp_pprev != NULL ) {
446             ddp->ddp_pprev->ddp_pnext = ddp->ddp_pnext;
447         } else {
448             ddp_ports[ ddp->ddp_lsat.sat_port - 1 ] = ddp->ddp_pnext;
449         }
450         if ( ddp->ddp_pnext != NULL ) {
451             ddp->ddp_pnext->ddp_pprev = ddp->ddp_pprev;
452         }
453     }
454
455     if ( ddp->ddp_route.ro_rt ) {
456         rtfree( ddp->ddp_route.ro_rt );
457     }
458
459     if ( ddp->ddp_prev ) {
460         ddp->ddp_prev->ddp_next = ddp->ddp_next;
461     } else {
462         ddpcb = ddp->ddp_next;
463     }
464     if ( ddp->ddp_next ) {
465         ddp->ddp_next->ddp_prev = ddp->ddp_prev;
466     }
467     FREE(ddp, M_PCB);
468 }
469
470 /*
471  * For the moment, this just find the pcb with the correct local address.
472  * In the future, this will actually do some real searching, so we can use
473  * the sender's address to do de-multiplexing on a single port to many
474  * sockets (pcbs).
475  */
476 struct ddpcb *
477 ddp_search( struct sockaddr_at *from, struct sockaddr_at *to,
478                         struct at_ifaddr *aa)
479 {
480     struct ddpcb        *ddp;
481
482     /*
483      * Check for bad ports.
484      */
485     if ( to->sat_port < ATPORT_FIRST || to->sat_port >= ATPORT_LAST ) {
486         return( NULL );
487     }
488
489     /*
490      * Make sure the local address matches the sent address.  What about
491      * the interface?
492      */
493     for ( ddp = ddp_ports[ to->sat_port - 1 ]; ddp; ddp = ddp->ddp_pnext ) {
494         /* XXX should we handle 0.YY? */
495
496         /* XXXX.YY to socket on destination interface */
497         if ( to->sat_addr.s_net == ddp->ddp_lsat.sat_addr.s_net &&
498                 to->sat_addr.s_node == ddp->ddp_lsat.sat_addr.s_node ) {
499             break;
500         }
501
502         /* 0.255 to socket on receiving interface */
503         if ( to->sat_addr.s_node == ATADDR_BCAST && ( to->sat_addr.s_net == 0 ||
504                 to->sat_addr.s_net == ddp->ddp_lsat.sat_addr.s_net ) &&
505                 ddp->ddp_lsat.sat_addr.s_net == AA_SAT( aa )->sat_addr.s_net ) {
506             break;
507         }
508
509         /* XXXX.0 to socket on destination interface */
510         if ( to->sat_addr.s_net == aa->aa_firstnet &&
511                 to->sat_addr.s_node == 0 &&
512                 ntohs( ddp->ddp_lsat.sat_addr.s_net ) >=
513                 ntohs( aa->aa_firstnet ) &&
514                 ntohs( ddp->ddp_lsat.sat_addr.s_net ) <=
515                 ntohs( aa->aa_lastnet )) {
516             break;
517         }
518     }
519     return( ddp );
520 }
521 static int
522 at_setpeeraddr(struct socket *so, struct sockaddr **nam)
523 {
524         return(EOPNOTSUPP);
525 }
526
527 static int
528 at_setsockaddr(struct socket *so, struct sockaddr **nam)
529 {
530         struct ddpcb    *ddp;
531
532         ddp = sotoddpcb( so );
533         if ( ddp == NULL ) {
534             return( EINVAL);
535         }
536         at_sockaddr( ddp, nam );
537         return(0);
538 }
539
540
541 void 
542 ddp_init(void)
543 {
544         netisr_register(NETISR_ATALK1, cpu0_portfn, pktinfo_portfn_cpu0,
545                         at1intr, NETISR_FLAG_NOTMPSAFE);
546         netisr_register(NETISR_ATALK2, cpu0_portfn, pktinfo_portfn_cpu0,
547                         at2intr, NETISR_FLAG_NOTMPSAFE);
548         netisr_register(NETISR_AARP, cpu0_portfn, pktinfo_portfn_cpu0,
549                         aarpintr, NETISR_FLAG_NOTMPSAFE);
550 }
551
552 #if 0
553 static void 
554 ddp_clean(void)
555 {
556     struct ddpcb        *ddp;
557
558     for ( ddp = ddpcb; ddp; ddp = ddp->ddp_next ) {
559         at_pcbdetach( ddp->ddp_socket, ddp );
560     }
561 }
562 #endif
563
564 struct pr_usrreqs ddp_usrreqs = {
565         .pru_abort = ddp_abort,
566         .pru_accept = pru_accept_notsupp,
567         .pru_attach = ddp_attach,
568         .pru_bind = ddp_bind,
569         .pru_connect = ddp_connect,
570         .pru_connect2 = pru_connect2_notsupp,
571         .pru_control = at_control,
572         .pru_detach = ddp_detach,
573         .pru_disconnect = ddp_disconnect,
574         .pru_listen = pru_listen_notsupp,
575         .pru_peeraddr = at_setpeeraddr,
576         .pru_rcvd = pru_rcvd_notsupp,
577         .pru_rcvoob = pru_rcvoob_notsupp,
578         .pru_send = ddp_send,
579         .pru_sense = pru_sense_null,
580         .pru_shutdown = ddp_shutdown,
581         .pru_sockaddr = at_setsockaddr,
582         .pru_sosend = sosend,
583         .pru_soreceive = soreceive,
584         .pru_sopoll = sopoll
585 };