ddp_pcb.c revision 165974
1230479Snetchild/*-
2230479Snetchild * Copyright (c) 2004-2005 Robert N. M. Watson
3230479Snetchild * All rights reserved.
4230479Snetchild *
5230479Snetchild * Redistribution and use in source and binary forms, with or without
6230479Snetchild * modification, are permitted provided that the following conditions
7230479Snetchild * are met:
8230479Snetchild * 1. Redistributions of source code must retain the above copyright
9230479Snetchild *    notice, this list of conditions and the following disclaimer.
10230479Snetchild * 2. Redistributions in binary form must reproduce the above copyright
11230479Snetchild *    notice, this list of conditions and the following disclaimer in the
12230479Snetchild *    documentation and/or other materials provided with the distribution.
13230479Snetchild *
14230479Snetchild * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15230479Snetchild * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16230479Snetchild * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17230479Snetchild * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18230479Snetchild * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19230479Snetchild * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20230479Snetchild * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21230479Snetchild * 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: head/sys/netatalk/ddp_pcb.c 165974 2007-01-12 15:07:51Z rwatson $
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	if (addr != NULL) {
108		sat = (struct sockaddr_at *)addr;
109		if (sat->sat_family != AF_APPLETALK)
110			return (EAFNOSUPPORT);
111
112		if (sat->sat_addr.s_node != ATADDR_ANYNODE ||
113		    sat->sat_addr.s_net != ATADDR_ANYNET) {
114			for (aa = at_ifaddr_list; aa != NULL;
115			    aa = aa->aa_next) {
116				if ((sat->sat_addr.s_net ==
117				    AA_SAT(aa)->sat_addr.s_net) &&
118				    (sat->sat_addr.s_node ==
119				    AA_SAT(aa)->sat_addr.s_node))
120					break;
121			}
122			if (aa == NULL)
123				return (EADDRNOTAVAIL);
124		}
125
126		if (sat->sat_port != ATADDR_ANYPORT) {
127			if (sat->sat_port < ATPORT_FIRST ||
128			    sat->sat_port >= ATPORT_LAST)
129				return (EINVAL);
130			if (sat->sat_port < ATPORT_RESERVED &&
131			    priv_check(td, PRIV_NETATALK_RESERVEDPORT))
132				return (EACCES);
133		}
134	} else {
135		bzero((caddr_t)&lsat, sizeof(struct sockaddr_at));
136		lsat.sat_len = sizeof(struct sockaddr_at);
137		lsat.sat_addr.s_node = ATADDR_ANYNODE;
138		lsat.sat_addr.s_net = ATADDR_ANYNET;
139		lsat.sat_family = AF_APPLETALK;
140		sat = &lsat;
141	}
142
143	if (sat->sat_addr.s_node == ATADDR_ANYNODE &&
144	    sat->sat_addr.s_net == ATADDR_ANYNET) {
145		if (at_ifaddr_list == NULL)
146			return (EADDRNOTAVAIL);
147		sat->sat_addr = AA_SAT(at_ifaddr_list)->sat_addr;
148	}
149	ddp->ddp_lsat = *sat;
150
151	/*
152	 * Choose port.
153	 */
154	if (sat->sat_port == ATADDR_ANYPORT) {
155		for (sat->sat_port = ATPORT_RESERVED;
156		    sat->sat_port < ATPORT_LAST; sat->sat_port++) {
157			if (ddp_ports[sat->sat_port - 1] == NULL)
158				break;
159		}
160		if (sat->sat_port == ATPORT_LAST)
161			return (EADDRNOTAVAIL);
162		ddp->ddp_lsat.sat_port = sat->sat_port;
163		ddp_ports[sat->sat_port - 1] = ddp;
164	} else {
165		for (ddpp = ddp_ports[sat->sat_port - 1]; ddpp;
166		    ddpp = ddpp->ddp_pnext) {
167			if (ddpp->ddp_lsat.sat_addr.s_net ==
168			    sat->sat_addr.s_net &&
169			    ddpp->ddp_lsat.sat_addr.s_node ==
170			    sat->sat_addr.s_node)
171				break;
172		}
173		if (ddpp != NULL)
174			return (EADDRINUSE);
175		ddp->ddp_pnext = ddp_ports[sat->sat_port - 1];
176		ddp_ports[sat->sat_port - 1] = ddp;
177		if (ddp->ddp_pnext != NULL)
178			ddp->ddp_pnext->ddp_pprev = ddp;
179	}
180
181	return (0);
182}
183
184int
185at_pcbconnect(struct ddpcb *ddp, struct sockaddr *addr, struct thread *td)
186{
187	struct sockaddr_at	*sat = (struct sockaddr_at *)addr;
188	struct route	*ro;
189	struct at_ifaddr	*aa = NULL;
190	struct ifnet	*ifp;
191	u_short		hintnet = 0, net;
192
193	DDP_LIST_XLOCK_ASSERT();
194	DDP_LOCK_ASSERT(ddp);
195
196	if (sat->sat_family != AF_APPLETALK)
197		return (EAFNOSUPPORT);
198
199	/*
200	 * Under phase 2, network 0 means "the network".  We take "the
201	 * network" to mean the network the control block is bound to.  If
202	 * the control block is not bound, there is an error.
203	 */
204	if (sat->sat_addr.s_net == ATADDR_ANYNET &&
205	    sat->sat_addr.s_node != ATADDR_ANYNODE) {
206		if (ddp->ddp_lsat.sat_port == ATADDR_ANYPORT)
207			return (EADDRNOTAVAIL);
208		hintnet = ddp->ddp_lsat.sat_addr.s_net;
209	}
210
211	ro = &ddp->ddp_route;
212	/*
213	 * If we've got an old route for this pcb, check that it is valid.
214	 * If we've changed our address, we may have an old "good looking"
215	 * route here.  Attempt to detect it.
216	 */
217	if (ro->ro_rt) {
218		if (hintnet)
219			net = hintnet;
220		else
221			net = sat->sat_addr.s_net;
222		aa = NULL;
223		if ((ifp = ro->ro_rt->rt_ifp) != NULL) {
224			for (aa = at_ifaddr_list; aa != NULL;
225			    aa = aa->aa_next) {
226				if (aa->aa_ifp == ifp &&
227				    ntohs(net) >= ntohs(aa->aa_firstnet) &&
228				    ntohs(net) <= ntohs(aa->aa_lastnet))
229					break;
230			}
231		}
232		if (aa == NULL || (satosat(&ro->ro_dst)->sat_addr.s_net !=
233		    (hintnet ? hintnet : sat->sat_addr.s_net) ||
234		    satosat(&ro->ro_dst)->sat_addr.s_node !=
235		    sat->sat_addr.s_node)) {
236			RTFREE(ro->ro_rt);
237			ro->ro_rt = NULL;
238		}
239	}
240
241	/*
242	 * If we've got no route for this interface, try to find one.
243	 */
244	if (ro->ro_rt == NULL || ro->ro_rt->rt_ifp == NULL) {
245		ro->ro_dst.sa_len = sizeof(struct sockaddr_at);
246		ro->ro_dst.sa_family = AF_APPLETALK;
247		if (hintnet)
248			satosat(&ro->ro_dst)->sat_addr.s_net = hintnet;
249		else
250			satosat(&ro->ro_dst)->sat_addr.s_net =
251			    sat->sat_addr.s_net;
252		satosat(&ro->ro_dst)->sat_addr.s_node = sat->sat_addr.s_node;
253		rtalloc(ro);
254	}
255
256	/*
257	 * Make sure any route that we have has a valid interface.
258	 */
259	aa = NULL;
260	if (ro->ro_rt && (ifp = ro->ro_rt->rt_ifp)) {
261		for (aa = at_ifaddr_list; aa != NULL; aa = aa->aa_next) {
262			if (aa->aa_ifp == ifp)
263				break;
264		}
265	}
266	if (aa == NULL)
267		return (ENETUNREACH);
268
269	ddp->ddp_fsat = *sat;
270	if (ddp->ddp_lsat.sat_port == ATADDR_ANYPORT)
271		return (at_pcbsetaddr(ddp, NULL, td));
272	return (0);
273}
274
275void
276at_pcbdisconnect(struct ddpcb	*ddp)
277{
278
279	DDP_LOCK_ASSERT(ddp);
280
281	ddp->ddp_fsat.sat_addr.s_net = ATADDR_ANYNET;
282	ddp->ddp_fsat.sat_addr.s_node = ATADDR_ANYNODE;
283	ddp->ddp_fsat.sat_port = ATADDR_ANYPORT;
284}
285
286int
287at_pcballoc(struct socket *so)
288{
289	struct ddpcb *ddp;
290
291	DDP_LIST_XLOCK_ASSERT();
292
293	MALLOC(ddp, struct ddpcb *, sizeof *ddp, M_PCB, M_NOWAIT | M_ZERO);
294	if (ddp == NULL)
295		return (ENOBUFS);
296	DDP_LOCK_INIT(ddp);
297	ddp->ddp_lsat.sat_port = ATADDR_ANYPORT;
298
299	ddp->ddp_socket = so;
300	so->so_pcb = (caddr_t)ddp;
301
302	ddp->ddp_next = ddpcb_list;
303	ddp->ddp_prev = NULL;
304	ddp->ddp_pprev = NULL;
305	ddp->ddp_pnext = NULL;
306	if (ddpcb_list != NULL)
307		ddpcb_list->ddp_prev = ddp;
308	ddpcb_list = ddp;
309	return(0);
310}
311
312void
313at_pcbdetach(struct socket *so, struct ddpcb *ddp)
314{
315
316	/*
317	 * We modify ddp, ddp_ports, and the global list.
318	 */
319	DDP_LIST_XLOCK_ASSERT();
320	DDP_LOCK_ASSERT(ddp);
321	KASSERT(so->so_pcb != NULL, ("at_pcbdetach: so_pcb == NULL"));
322
323	so->so_pcb = NULL;
324
325	/* Remove ddp from ddp_ports list. */
326	if (ddp->ddp_lsat.sat_port != ATADDR_ANYPORT &&
327	    ddp_ports[ddp->ddp_lsat.sat_port - 1] != NULL) {
328		if (ddp->ddp_pprev != NULL)
329			ddp->ddp_pprev->ddp_pnext = ddp->ddp_pnext;
330		else
331			ddp_ports[ddp->ddp_lsat.sat_port - 1] = ddp->ddp_pnext;
332		if (ddp->ddp_pnext != NULL)
333			ddp->ddp_pnext->ddp_pprev = ddp->ddp_pprev;
334	}
335
336	if (ddp->ddp_route.ro_rt)
337		RTFREE(ddp->ddp_route.ro_rt);
338
339	if (ddp->ddp_prev)
340		ddp->ddp_prev->ddp_next = ddp->ddp_next;
341	else
342		ddpcb_list = ddp->ddp_next;
343	if (ddp->ddp_next)
344		ddp->ddp_next->ddp_prev = ddp->ddp_prev;
345	DDP_UNLOCK(ddp);
346	DDP_LOCK_DESTROY(ddp);
347	FREE(ddp, M_PCB);
348}
349
350/*
351 * For the moment, this just find the pcb with the correct local address.  In
352 * the future, this will actually do some real searching, so we can use the
353 * sender's address to do de-multiplexing on a single port to many sockets
354 * (pcbs).
355 */
356struct ddpcb *
357ddp_search(struct sockaddr_at *from, struct sockaddr_at *to,
358    struct at_ifaddr *aa)
359{
360	struct ddpcb *ddp;
361
362	DDP_LIST_SLOCK_ASSERT();
363
364	/*
365	 * Check for bad ports.
366	 */
367	if (to->sat_port < ATPORT_FIRST || to->sat_port >= ATPORT_LAST)
368		return (NULL);
369
370	/*
371	 * Make sure the local address matches the sent address.  What about
372	 * the interface?
373	 */
374	for (ddp = ddp_ports[to->sat_port - 1]; ddp; ddp = ddp->ddp_pnext) {
375		DDP_LOCK(ddp);
376		/* XXX should we handle 0.YY? */
377		/* XXXX.YY to socket on destination interface */
378		if (to->sat_addr.s_net == ddp->ddp_lsat.sat_addr.s_net &&
379		    to->sat_addr.s_node == ddp->ddp_lsat.sat_addr.s_node) {
380			DDP_UNLOCK(ddp);
381			break;
382		}
383
384		/* 0.255 to socket on receiving interface */
385		if (to->sat_addr.s_node == ATADDR_BCAST &&
386		    (to->sat_addr.s_net == 0 ||
387		    to->sat_addr.s_net == ddp->ddp_lsat.sat_addr.s_net) &&
388		    ddp->ddp_lsat.sat_addr.s_net ==
389		    AA_SAT(aa)->sat_addr.s_net) {
390			DDP_UNLOCK(ddp);
391			break;
392		}
393
394		/* XXXX.0 to socket on destination interface */
395		if (to->sat_addr.s_net == aa->aa_firstnet &&
396		    to->sat_addr.s_node == 0 &&
397		    ntohs(ddp->ddp_lsat.sat_addr.s_net) >=
398		    ntohs(aa->aa_firstnet) &&
399		    ntohs(ddp->ddp_lsat.sat_addr.s_net) <=
400		    ntohs(aa->aa_lastnet)) {
401			DDP_UNLOCK(ddp);
402			break;
403		}
404		DDP_UNLOCK(ddp);
405	}
406	return (ddp);
407}
408