ddp_pcb.c revision 194619
1139827Simp/*-
2194619Srwatson * Copyright (c) 2004-2009 Robert N. M. Watson
3165891Srwatson * All rights reserved.
4165891Srwatson *
5165891Srwatson * Redistribution and use in source and binary forms, with or without
6165891Srwatson * modification, are permitted provided that the following conditions
7165891Srwatson * are met:
8165891Srwatson * 1. Redistributions of source code must retain the above copyright
9165891Srwatson *    notice, this list of conditions and the following disclaimer.
10165891Srwatson * 2. Redistributions in binary form must reproduce the above copyright
11165891Srwatson *    notice, this list of conditions and the following disclaimer in the
12165891Srwatson *    documentation and/or other materials provided with the distribution.
13165891Srwatson *
14165891Srwatson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15165891Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16165891Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17165891Srwatson * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18165891Srwatson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19165891Srwatson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20165891Srwatson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21165891Srwatson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22165891Srwatson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23165891Srwatson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24165891Srwatson * SUCH DAMAGE.
25165891Srwatson *
26165974Srwatson * Copyright (c) 1990, 1994 Regents of The University of Michigan.
27139827Simp * All Rights Reserved.
2867893Sphk *
29139827Simp * Permission to use, copy, modify, and distribute this software and
30139827Simp * its documentation for any purpose and without fee is hereby granted,
31139827Simp * provided that the above copyright notice appears in all copies and
32139827Simp * that both that copyright notice and this permission notice appear
33139827Simp * in supporting documentation, and that the name of The University
34139827Simp * of Michigan not be used in advertising or publicity pertaining to
35139827Simp * distribution of the software without specific, written prior
36139827Simp * permission. This software is supplied as is without expressed or
37139827Simp * implied warranties of any kind.
38139827Simp *
39139827Simp * This product includes software developed by the University of
40139827Simp * California, Berkeley and its contributors.
41139827Simp *
42139827Simp *	Research Systems Unix Group
43139827Simp *	The University of Michigan
44139827Simp *	c/o Wesley Craig
45139827Simp *	535 W. William Street
46139827Simp *	Ann Arbor, Michigan
47139827Simp *	+1-313-764-2278
48139827Simp *	netatalk@umich.edu
4967893Sphk * $FreeBSD: head/sys/netatalk/ddp_pcb.c 194619 2009-06-22 10:23:54Z rwatson $
5015885Sjulian */
5115885Sjulian
5215885Sjulian#include <sys/param.h>
5315885Sjulian#include <sys/systm.h>
5429024Sbde#include <sys/malloc.h>
5515885Sjulian#include <sys/mbuf.h>
56164033Srwatson#include <sys/priv.h>
5715885Sjulian#include <sys/socket.h>
5815885Sjulian#include <sys/socketvar.h>
5915885Sjulian#include <sys/protosw.h>
6015885Sjulian#include <net/if.h>
6115885Sjulian#include <net/route.h>
62111888Sjlemon#include <net/netisr.h>
6315885Sjulian
6418207Sbde#include <netatalk/at.h>
6518207Sbde#include <netatalk/at_var.h>
6618207Sbde#include <netatalk/ddp_var.h>
67127195Srwatson#include <netatalk/ddp_pcb.h>
6815885Sjulian#include <netatalk/at_extern.h>
6915885Sjulian
70132043Srwatsonstruct mtx		 ddp_list_mtx;
71165974Srwatsonstatic struct ddpcb	*ddp_ports[ATPORT_LAST];
72132043Srwatsonstruct ddpcb		*ddpcb_list = NULL;
7315885Sjulian
74127195Srwatsonvoid
7528270Swollmanat_sockaddr(struct ddpcb *ddp, struct sockaddr **addr)
7615885Sjulian{
77132043Srwatson
78165974Srwatson	/*
79165974Srwatson	 * Prevent modification of ddp during copy of addr.
80165974Srwatson	 */
81165974Srwatson	DDP_LOCK_ASSERT(ddp);
82165974Srwatson	*addr = sodupsockaddr((struct sockaddr *)&ddp->ddp_lsat, M_NOWAIT);
8315885Sjulian}
8415885Sjulian
85127195Srwatsonint
8683366Sjulianat_pcbsetaddr(struct ddpcb *ddp, struct sockaddr *addr, struct thread *td)
8715885Sjulian{
88165974Srwatson	struct sockaddr_at lsat, *sat;
89165974Srwatson	struct at_ifaddr *aa;
90165974Srwatson	struct ddpcb *ddpp;
9115885Sjulian
92165974Srwatson	/*
93165974Srwatson	 * We read and write both the ddp passed in, and also ddp_ports.
94165974Srwatson	 */
95165974Srwatson	DDP_LIST_XLOCK_ASSERT();
96165974Srwatson	DDP_LOCK_ASSERT(ddp);
97132043Srwatson
98165974Srwatson	/*
99165974Srwatson	 * Shouldn't be bound.
100165974Srwatson	 */
101165974Srwatson	if (ddp->ddp_lsat.sat_port != ATADDR_ANYPORT)
102165974Srwatson		return (EINVAL);
10315885Sjulian
104165974Srwatson	/*
105165974Srwatson	 * Validate passed address.
106165974Srwatson	 */
107194619Srwatson	aa = NULL;
108165974Srwatson	if (addr != NULL) {
109165974Srwatson		sat = (struct sockaddr_at *)addr;
110165974Srwatson		if (sat->sat_family != AF_APPLETALK)
111165974Srwatson			return (EAFNOSUPPORT);
11215885Sjulian
113165974Srwatson		if (sat->sat_addr.s_node != ATADDR_ANYNODE ||
114165974Srwatson		    sat->sat_addr.s_net != ATADDR_ANYNET) {
115194619Srwatson			AT_IFADDR_RLOCK();
116165974Srwatson			for (aa = at_ifaddr_list; aa != NULL;
117165974Srwatson			    aa = aa->aa_next) {
118165974Srwatson				if ((sat->sat_addr.s_net ==
119165974Srwatson				    AA_SAT(aa)->sat_addr.s_net) &&
120165974Srwatson				    (sat->sat_addr.s_node ==
121165974Srwatson				    AA_SAT(aa)->sat_addr.s_node))
122165974Srwatson					break;
123165974Srwatson			}
124194619Srwatson			AT_IFADDR_RUNLOCK();
125165974Srwatson			if (aa == NULL)
126165974Srwatson				return (EADDRNOTAVAIL);
12715885Sjulian		}
12815885Sjulian
129165974Srwatson		if (sat->sat_port != ATADDR_ANYPORT) {
130165974Srwatson			if (sat->sat_port < ATPORT_FIRST ||
131165974Srwatson			    sat->sat_port >= ATPORT_LAST)
132165974Srwatson				return (EINVAL);
133165974Srwatson			if (sat->sat_port < ATPORT_RESERVED &&
134165974Srwatson			    priv_check(td, PRIV_NETATALK_RESERVEDPORT))
135165974Srwatson				return (EACCES);
136165974Srwatson		}
137165974Srwatson	} else {
138165974Srwatson		bzero((caddr_t)&lsat, sizeof(struct sockaddr_at));
139165974Srwatson		lsat.sat_len = sizeof(struct sockaddr_at);
140165974Srwatson		lsat.sat_addr.s_node = ATADDR_ANYNODE;
141165974Srwatson		lsat.sat_addr.s_net = ATADDR_ANYNET;
142165974Srwatson		lsat.sat_family = AF_APPLETALK;
143165974Srwatson		sat = &lsat;
14415885Sjulian	}
14515885Sjulian
146165974Srwatson	if (sat->sat_addr.s_node == ATADDR_ANYNODE &&
147127288Srwatson	    sat->sat_addr.s_net == ATADDR_ANYNET) {
148194619Srwatson		AT_IFADDR_RLOCK();
149194619Srwatson		if (at_ifaddr_list == NULL) {
150194619Srwatson			AT_IFADDR_RUNLOCK();
151165974Srwatson			return (EADDRNOTAVAIL);
152194619Srwatson		}
153165974Srwatson		sat->sat_addr = AA_SAT(at_ifaddr_list)->sat_addr;
154194619Srwatson		AT_IFADDR_RUNLOCK();
15515885Sjulian	}
156165974Srwatson	ddp->ddp_lsat = *sat;
15715885Sjulian
158165974Srwatson	/*
159165974Srwatson	 * Choose port.
160165974Srwatson	 */
161165974Srwatson	if (sat->sat_port == ATADDR_ANYPORT) {
162165974Srwatson		for (sat->sat_port = ATPORT_RESERVED;
163165974Srwatson		    sat->sat_port < ATPORT_LAST; sat->sat_port++) {
164165974Srwatson			if (ddp_ports[sat->sat_port - 1] == NULL)
165165974Srwatson				break;
166165974Srwatson		}
167165974Srwatson		if (sat->sat_port == ATPORT_LAST)
168165974Srwatson			return (EADDRNOTAVAIL);
169165974Srwatson		ddp->ddp_lsat.sat_port = sat->sat_port;
170165974Srwatson		ddp_ports[sat->sat_port - 1] = ddp;
171165974Srwatson	} else {
172165974Srwatson		for (ddpp = ddp_ports[sat->sat_port - 1]; ddpp;
173165974Srwatson		    ddpp = ddpp->ddp_pnext) {
174165974Srwatson			if (ddpp->ddp_lsat.sat_addr.s_net ==
175165974Srwatson			    sat->sat_addr.s_net &&
176165974Srwatson			    ddpp->ddp_lsat.sat_addr.s_node ==
177165974Srwatson			    sat->sat_addr.s_node)
178165974Srwatson				break;
179165974Srwatson		}
180165974Srwatson		if (ddpp != NULL)
181165974Srwatson			return (EADDRINUSE);
182165974Srwatson		ddp->ddp_pnext = ddp_ports[sat->sat_port - 1];
183165974Srwatson		ddp_ports[sat->sat_port - 1] = ddp;
184165974Srwatson		if (ddp->ddp_pnext != NULL)
185165974Srwatson			ddp->ddp_pnext->ddp_pprev = ddp;
18615885Sjulian	}
18715885Sjulian
188165974Srwatson	return (0);
18915885Sjulian}
19015885Sjulian
191127195Srwatsonint
19283366Sjulianat_pcbconnect(struct ddpcb *ddp, struct sockaddr *addr, struct thread *td)
19315885Sjulian{
194165974Srwatson	struct sockaddr_at	*sat = (struct sockaddr_at *)addr;
195165974Srwatson	struct route	*ro;
196165974Srwatson	struct at_ifaddr	*aa = NULL;
197165974Srwatson	struct ifnet	*ifp;
198165974Srwatson	u_short		hintnet = 0, net;
19915885Sjulian
200165974Srwatson	DDP_LIST_XLOCK_ASSERT();
201165974Srwatson	DDP_LOCK_ASSERT(ddp);
202132043Srwatson
203165974Srwatson	if (sat->sat_family != AF_APPLETALK)
204165974Srwatson		return (EAFNOSUPPORT);
20515885Sjulian
206165974Srwatson	/*
207165974Srwatson	 * Under phase 2, network 0 means "the network".  We take "the
208165974Srwatson	 * network" to mean the network the control block is bound to.  If
209165974Srwatson	 * the control block is not bound, there is an error.
210165974Srwatson	 */
211165974Srwatson	if (sat->sat_addr.s_net == ATADDR_ANYNET &&
212165974Srwatson	    sat->sat_addr.s_node != ATADDR_ANYNODE) {
213165974Srwatson		if (ddp->ddp_lsat.sat_port == ATADDR_ANYPORT)
214165974Srwatson			return (EADDRNOTAVAIL);
215165974Srwatson		hintnet = ddp->ddp_lsat.sat_addr.s_net;
21615885Sjulian	}
21715885Sjulian
218165974Srwatson	ro = &ddp->ddp_route;
219165974Srwatson	/*
220165974Srwatson	 * If we've got an old route for this pcb, check that it is valid.
221165974Srwatson	 * If we've changed our address, we may have an old "good looking"
222165974Srwatson	 * route here.  Attempt to detect it.
223165974Srwatson	 */
224165974Srwatson	if (ro->ro_rt) {
225165974Srwatson		if (hintnet)
226165974Srwatson			net = hintnet;
227165974Srwatson		else
228165974Srwatson			net = sat->sat_addr.s_net;
229165974Srwatson		aa = NULL;
230194619Srwatson		AT_IFADDR_RLOCK();
231165974Srwatson		if ((ifp = ro->ro_rt->rt_ifp) != NULL) {
232165974Srwatson			for (aa = at_ifaddr_list; aa != NULL;
233165974Srwatson			    aa = aa->aa_next) {
234165974Srwatson				if (aa->aa_ifp == ifp &&
235165974Srwatson				    ntohs(net) >= ntohs(aa->aa_firstnet) &&
236165974Srwatson				    ntohs(net) <= ntohs(aa->aa_lastnet))
237165974Srwatson					break;
238165974Srwatson			}
23915885Sjulian		}
240165974Srwatson		if (aa == NULL || (satosat(&ro->ro_dst)->sat_addr.s_net !=
241165974Srwatson		    (hintnet ? hintnet : sat->sat_addr.s_net) ||
242165974Srwatson		    satosat(&ro->ro_dst)->sat_addr.s_node !=
243165974Srwatson		    sat->sat_addr.s_node)) {
244165974Srwatson			RTFREE(ro->ro_rt);
245165974Srwatson			ro->ro_rt = NULL;
246165974Srwatson		}
247194619Srwatson		AT_IFADDR_RUNLOCK();
24815885Sjulian	}
24915885Sjulian
250165974Srwatson	/*
251165974Srwatson	 * If we've got no route for this interface, try to find one.
252165974Srwatson	 */
253165974Srwatson	if (ro->ro_rt == NULL || ro->ro_rt->rt_ifp == NULL) {
254165974Srwatson		ro->ro_dst.sa_len = sizeof(struct sockaddr_at);
255165974Srwatson		ro->ro_dst.sa_family = AF_APPLETALK;
256165974Srwatson		if (hintnet)
257165974Srwatson			satosat(&ro->ro_dst)->sat_addr.s_net = hintnet;
258165974Srwatson		else
259165974Srwatson			satosat(&ro->ro_dst)->sat_addr.s_net =
260165974Srwatson			    sat->sat_addr.s_net;
261165974Srwatson		satosat(&ro->ro_dst)->sat_addr.s_node = sat->sat_addr.s_node;
262165974Srwatson		rtalloc(ro);
26315885Sjulian	}
26415885Sjulian
265165974Srwatson	/*
266165974Srwatson	 * Make sure any route that we have has a valid interface.
267165974Srwatson	 */
268165974Srwatson	aa = NULL;
269165974Srwatson	if (ro->ro_rt && (ifp = ro->ro_rt->rt_ifp)) {
270194619Srwatson		AT_IFADDR_RLOCK();
271165974Srwatson		for (aa = at_ifaddr_list; aa != NULL; aa = aa->aa_next) {
272165974Srwatson			if (aa->aa_ifp == ifp)
273165974Srwatson				break;
274165974Srwatson		}
275194619Srwatson		AT_IFADDR_RUNLOCK();
27615885Sjulian	}
277165974Srwatson	if (aa == NULL)
278165974Srwatson		return (ENETUNREACH);
27915885Sjulian
280165974Srwatson	ddp->ddp_fsat = *sat;
281165974Srwatson	if (ddp->ddp_lsat.sat_port == ATADDR_ANYPORT)
282165974Srwatson		return (at_pcbsetaddr(ddp, NULL, td));
283165974Srwatson	return (0);
28415885Sjulian}
28515885Sjulian
286127195Srwatsonvoid
287127288Srwatsonat_pcbdisconnect(struct ddpcb	*ddp)
28815885Sjulian{
289132043Srwatson
290165974Srwatson	DDP_LOCK_ASSERT(ddp);
291132043Srwatson
292165974Srwatson	ddp->ddp_fsat.sat_addr.s_net = ATADDR_ANYNET;
293165974Srwatson	ddp->ddp_fsat.sat_addr.s_node = ATADDR_ANYNODE;
294165974Srwatson	ddp->ddp_fsat.sat_port = ATADDR_ANYPORT;
29515885Sjulian}
29615885Sjulian
297127195Srwatsonint
298127288Srwatsonat_pcballoc(struct socket *so)
29915885Sjulian{
300165974Srwatson	struct ddpcb *ddp;
30115885Sjulian
302132043Srwatson	DDP_LIST_XLOCK_ASSERT();
303132043Srwatson
304184205Sdes	ddp = malloc(sizeof *ddp, M_PCB, M_NOWAIT | M_ZERO);
305139597Srwatson	if (ddp == NULL)
306139597Srwatson		return (ENOBUFS);
307132043Srwatson	DDP_LOCK_INIT(ddp);
30828270Swollman	ddp->ddp_lsat.sat_port = ATADDR_ANYPORT;
30915885Sjulian
310132043Srwatson	ddp->ddp_socket = so;
311132043Srwatson	so->so_pcb = (caddr_t)ddp;
312132043Srwatson
313127293Srwatson	ddp->ddp_next = ddpcb_list;
31428270Swollman	ddp->ddp_prev = NULL;
31528270Swollman	ddp->ddp_pprev = NULL;
31628270Swollman	ddp->ddp_pnext = NULL;
317165974Srwatson	if (ddpcb_list != NULL)
318127293Srwatson		ddpcb_list->ddp_prev = ddp;
319127293Srwatson	ddpcb_list = ddp;
320132043Srwatson	return(0);
32115885Sjulian}
32215885Sjulian
323127195Srwatsonvoid
324127288Srwatsonat_pcbdetach(struct socket *so, struct ddpcb *ddp)
32515885Sjulian{
326132043Srwatson
327165974Srwatson	/*
328165974Srwatson	 * We modify ddp, ddp_ports, and the global list.
329165974Srwatson	 */
330165974Srwatson	DDP_LIST_XLOCK_ASSERT();
331165974Srwatson	DDP_LOCK_ASSERT(ddp);
332165974Srwatson	KASSERT(so->so_pcb != NULL, ("at_pcbdetach: so_pcb == NULL"));
333132043Srwatson
334165974Srwatson	so->so_pcb = NULL;
33515885Sjulian
336165974Srwatson	/* Remove ddp from ddp_ports list. */
337165974Srwatson	if (ddp->ddp_lsat.sat_port != ATADDR_ANYPORT &&
338165974Srwatson	    ddp_ports[ddp->ddp_lsat.sat_port - 1] != NULL) {
339165974Srwatson		if (ddp->ddp_pprev != NULL)
340165974Srwatson			ddp->ddp_pprev->ddp_pnext = ddp->ddp_pnext;
341165974Srwatson		else
342165974Srwatson			ddp_ports[ddp->ddp_lsat.sat_port - 1] = ddp->ddp_pnext;
343165974Srwatson		if (ddp->ddp_pnext != NULL)
344165974Srwatson			ddp->ddp_pnext->ddp_pprev = ddp->ddp_pprev;
34515885Sjulian	}
34615885Sjulian
347165974Srwatson	if (ddp->ddp_route.ro_rt)
348165974Srwatson		RTFREE(ddp->ddp_route.ro_rt);
34915885Sjulian
350165974Srwatson	if (ddp->ddp_prev)
351165974Srwatson		ddp->ddp_prev->ddp_next = ddp->ddp_next;
352165974Srwatson	else
353165974Srwatson		ddpcb_list = ddp->ddp_next;
354165974Srwatson	if (ddp->ddp_next)
355165974Srwatson		ddp->ddp_next->ddp_prev = ddp->ddp_prev;
356165974Srwatson	DDP_UNLOCK(ddp);
357165974Srwatson	DDP_LOCK_DESTROY(ddp);
358184205Sdes	free(ddp, M_PCB);
35915885Sjulian}
36015885Sjulian
36115885Sjulian/*
362165974Srwatson * For the moment, this just find the pcb with the correct local address.  In
363165974Srwatson * the future, this will actually do some real searching, so we can use the
364165974Srwatson * sender's address to do de-multiplexing on a single port to many sockets
365165974Srwatson * (pcbs).
36615885Sjulian */
36715885Sjulianstruct ddpcb *
368127288Srwatsonddp_search(struct sockaddr_at *from, struct sockaddr_at *to,
369165974Srwatson    struct at_ifaddr *aa)
37015885Sjulian{
371165974Srwatson	struct ddpcb *ddp;
37215885Sjulian
373165974Srwatson	DDP_LIST_SLOCK_ASSERT();
374132043Srwatson
375165974Srwatson	/*
376165974Srwatson	 * Check for bad ports.
377165974Srwatson	 */
378165974Srwatson	if (to->sat_port < ATPORT_FIRST || to->sat_port >= ATPORT_LAST)
379165974Srwatson		return (NULL);
38015885Sjulian
381165974Srwatson	/*
382165974Srwatson	 * Make sure the local address matches the sent address.  What about
383165974Srwatson	 * the interface?
384165974Srwatson	 */
385165974Srwatson	for (ddp = ddp_ports[to->sat_port - 1]; ddp; ddp = ddp->ddp_pnext) {
386165974Srwatson		DDP_LOCK(ddp);
387165974Srwatson		/* XXX should we handle 0.YY? */
388165974Srwatson		/* XXXX.YY to socket on destination interface */
389165974Srwatson		if (to->sat_addr.s_net == ddp->ddp_lsat.sat_addr.s_net &&
390165974Srwatson		    to->sat_addr.s_node == ddp->ddp_lsat.sat_addr.s_node) {
391165974Srwatson			DDP_UNLOCK(ddp);
392165974Srwatson			break;
393165974Srwatson		}
39415885Sjulian
395165974Srwatson		/* 0.255 to socket on receiving interface */
396165974Srwatson		if (to->sat_addr.s_node == ATADDR_BCAST &&
397165974Srwatson		    (to->sat_addr.s_net == 0 ||
398165974Srwatson		    to->sat_addr.s_net == ddp->ddp_lsat.sat_addr.s_net) &&
399165974Srwatson		    ddp->ddp_lsat.sat_addr.s_net ==
400165974Srwatson		    AA_SAT(aa)->sat_addr.s_net) {
401165974Srwatson			DDP_UNLOCK(ddp);
402165974Srwatson			break;
403165974Srwatson		}
40415885Sjulian
405165974Srwatson		/* XXXX.0 to socket on destination interface */
406165974Srwatson		if (to->sat_addr.s_net == aa->aa_firstnet &&
407165974Srwatson		    to->sat_addr.s_node == 0 &&
408165974Srwatson		    ntohs(ddp->ddp_lsat.sat_addr.s_net) >=
409165974Srwatson		    ntohs(aa->aa_firstnet) &&
410165974Srwatson		    ntohs(ddp->ddp_lsat.sat_addr.s_net) <=
411165974Srwatson		    ntohs(aa->aa_lastnet)) {
412165974Srwatson			DDP_UNLOCK(ddp);
413165974Srwatson			break;
414165974Srwatson		}
415165974Srwatson		DDP_UNLOCK(ddp);
41615885Sjulian	}
417165974Srwatson	return (ddp);
41815885Sjulian}
419