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$
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();
116194913Srwatson			TAILQ_FOREACH(aa, &at_ifaddrhead, aa_link) {
117165974Srwatson				if ((sat->sat_addr.s_net ==
118165974Srwatson				    AA_SAT(aa)->sat_addr.s_net) &&
119165974Srwatson				    (sat->sat_addr.s_node ==
120165974Srwatson				    AA_SAT(aa)->sat_addr.s_node))
121165974Srwatson					break;
122165974Srwatson			}
123194619Srwatson			AT_IFADDR_RUNLOCK();
124165974Srwatson			if (aa == NULL)
125165974Srwatson				return (EADDRNOTAVAIL);
12615885Sjulian		}
12715885Sjulian
128165974Srwatson		if (sat->sat_port != ATADDR_ANYPORT) {
129165974Srwatson			if (sat->sat_port < ATPORT_FIRST ||
130165974Srwatson			    sat->sat_port >= ATPORT_LAST)
131165974Srwatson				return (EINVAL);
132165974Srwatson			if (sat->sat_port < ATPORT_RESERVED &&
133165974Srwatson			    priv_check(td, PRIV_NETATALK_RESERVEDPORT))
134165974Srwatson				return (EACCES);
135165974Srwatson		}
136165974Srwatson	} else {
137165974Srwatson		bzero((caddr_t)&lsat, sizeof(struct sockaddr_at));
138165974Srwatson		lsat.sat_len = sizeof(struct sockaddr_at);
139165974Srwatson		lsat.sat_addr.s_node = ATADDR_ANYNODE;
140165974Srwatson		lsat.sat_addr.s_net = ATADDR_ANYNET;
141165974Srwatson		lsat.sat_family = AF_APPLETALK;
142165974Srwatson		sat = &lsat;
14315885Sjulian	}
14415885Sjulian
145165974Srwatson	if (sat->sat_addr.s_node == ATADDR_ANYNODE &&
146127288Srwatson	    sat->sat_addr.s_net == ATADDR_ANYNET) {
147194619Srwatson		AT_IFADDR_RLOCK();
148194913Srwatson		if (TAILQ_EMPTY(&at_ifaddrhead)) {
149194619Srwatson			AT_IFADDR_RUNLOCK();
150165974Srwatson			return (EADDRNOTAVAIL);
151194619Srwatson		}
152194913Srwatson		sat->sat_addr = AA_SAT(TAILQ_FIRST(&at_ifaddrhead))->sat_addr;
153194619Srwatson		AT_IFADDR_RUNLOCK();
15415885Sjulian	}
155165974Srwatson	ddp->ddp_lsat = *sat;
15615885Sjulian
157165974Srwatson	/*
158165974Srwatson	 * Choose port.
159165974Srwatson	 */
160165974Srwatson	if (sat->sat_port == ATADDR_ANYPORT) {
161165974Srwatson		for (sat->sat_port = ATPORT_RESERVED;
162165974Srwatson		    sat->sat_port < ATPORT_LAST; sat->sat_port++) {
163165974Srwatson			if (ddp_ports[sat->sat_port - 1] == NULL)
164165974Srwatson				break;
165165974Srwatson		}
166165974Srwatson		if (sat->sat_port == ATPORT_LAST)
167165974Srwatson			return (EADDRNOTAVAIL);
168165974Srwatson		ddp->ddp_lsat.sat_port = sat->sat_port;
169165974Srwatson		ddp_ports[sat->sat_port - 1] = ddp;
170165974Srwatson	} else {
171165974Srwatson		for (ddpp = ddp_ports[sat->sat_port - 1]; ddpp;
172165974Srwatson		    ddpp = ddpp->ddp_pnext) {
173165974Srwatson			if (ddpp->ddp_lsat.sat_addr.s_net ==
174165974Srwatson			    sat->sat_addr.s_net &&
175165974Srwatson			    ddpp->ddp_lsat.sat_addr.s_node ==
176165974Srwatson			    sat->sat_addr.s_node)
177165974Srwatson				break;
178165974Srwatson		}
179165974Srwatson		if (ddpp != NULL)
180165974Srwatson			return (EADDRINUSE);
181165974Srwatson		ddp->ddp_pnext = ddp_ports[sat->sat_port - 1];
182165974Srwatson		ddp_ports[sat->sat_port - 1] = ddp;
183165974Srwatson		if (ddp->ddp_pnext != NULL)
184165974Srwatson			ddp->ddp_pnext->ddp_pprev = ddp;
18515885Sjulian	}
18615885Sjulian
187165974Srwatson	return (0);
18815885Sjulian}
18915885Sjulian
190127195Srwatsonint
19183366Sjulianat_pcbconnect(struct ddpcb *ddp, struct sockaddr *addr, struct thread *td)
19215885Sjulian{
193165974Srwatson	struct sockaddr_at	*sat = (struct sockaddr_at *)addr;
194165974Srwatson	struct route	*ro;
195165974Srwatson	struct at_ifaddr	*aa = NULL;
196165974Srwatson	struct ifnet	*ifp;
197165974Srwatson	u_short		hintnet = 0, net;
19815885Sjulian
199165974Srwatson	DDP_LIST_XLOCK_ASSERT();
200165974Srwatson	DDP_LOCK_ASSERT(ddp);
201132043Srwatson
202165974Srwatson	if (sat->sat_family != AF_APPLETALK)
203165974Srwatson		return (EAFNOSUPPORT);
20415885Sjulian
205165974Srwatson	/*
206165974Srwatson	 * Under phase 2, network 0 means "the network".  We take "the
207165974Srwatson	 * network" to mean the network the control block is bound to.  If
208165974Srwatson	 * the control block is not bound, there is an error.
209165974Srwatson	 */
210165974Srwatson	if (sat->sat_addr.s_net == ATADDR_ANYNET &&
211165974Srwatson	    sat->sat_addr.s_node != ATADDR_ANYNODE) {
212165974Srwatson		if (ddp->ddp_lsat.sat_port == ATADDR_ANYPORT)
213165974Srwatson			return (EADDRNOTAVAIL);
214165974Srwatson		hintnet = ddp->ddp_lsat.sat_addr.s_net;
21515885Sjulian	}
21615885Sjulian
217165974Srwatson	ro = &ddp->ddp_route;
218165974Srwatson	/*
219165974Srwatson	 * If we've got an old route for this pcb, check that it is valid.
220165974Srwatson	 * If we've changed our address, we may have an old "good looking"
221165974Srwatson	 * route here.  Attempt to detect it.
222165974Srwatson	 */
223165974Srwatson	if (ro->ro_rt) {
224165974Srwatson		if (hintnet)
225165974Srwatson			net = hintnet;
226165974Srwatson		else
227165974Srwatson			net = sat->sat_addr.s_net;
228165974Srwatson		aa = NULL;
229194619Srwatson		AT_IFADDR_RLOCK();
230165974Srwatson		if ((ifp = ro->ro_rt->rt_ifp) != NULL) {
231194913Srwatson			TAILQ_FOREACH(aa, &at_ifaddrhead, aa_link) {
232165974Srwatson				if (aa->aa_ifp == ifp &&
233165974Srwatson				    ntohs(net) >= ntohs(aa->aa_firstnet) &&
234165974Srwatson				    ntohs(net) <= ntohs(aa->aa_lastnet))
235165974Srwatson					break;
236165974Srwatson			}
23715885Sjulian		}
238165974Srwatson		if (aa == NULL || (satosat(&ro->ro_dst)->sat_addr.s_net !=
239165974Srwatson		    (hintnet ? hintnet : sat->sat_addr.s_net) ||
240165974Srwatson		    satosat(&ro->ro_dst)->sat_addr.s_node !=
241165974Srwatson		    sat->sat_addr.s_node)) {
242165974Srwatson			RTFREE(ro->ro_rt);
243165974Srwatson			ro->ro_rt = NULL;
244165974Srwatson		}
245194619Srwatson		AT_IFADDR_RUNLOCK();
24615885Sjulian	}
24715885Sjulian
248165974Srwatson	/*
249165974Srwatson	 * If we've got no route for this interface, try to find one.
250165974Srwatson	 */
251165974Srwatson	if (ro->ro_rt == NULL || ro->ro_rt->rt_ifp == NULL) {
252165974Srwatson		ro->ro_dst.sa_len = sizeof(struct sockaddr_at);
253165974Srwatson		ro->ro_dst.sa_family = AF_APPLETALK;
254165974Srwatson		if (hintnet)
255165974Srwatson			satosat(&ro->ro_dst)->sat_addr.s_net = hintnet;
256165974Srwatson		else
257165974Srwatson			satosat(&ro->ro_dst)->sat_addr.s_net =
258165974Srwatson			    sat->sat_addr.s_net;
259165974Srwatson		satosat(&ro->ro_dst)->sat_addr.s_node = sat->sat_addr.s_node;
260165974Srwatson		rtalloc(ro);
26115885Sjulian	}
26215885Sjulian
263165974Srwatson	/*
264165974Srwatson	 * Make sure any route that we have has a valid interface.
265165974Srwatson	 */
266165974Srwatson	aa = NULL;
267165974Srwatson	if (ro->ro_rt && (ifp = ro->ro_rt->rt_ifp)) {
268194619Srwatson		AT_IFADDR_RLOCK();
269194913Srwatson		TAILQ_FOREACH(aa, &at_ifaddrhead, aa_link) {
270165974Srwatson			if (aa->aa_ifp == ifp)
271165974Srwatson				break;
272165974Srwatson		}
273194619Srwatson		AT_IFADDR_RUNLOCK();
27415885Sjulian	}
275165974Srwatson	if (aa == NULL)
276165974Srwatson		return (ENETUNREACH);
27715885Sjulian
278165974Srwatson	ddp->ddp_fsat = *sat;
279165974Srwatson	if (ddp->ddp_lsat.sat_port == ATADDR_ANYPORT)
280165974Srwatson		return (at_pcbsetaddr(ddp, NULL, td));
281165974Srwatson	return (0);
28215885Sjulian}
28315885Sjulian
284127195Srwatsonvoid
285127288Srwatsonat_pcbdisconnect(struct ddpcb	*ddp)
28615885Sjulian{
287132043Srwatson
288165974Srwatson	DDP_LOCK_ASSERT(ddp);
289132043Srwatson
290165974Srwatson	ddp->ddp_fsat.sat_addr.s_net = ATADDR_ANYNET;
291165974Srwatson	ddp->ddp_fsat.sat_addr.s_node = ATADDR_ANYNODE;
292165974Srwatson	ddp->ddp_fsat.sat_port = ATADDR_ANYPORT;
29315885Sjulian}
29415885Sjulian
295127195Srwatsonint
296127288Srwatsonat_pcballoc(struct socket *so)
29715885Sjulian{
298165974Srwatson	struct ddpcb *ddp;
29915885Sjulian
300132043Srwatson	DDP_LIST_XLOCK_ASSERT();
301132043Srwatson
302184205Sdes	ddp = malloc(sizeof *ddp, M_PCB, M_NOWAIT | M_ZERO);
303139597Srwatson	if (ddp == NULL)
304139597Srwatson		return (ENOBUFS);
305132043Srwatson	DDP_LOCK_INIT(ddp);
30628270Swollman	ddp->ddp_lsat.sat_port = ATADDR_ANYPORT;
30715885Sjulian
308132043Srwatson	ddp->ddp_socket = so;
309132043Srwatson	so->so_pcb = (caddr_t)ddp;
310132043Srwatson
311127293Srwatson	ddp->ddp_next = ddpcb_list;
31228270Swollman	ddp->ddp_prev = NULL;
31328270Swollman	ddp->ddp_pprev = NULL;
31428270Swollman	ddp->ddp_pnext = NULL;
315165974Srwatson	if (ddpcb_list != NULL)
316127293Srwatson		ddpcb_list->ddp_prev = ddp;
317127293Srwatson	ddpcb_list = ddp;
318132043Srwatson	return(0);
31915885Sjulian}
32015885Sjulian
321127195Srwatsonvoid
322127288Srwatsonat_pcbdetach(struct socket *so, struct ddpcb *ddp)
32315885Sjulian{
324132043Srwatson
325165974Srwatson	/*
326165974Srwatson	 * We modify ddp, ddp_ports, and the global list.
327165974Srwatson	 */
328165974Srwatson	DDP_LIST_XLOCK_ASSERT();
329165974Srwatson	DDP_LOCK_ASSERT(ddp);
330165974Srwatson	KASSERT(so->so_pcb != NULL, ("at_pcbdetach: so_pcb == NULL"));
331132043Srwatson
332165974Srwatson	so->so_pcb = NULL;
33315885Sjulian
334165974Srwatson	/* Remove ddp from ddp_ports list. */
335165974Srwatson	if (ddp->ddp_lsat.sat_port != ATADDR_ANYPORT &&
336165974Srwatson	    ddp_ports[ddp->ddp_lsat.sat_port - 1] != NULL) {
337165974Srwatson		if (ddp->ddp_pprev != NULL)
338165974Srwatson			ddp->ddp_pprev->ddp_pnext = ddp->ddp_pnext;
339165974Srwatson		else
340165974Srwatson			ddp_ports[ddp->ddp_lsat.sat_port - 1] = ddp->ddp_pnext;
341165974Srwatson		if (ddp->ddp_pnext != NULL)
342165974Srwatson			ddp->ddp_pnext->ddp_pprev = ddp->ddp_pprev;
34315885Sjulian	}
34415885Sjulian
345165974Srwatson	if (ddp->ddp_route.ro_rt)
346165974Srwatson		RTFREE(ddp->ddp_route.ro_rt);
34715885Sjulian
348165974Srwatson	if (ddp->ddp_prev)
349165974Srwatson		ddp->ddp_prev->ddp_next = ddp->ddp_next;
350165974Srwatson	else
351165974Srwatson		ddpcb_list = ddp->ddp_next;
352165974Srwatson	if (ddp->ddp_next)
353165974Srwatson		ddp->ddp_next->ddp_prev = ddp->ddp_prev;
354165974Srwatson	DDP_UNLOCK(ddp);
355165974Srwatson	DDP_LOCK_DESTROY(ddp);
356184205Sdes	free(ddp, M_PCB);
35715885Sjulian}
35815885Sjulian
35915885Sjulian/*
360165974Srwatson * For the moment, this just find the pcb with the correct local address.  In
361165974Srwatson * the future, this will actually do some real searching, so we can use the
362165974Srwatson * sender's address to do de-multiplexing on a single port to many sockets
363165974Srwatson * (pcbs).
36415885Sjulian */
36515885Sjulianstruct ddpcb *
366127288Srwatsonddp_search(struct sockaddr_at *from, struct sockaddr_at *to,
367165974Srwatson    struct at_ifaddr *aa)
36815885Sjulian{
369165974Srwatson	struct ddpcb *ddp;
37015885Sjulian
371165974Srwatson	DDP_LIST_SLOCK_ASSERT();
372132043Srwatson
373165974Srwatson	/*
374165974Srwatson	 * Check for bad ports.
375165974Srwatson	 */
376165974Srwatson	if (to->sat_port < ATPORT_FIRST || to->sat_port >= ATPORT_LAST)
377165974Srwatson		return (NULL);
37815885Sjulian
379165974Srwatson	/*
380165974Srwatson	 * Make sure the local address matches the sent address.  What about
381165974Srwatson	 * the interface?
382165974Srwatson	 */
383165974Srwatson	for (ddp = ddp_ports[to->sat_port - 1]; ddp; ddp = ddp->ddp_pnext) {
384165974Srwatson		DDP_LOCK(ddp);
385165974Srwatson		/* XXX should we handle 0.YY? */
386165974Srwatson		/* XXXX.YY to socket on destination interface */
387165974Srwatson		if (to->sat_addr.s_net == ddp->ddp_lsat.sat_addr.s_net &&
388165974Srwatson		    to->sat_addr.s_node == ddp->ddp_lsat.sat_addr.s_node) {
389165974Srwatson			DDP_UNLOCK(ddp);
390165974Srwatson			break;
391165974Srwatson		}
39215885Sjulian
393165974Srwatson		/* 0.255 to socket on receiving interface */
394165974Srwatson		if (to->sat_addr.s_node == ATADDR_BCAST &&
395165974Srwatson		    (to->sat_addr.s_net == 0 ||
396165974Srwatson		    to->sat_addr.s_net == ddp->ddp_lsat.sat_addr.s_net) &&
397165974Srwatson		    ddp->ddp_lsat.sat_addr.s_net ==
398165974Srwatson		    AA_SAT(aa)->sat_addr.s_net) {
399165974Srwatson			DDP_UNLOCK(ddp);
400165974Srwatson			break;
401165974Srwatson		}
40215885Sjulian
403165974Srwatson		/* XXXX.0 to socket on destination interface */
404165974Srwatson		if (to->sat_addr.s_net == aa->aa_firstnet &&
405165974Srwatson		    to->sat_addr.s_node == 0 &&
406165974Srwatson		    ntohs(ddp->ddp_lsat.sat_addr.s_net) >=
407165974Srwatson		    ntohs(aa->aa_firstnet) &&
408165974Srwatson		    ntohs(ddp->ddp_lsat.sat_addr.s_net) <=
409165974Srwatson		    ntohs(aa->aa_lastnet)) {
410165974Srwatson			DDP_UNLOCK(ddp);
411165974Srwatson			break;
412165974Srwatson		}
413165974Srwatson		DDP_UNLOCK(ddp);
41415885Sjulian	}
415165974Srwatson	return (ddp);
41615885Sjulian}
417