1/*-
2 * Copyright (c) 2004-2009 Robert N. M. Watson
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * Copyright (c) 1990, 1994 Regents of The University of Michigan.
27 * All Rights Reserved.
28 *
29 * Permission to use, copy, modify, and distribute this software and
30 * its documentation for any purpose and without fee is hereby granted,
31 * provided that the above copyright notice appears in all copies and
32 * that both that copyright notice and this permission notice appear
33 * in supporting documentation, and that the name of The University
34 * of Michigan not be used in advertising or publicity pertaining to
35 * distribution of the software without specific, written prior
36 * permission. This software is supplied as is without expressed or
37 * implied warranties of any kind.
38 *
39 * This product includes software developed by the University of
40 * California, Berkeley and its contributors.
41 *
42 *	Research Systems Unix Group
43 *	The University of Michigan
44 *	c/o Wesley Craig
45 *	535 W. William Street
46 *	Ann Arbor, Michigan
47 *	+1-313-764-2278
48 *	netatalk@umich.edu
49 * $FreeBSD$
50 */
51
52#include <sys/param.h>
53#include <sys/systm.h>
54#include <sys/malloc.h>
55#include <sys/mbuf.h>
56#include <sys/priv.h>
57#include <sys/socket.h>
58#include <sys/socketvar.h>
59#include <sys/protosw.h>
60#include <net/if.h>
61#include <net/route.h>
62#include <net/netisr.h>
63
64#include <netatalk/at.h>
65#include <netatalk/at_var.h>
66#include <netatalk/ddp_var.h>
67#include <netatalk/ddp_pcb.h>
68#include <netatalk/at_extern.h>
69
70struct mtx		 ddp_list_mtx;
71static struct ddpcb	*ddp_ports[ATPORT_LAST];
72struct ddpcb		*ddpcb_list = NULL;
73
74void
75at_sockaddr(struct ddpcb *ddp, struct sockaddr **addr)
76{
77
78	/*
79	 * Prevent modification of ddp during copy of addr.
80	 */
81	DDP_LOCK_ASSERT(ddp);
82	*addr = sodupsockaddr((struct sockaddr *)&ddp->ddp_lsat, M_NOWAIT);
83}
84
85int
86at_pcbsetaddr(struct ddpcb *ddp, struct sockaddr *addr, struct thread *td)
87{
88	struct sockaddr_at lsat, *sat;
89	struct at_ifaddr *aa;
90	struct ddpcb *ddpp;
91
92	/*
93	 * We read and write both the ddp passed in, and also ddp_ports.
94	 */
95	DDP_LIST_XLOCK_ASSERT();
96	DDP_LOCK_ASSERT(ddp);
97
98	/*
99	 * Shouldn't be bound.
100	 */
101	if (ddp->ddp_lsat.sat_port != ATADDR_ANYPORT)
102		return (EINVAL);
103
104	/*
105	 * Validate passed address.
106	 */
107	aa = NULL;
108	if (addr != NULL) {
109		sat = (struct sockaddr_at *)addr;
110		if (sat->sat_family != AF_APPLETALK)
111			return (EAFNOSUPPORT);
112
113		if (sat->sat_addr.s_node != ATADDR_ANYNODE ||
114		    sat->sat_addr.s_net != ATADDR_ANYNET) {
115			AT_IFADDR_RLOCK();
116			TAILQ_FOREACH(aa, &at_ifaddrhead, aa_link) {
117				if ((sat->sat_addr.s_net ==
118				    AA_SAT(aa)->sat_addr.s_net) &&
119				    (sat->sat_addr.s_node ==
120				    AA_SAT(aa)->sat_addr.s_node))
121					break;
122			}
123			AT_IFADDR_RUNLOCK();
124			if (aa == NULL)
125				return (EADDRNOTAVAIL);
126		}
127
128		if (sat->sat_port != ATADDR_ANYPORT) {
129			if (sat->sat_port < ATPORT_FIRST ||
130			    sat->sat_port >= ATPORT_LAST)
131				return (EINVAL);
132			if (sat->sat_port < ATPORT_RESERVED &&
133			    priv_check(td, PRIV_NETATALK_RESERVEDPORT))
134				return (EACCES);
135		}
136	} else {
137		bzero((caddr_t)&lsat, sizeof(struct sockaddr_at));
138		lsat.sat_len = sizeof(struct sockaddr_at);
139		lsat.sat_addr.s_node = ATADDR_ANYNODE;
140		lsat.sat_addr.s_net = ATADDR_ANYNET;
141		lsat.sat_family = AF_APPLETALK;
142		sat = &lsat;
143	}
144
145	if (sat->sat_addr.s_node == ATADDR_ANYNODE &&
146	    sat->sat_addr.s_net == ATADDR_ANYNET) {
147		AT_IFADDR_RLOCK();
148		if (TAILQ_EMPTY(&at_ifaddrhead)) {
149			AT_IFADDR_RUNLOCK();
150			return (EADDRNOTAVAIL);
151		}
152		sat->sat_addr = AA_SAT(TAILQ_FIRST(&at_ifaddrhead))->sat_addr;
153		AT_IFADDR_RUNLOCK();
154	}
155	ddp->ddp_lsat = *sat;
156
157	/*
158	 * Choose port.
159	 */
160	if (sat->sat_port == ATADDR_ANYPORT) {
161		for (sat->sat_port = ATPORT_RESERVED;
162		    sat->sat_port < ATPORT_LAST; sat->sat_port++) {
163			if (ddp_ports[sat->sat_port - 1] == NULL)
164				break;
165		}
166		if (sat->sat_port == ATPORT_LAST)
167			return (EADDRNOTAVAIL);
168		ddp->ddp_lsat.sat_port = sat->sat_port;
169		ddp_ports[sat->sat_port - 1] = ddp;
170	} else {
171		for (ddpp = ddp_ports[sat->sat_port - 1]; ddpp;
172		    ddpp = ddpp->ddp_pnext) {
173			if (ddpp->ddp_lsat.sat_addr.s_net ==
174			    sat->sat_addr.s_net &&
175			    ddpp->ddp_lsat.sat_addr.s_node ==
176			    sat->sat_addr.s_node)
177				break;
178		}
179		if (ddpp != NULL)
180			return (EADDRINUSE);
181		ddp->ddp_pnext = ddp_ports[sat->sat_port - 1];
182		ddp_ports[sat->sat_port - 1] = ddp;
183		if (ddp->ddp_pnext != NULL)
184			ddp->ddp_pnext->ddp_pprev = ddp;
185	}
186
187	return (0);
188}
189
190int
191at_pcbconnect(struct ddpcb *ddp, struct sockaddr *addr, struct thread *td)
192{
193	struct sockaddr_at	*sat = (struct sockaddr_at *)addr;
194	struct route	*ro;
195	struct at_ifaddr	*aa = NULL;
196	struct ifnet	*ifp;
197	u_short		hintnet = 0, net;
198
199	DDP_LIST_XLOCK_ASSERT();
200	DDP_LOCK_ASSERT(ddp);
201
202	if (sat->sat_family != AF_APPLETALK)
203		return (EAFNOSUPPORT);
204
205	/*
206	 * Under phase 2, network 0 means "the network".  We take "the
207	 * network" to mean the network the control block is bound to.  If
208	 * the control block is not bound, there is an error.
209	 */
210	if (sat->sat_addr.s_net == ATADDR_ANYNET &&
211	    sat->sat_addr.s_node != ATADDR_ANYNODE) {
212		if (ddp->ddp_lsat.sat_port == ATADDR_ANYPORT)
213			return (EADDRNOTAVAIL);
214		hintnet = ddp->ddp_lsat.sat_addr.s_net;
215	}
216
217	ro = &ddp->ddp_route;
218	/*
219	 * If we've got an old route for this pcb, check that it is valid.
220	 * If we've changed our address, we may have an old "good looking"
221	 * route here.  Attempt to detect it.
222	 */
223	if (ro->ro_rt) {
224		if (hintnet)
225			net = hintnet;
226		else
227			net = sat->sat_addr.s_net;
228		aa = NULL;
229		AT_IFADDR_RLOCK();
230		if ((ifp = ro->ro_rt->rt_ifp) != NULL) {
231			TAILQ_FOREACH(aa, &at_ifaddrhead, aa_link) {
232				if (aa->aa_ifp == ifp &&
233				    ntohs(net) >= ntohs(aa->aa_firstnet) &&
234				    ntohs(net) <= ntohs(aa->aa_lastnet))
235					break;
236			}
237		}
238		if (aa == NULL || (satosat(&ro->ro_dst)->sat_addr.s_net !=
239		    (hintnet ? hintnet : sat->sat_addr.s_net) ||
240		    satosat(&ro->ro_dst)->sat_addr.s_node !=
241		    sat->sat_addr.s_node)) {
242			RTFREE(ro->ro_rt);
243			ro->ro_rt = NULL;
244		}
245		AT_IFADDR_RUNLOCK();
246	}
247
248	/*
249	 * If we've got no route for this interface, try to find one.
250	 */
251	if (ro->ro_rt == NULL || ro->ro_rt->rt_ifp == NULL) {
252		ro->ro_dst.sa_len = sizeof(struct sockaddr_at);
253		ro->ro_dst.sa_family = AF_APPLETALK;
254		if (hintnet)
255			satosat(&ro->ro_dst)->sat_addr.s_net = hintnet;
256		else
257			satosat(&ro->ro_dst)->sat_addr.s_net =
258			    sat->sat_addr.s_net;
259		satosat(&ro->ro_dst)->sat_addr.s_node = sat->sat_addr.s_node;
260		rtalloc(ro);
261	}
262
263	/*
264	 * Make sure any route that we have has a valid interface.
265	 */
266	aa = NULL;
267	if (ro->ro_rt && (ifp = ro->ro_rt->rt_ifp)) {
268		AT_IFADDR_RLOCK();
269		TAILQ_FOREACH(aa, &at_ifaddrhead, aa_link) {
270			if (aa->aa_ifp == ifp)
271				break;
272		}
273		AT_IFADDR_RUNLOCK();
274	}
275	if (aa == NULL)
276		return (ENETUNREACH);
277
278	ddp->ddp_fsat = *sat;
279	if (ddp->ddp_lsat.sat_port == ATADDR_ANYPORT)
280		return (at_pcbsetaddr(ddp, NULL, td));
281	return (0);
282}
283
284void
285at_pcbdisconnect(struct ddpcb	*ddp)
286{
287
288	DDP_LOCK_ASSERT(ddp);
289
290	ddp->ddp_fsat.sat_addr.s_net = ATADDR_ANYNET;
291	ddp->ddp_fsat.sat_addr.s_node = ATADDR_ANYNODE;
292	ddp->ddp_fsat.sat_port = ATADDR_ANYPORT;
293}
294
295int
296at_pcballoc(struct socket *so)
297{
298	struct ddpcb *ddp;
299
300	DDP_LIST_XLOCK_ASSERT();
301
302	ddp = malloc(sizeof *ddp, M_PCB, M_NOWAIT | M_ZERO);
303	if (ddp == NULL)
304		return (ENOBUFS);
305	DDP_LOCK_INIT(ddp);
306	ddp->ddp_lsat.sat_port = ATADDR_ANYPORT;
307
308	ddp->ddp_socket = so;
309	so->so_pcb = (caddr_t)ddp;
310
311	ddp->ddp_next = ddpcb_list;
312	ddp->ddp_prev = NULL;
313	ddp->ddp_pprev = NULL;
314	ddp->ddp_pnext = NULL;
315	if (ddpcb_list != NULL)
316		ddpcb_list->ddp_prev = ddp;
317	ddpcb_list = ddp;
318	return(0);
319}
320
321void
322at_pcbdetach(struct socket *so, struct ddpcb *ddp)
323{
324
325	/*
326	 * We modify ddp, ddp_ports, and the global list.
327	 */
328	DDP_LIST_XLOCK_ASSERT();
329	DDP_LOCK_ASSERT(ddp);
330	KASSERT(so->so_pcb != NULL, ("at_pcbdetach: so_pcb == NULL"));
331
332	so->so_pcb = NULL;
333
334	/* Remove ddp from ddp_ports list. */
335	if (ddp->ddp_lsat.sat_port != ATADDR_ANYPORT &&
336	    ddp_ports[ddp->ddp_lsat.sat_port - 1] != NULL) {
337		if (ddp->ddp_pprev != NULL)
338			ddp->ddp_pprev->ddp_pnext = ddp->ddp_pnext;
339		else
340			ddp_ports[ddp->ddp_lsat.sat_port - 1] = ddp->ddp_pnext;
341		if (ddp->ddp_pnext != NULL)
342			ddp->ddp_pnext->ddp_pprev = ddp->ddp_pprev;
343	}
344
345	if (ddp->ddp_route.ro_rt)
346		RTFREE(ddp->ddp_route.ro_rt);
347
348	if (ddp->ddp_prev)
349		ddp->ddp_prev->ddp_next = ddp->ddp_next;
350	else
351		ddpcb_list = ddp->ddp_next;
352	if (ddp->ddp_next)
353		ddp->ddp_next->ddp_prev = ddp->ddp_prev;
354	DDP_UNLOCK(ddp);
355	DDP_LOCK_DESTROY(ddp);
356	free(ddp, M_PCB);
357}
358
359/*
360 * For the moment, this just find the pcb with the correct local address.  In
361 * the future, this will actually do some real searching, so we can use the
362 * sender's address to do de-multiplexing on a single port to many sockets
363 * (pcbs).
364 */
365struct ddpcb *
366ddp_search(struct sockaddr_at *from, struct sockaddr_at *to,
367    struct at_ifaddr *aa)
368{
369	struct ddpcb *ddp;
370
371	DDP_LIST_SLOCK_ASSERT();
372
373	/*
374	 * Check for bad ports.
375	 */
376	if (to->sat_port < ATPORT_FIRST || to->sat_port >= ATPORT_LAST)
377		return (NULL);
378
379	/*
380	 * Make sure the local address matches the sent address.  What about
381	 * the interface?
382	 */
383	for (ddp = ddp_ports[to->sat_port - 1]; ddp; ddp = ddp->ddp_pnext) {
384		DDP_LOCK(ddp);
385		/* XXX should we handle 0.YY? */
386		/* XXXX.YY to socket on destination interface */
387		if (to->sat_addr.s_net == ddp->ddp_lsat.sat_addr.s_net &&
388		    to->sat_addr.s_node == ddp->ddp_lsat.sat_addr.s_node) {
389			DDP_UNLOCK(ddp);
390			break;
391		}
392
393		/* 0.255 to socket on receiving interface */
394		if (to->sat_addr.s_node == ATADDR_BCAST &&
395		    (to->sat_addr.s_net == 0 ||
396		    to->sat_addr.s_net == ddp->ddp_lsat.sat_addr.s_net) &&
397		    ddp->ddp_lsat.sat_addr.s_net ==
398		    AA_SAT(aa)->sat_addr.s_net) {
399			DDP_UNLOCK(ddp);
400			break;
401		}
402
403		/* XXXX.0 to socket on destination interface */
404		if (to->sat_addr.s_net == aa->aa_firstnet &&
405		    to->sat_addr.s_node == 0 &&
406		    ntohs(ddp->ddp_lsat.sat_addr.s_net) >=
407		    ntohs(aa->aa_firstnet) &&
408		    ntohs(ddp->ddp_lsat.sat_addr.s_net) <=
409		    ntohs(aa->aa_lastnet)) {
410			DDP_UNLOCK(ddp);
411			break;
412		}
413		DDP_UNLOCK(ddp);
414	}
415	return (ddp);
416}
417