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