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