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