xref: /netbsd-src/sys/netatalk/ddp_usrreq.c (revision 8b0f9554ff8762542c4defc4f70e1eb76fb508fa)
1 /*	$NetBSD: ddp_usrreq.c,v 1.25 2007/05/02 20:40:24 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.25 2007/05/02 20:40:24 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 	const struct sockaddr_at *cdst;
339 	struct sockaddr_at *sat = mtod(addr, struct sockaddr_at *);
340 	struct route *ro;
341 	struct at_ifaddr *aa;
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 	rtcache_check(ro);
369 	if (ro->ro_rt != NULL) {
370 		if (hintnet) {
371 			net = hintnet;
372 		} else {
373 			net = sat->sat_addr.s_net;
374 		}
375 		if ((ifp = ro->ro_rt->rt_ifp) != NULL) {
376 			TAILQ_FOREACH(aa, &at_ifaddr, aa_list) {
377 				if (aa->aa_ifp == ifp &&
378 				    ntohs(net) >= ntohs(aa->aa_firstnet) &&
379 				    ntohs(net) <= ntohs(aa->aa_lastnet)) {
380 					break;
381 				}
382 			}
383 		} else
384 			aa = NULL;
385 		cdst = satocsat(rtcache_getdst(ro));
386 		if (aa == NULL || (cdst->sat_addr.s_net !=
387 		    (hintnet ? hintnet : sat->sat_addr.s_net) ||
388 		    cdst->sat_addr.s_node != sat->sat_addr.s_node))
389 			rtcache_free(ro);
390 	}
391 	/*
392          * If we've got no route for this interface, try to find one.
393          */
394 	if (ro->ro_rt == NULL) {
395 		union {
396 			struct sockaddr		dst;
397 			struct sockaddr_at	dsta;
398 		} u;
399 
400 		sockaddr_at_init(&u.dsta, &sat->sat_addr, 0);
401 		if (hintnet)
402 			u.dsta.sat_addr.s_net = hintnet;
403 		rtcache_setdst(ro, &u.dst);
404 
405 		rtcache_init(ro);
406 	}
407 	/*
408          * Make sure any route that we have has a valid interface.
409          */
410 	if (ro->ro_rt != NULL && (ifp = ro->ro_rt->rt_ifp) != NULL) {
411 		TAILQ_FOREACH(aa, &at_ifaddr, aa_list) {
412 			if (aa->aa_ifp == ifp)
413 				break;
414 		}
415 	} else
416 		aa = NULL;
417 	if (aa == NULL)
418 		return ENETUNREACH;
419 	ddp->ddp_fsat = *sat;
420 	if (ddp->ddp_lsat.sat_port == ATADDR_ANYPORT)
421 		return at_pcbsetaddr(ddp, NULL, l);
422 	return 0;
423 }
424 
425 static void
426 at_pcbdisconnect(ddp)
427 	struct ddpcb   *ddp;
428 {
429 	ddp->ddp_fsat.sat_addr.s_net = ATADDR_ANYNET;
430 	ddp->ddp_fsat.sat_addr.s_node = ATADDR_ANYNODE;
431 	ddp->ddp_fsat.sat_port = ATADDR_ANYPORT;
432 }
433 
434 static int
435 at_pcballoc(so)
436 	struct socket  *so;
437 {
438 	struct ddpcb   *ddp;
439 
440 	MALLOC(ddp, struct ddpcb *, sizeof(*ddp), M_PCB, M_WAITOK|M_ZERO);
441 	if (!ddp)
442 		panic("at_pcballoc");
443 	ddp->ddp_lsat.sat_port = ATADDR_ANYPORT;
444 
445 	ddp->ddp_next = ddpcb;
446 	ddp->ddp_prev = NULL;
447 	ddp->ddp_pprev = NULL;
448 	ddp->ddp_pnext = NULL;
449 	if (ddpcb) {
450 		ddpcb->ddp_prev = ddp;
451 	}
452 	ddpcb = ddp;
453 
454 	ddp->ddp_socket = so;
455 	so->so_pcb = (void *) ddp;
456 #ifdef MBUFTRACE
457 	so->so_rcv.sb_mowner = &atalk_rx_mowner;
458 	so->so_snd.sb_mowner = &atalk_tx_mowner;
459 #endif
460 	return 0;
461 }
462 
463 static void
464 at_pcbdetach(so, ddp)
465 	struct socket  *so;
466 	struct ddpcb   *ddp;
467 {
468 	soisdisconnected(so);
469 	so->so_pcb = 0;
470 	sofree(so);
471 
472 	/* remove ddp from ddp_ports list */
473 	if (ddp->ddp_lsat.sat_port != ATADDR_ANYPORT &&
474 	    ddp_ports[ddp->ddp_lsat.sat_port - 1] != NULL) {
475 		if (ddp->ddp_pprev != NULL) {
476 			ddp->ddp_pprev->ddp_pnext = ddp->ddp_pnext;
477 		} else {
478 			ddp_ports[ddp->ddp_lsat.sat_port - 1] = ddp->ddp_pnext;
479 		}
480 		if (ddp->ddp_pnext != NULL) {
481 			ddp->ddp_pnext->ddp_pprev = ddp->ddp_pprev;
482 		}
483 	}
484 	rtcache_free(&ddp->ddp_route);
485 	if (ddp->ddp_prev) {
486 		ddp->ddp_prev->ddp_next = ddp->ddp_next;
487 	} else {
488 		ddpcb = ddp->ddp_next;
489 	}
490 	if (ddp->ddp_next) {
491 		ddp->ddp_next->ddp_prev = ddp->ddp_prev;
492 	}
493 	free(ddp, M_PCB);
494 }
495 
496 /*
497  * For the moment, this just find the pcb with the correct local address.
498  * In the future, this will actually do some real searching, so we can use
499  * the sender's address to do de-multiplexing on a single port to many
500  * sockets (pcbs).
501  */
502 struct ddpcb   *
503 ddp_search(
504     struct sockaddr_at *from,
505     struct sockaddr_at *to,
506     struct at_ifaddr *aa)
507 {
508 	struct ddpcb   *ddp;
509 
510 	/*
511          * Check for bad ports.
512          */
513 	if (to->sat_port < ATPORT_FIRST || to->sat_port >= ATPORT_LAST)
514 		return NULL;
515 
516 	/*
517          * Make sure the local address matches the sent address.  What about
518          * the interface?
519          */
520 	for (ddp = ddp_ports[to->sat_port - 1]; ddp; ddp = ddp->ddp_pnext) {
521 		/* XXX should we handle 0.YY? */
522 
523 		/* XXXX.YY to socket on destination interface */
524 		if (to->sat_addr.s_net == ddp->ddp_lsat.sat_addr.s_net &&
525 		    to->sat_addr.s_node == ddp->ddp_lsat.sat_addr.s_node) {
526 			break;
527 		}
528 		/* 0.255 to socket on receiving interface */
529 		if (to->sat_addr.s_node == ATADDR_BCAST &&
530 		    (to->sat_addr.s_net == 0 ||
531 		    to->sat_addr.s_net == ddp->ddp_lsat.sat_addr.s_net) &&
532 		ddp->ddp_lsat.sat_addr.s_net == AA_SAT(aa)->sat_addr.s_net) {
533 			break;
534 		}
535 		/* XXXX.0 to socket on destination interface */
536 		if (to->sat_addr.s_net == aa->aa_firstnet &&
537 		    to->sat_addr.s_node == 0 &&
538 		    ntohs(ddp->ddp_lsat.sat_addr.s_net) >=
539 		    ntohs(aa->aa_firstnet) &&
540 		    ntohs(ddp->ddp_lsat.sat_addr.s_net) <=
541 		    ntohs(aa->aa_lastnet)) {
542 			break;
543 		}
544 	}
545 	return (ddp);
546 }
547 
548 /*
549  * Initialize all the ddp & appletalk stuff
550  */
551 void
552 ddp_init()
553 {
554 	TAILQ_INIT(&at_ifaddr);
555 	atintrq1.ifq_maxlen = IFQ_MAXLEN;
556 	atintrq2.ifq_maxlen = IFQ_MAXLEN;
557 
558 	MOWNER_ATTACH(&atalk_tx_mowner);
559 	MOWNER_ATTACH(&atalk_rx_mowner);
560 }
561 
562 #if 0
563 static void
564 ddp_clean()
565 {
566 	struct ddpcb   *ddp;
567 
568 	for (ddp = ddpcb; ddp; ddp = ddp->ddp_next)
569 		at_pcbdetach(ddp->ddp_socket, ddp);
570 }
571 #endif
572