1139827Simp/*-
215885Sjulian * Copyright (c) 1990,1991 Regents of The University of Michigan.
3194619Srwatson * Copyright (c) 2009 Robert N. M. Watson
415885Sjulian * All Rights Reserved.
567893Sphk *
6139827Simp * Permission to use, copy, modify, and distribute this software and
7139827Simp * its documentation for any purpose and without fee is hereby granted,
8139827Simp * provided that the above copyright notice appears in all copies and
9139827Simp * that both that copyright notice and this permission notice appear
10139827Simp * in supporting documentation, and that the name of The University
11139827Simp * of Michigan not be used in advertising or publicity pertaining to
12139827Simp * distribution of the software without specific, written prior
13139827Simp * permission. This software is supplied as is without expressed or
14139827Simp * implied warranties of any kind.
15139827Simp *
16139827Simp * This product includes software developed by the University of
17139827Simp * California, Berkeley and its contributors.
18139827Simp *
19139827Simp *	Research Systems Unix Group
20139827Simp *	The University of Michigan
21139827Simp *	c/o Wesley Craig
22139827Simp *	535 W. William Street
23139827Simp *	Ann Arbor, Michigan
24139827Simp *	+1-313-764-2278
25139827Simp *	netatalk@umich.edu
2615885Sjulian */
2715885Sjulian
28194619Srwatson#include <sys/cdefs.h>
29194619Srwatson__FBSDID("$FreeBSD$");
30194619Srwatson
3115885Sjulian#include <sys/param.h>
3215885Sjulian#include <sys/systm.h>
3324204Sbde#include <sys/sockio.h>
34194619Srwatson#include <sys/lock.h>
3529024Sbde#include <sys/malloc.h>
3615885Sjulian#include <sys/kernel.h>
37166833Srwatson#include <sys/priv.h>
38194619Srwatson#include <sys/rwlock.h>
3915885Sjulian#include <sys/socket.h>
4015885Sjulian#include <net/if.h>
4115885Sjulian#include <net/route.h>
4215885Sjulian#include <netinet/in.h>
4315885Sjulian#undef s_net
4415885Sjulian#include <netinet/if_ether.h>
4515885Sjulian
4618207Sbde#include <netatalk/at.h>
4718207Sbde#include <netatalk/at_var.h>
4815885Sjulian#include <netatalk/at_extern.h>
4915885Sjulian
50194619Srwatsonstruct rwlock		 at_ifaddr_rw;
51194913Srwatsonstruct at_ifaddrhead	 at_ifaddrhead;
5229182Sbde
53194619SrwatsonRW_SYSINIT(at_ifaddr_rw, &at_ifaddr_rw, "at_ifaddr_rw");
54194619Srwatson
55132411Srwatsonstatic int aa_dorangeroute(struct ifaddr *ifa, u_int first, u_int last,
56132411Srwatson	    int cmd);
57132411Srwatsonstatic int aa_addsingleroute(struct ifaddr *ifa, struct at_addr *addr,
58132411Srwatson	    struct at_addr *mask);
59132411Srwatsonstatic int aa_delsingleroute(struct ifaddr *ifa, struct at_addr *addr,
60132411Srwatson	    struct at_addr *mask);
6117254Sjulianstatic int aa_dosingleroute(struct ifaddr *ifa, struct at_addr *addr,
62132411Srwatson	    struct at_addr *mask, int cmd, int flags);
63127288Srwatsonstatic int at_scrub(struct ifnet *ifp, struct at_ifaddr *aa);
64127288Srwatsonstatic int at_ifinit(struct ifnet *ifp, struct at_ifaddr *aa,
65132411Srwatson	    struct sockaddr_at *sat);
6628845Sjulianstatic int aa_claim_addr(struct ifaddr *ifa, struct sockaddr *gw);
6715885Sjulian
68132411Srwatson#define	sateqaddr(a,b)							\
69132411Srwatson	((a)->sat_len == (b)->sat_len &&				\
70132411Srwatson	(a)->sat_family == (b)->sat_family &&				\
71132411Srwatson	(a)->sat_addr.s_net == (b)->sat_addr.s_net &&			\
72132411Srwatson	(a)->sat_addr.s_node == (b)->sat_addr.s_node)
7315885Sjulian
7415885Sjulianint
75132410Srwatsonat_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp,
76132410Srwatson    struct thread *td)
7715885Sjulian{
78132410Srwatson	struct ifreq *ifr = (struct ifreq *)data;
79132410Srwatson	struct sockaddr_at *sat;
80132410Srwatson	struct netrange	*nr;
81132410Srwatson	struct at_aliasreq *ifra = (struct at_aliasreq *)data;
82194819Srwatson	struct at_ifaddr *aa;
83194913Srwatson	struct ifaddr *ifa;
84194619Srwatson	int error;
8515885Sjulian
8617921Sjulian	/*
87132410Srwatson	 * If we have an ifp, then find the matching at_ifaddr if it exists
8817921Sjulian	 */
89194819Srwatson	aa = NULL;
90194819Srwatson	AT_IFADDR_RLOCK();
91132410Srwatson	if (ifp != NULL) {
92194913Srwatson		TAILQ_FOREACH(aa, &at_ifaddrhead, aa_link) {
93132410Srwatson			if (aa->aa_ifp == ifp)
94132410Srwatson				break;
9515885Sjulian		}
9615885Sjulian	}
97194819Srwatson	if (aa != NULL)
98194819Srwatson		ifa_ref(&aa->aa_ifa);
99194819Srwatson	AT_IFADDR_RUNLOCK();
100132410Srwatson
10117921Sjulian	/*
102132410Srwatson	 * In this first switch table we are basically getting ready for
103132410Srwatson	 * the second one, by getting the atalk-specific things set up
104132410Srwatson	 * so that they start to look more similar to other protocols etc.
10517921Sjulian	 */
106194819Srwatson	error = 0;
107132410Srwatson	switch (cmd) {
108132410Srwatson	case SIOCAIFADDR:
109132410Srwatson	case SIOCDIFADDR:
110132410Srwatson		/*
111132410Srwatson		 * If we have an appletalk sockaddr, scan forward of where we
112132410Srwatson		 * are now on the at_ifaddr list to find one with a matching
113132410Srwatson		 * address on this interface.  This may leave aa pointing to
114132410Srwatson		 * the first address on the NEXT interface!
115132410Srwatson		 */
116132410Srwatson		if (ifra->ifra_addr.sat_family == AF_APPLETALK) {
117194819Srwatson			struct at_ifaddr *oaa;
118194819Srwatson
119194819Srwatson			AT_IFADDR_RLOCK();
120194913Srwatson			for (oaa = aa; aa; aa = TAILQ_NEXT(aa, aa_link)) {
121132410Srwatson				if (aa->aa_ifp == ifp &&
122132410Srwatson				    sateqaddr(&aa->aa_addr, &ifra->ifra_addr))
123132410Srwatson					break;
124132410Srwatson			}
125194819Srwatson			if (oaa != NULL && oaa != aa)
126194819Srwatson				ifa_free(&oaa->aa_ifa);
127194819Srwatson			if (aa != NULL && oaa != aa)
128194819Srwatson				ifa_ref(&aa->aa_ifa);
129194819Srwatson			AT_IFADDR_RUNLOCK();
130132410Srwatson		}
131132410Srwatson		/*
132132410Srwatson		 * If we a retrying to delete an addres but didn't find such,
133132410Srwatson		 * then rewurn with an error
134132410Srwatson		 */
135194619Srwatson		if (cmd == SIOCDIFADDR && aa == NULL) {
136194819Srwatson			error = EADDRNOTAVAIL;
137194819Srwatson			goto out;
138194619Srwatson		}
139132410Srwatson		/*FALLTHROUGH*/
14015885Sjulian
141132410Srwatson	case SIOCSIFADDR:
142132410Srwatson		/*
143132410Srwatson		 * If we are not superuser, then we don't get to do these ops.
144164033Srwatson		 *
145164033Srwatson		 * XXXRW: Layering?
146132410Srwatson		 */
147194619Srwatson		if (priv_check(td, PRIV_NET_ADDIFADDR)) {
148194819Srwatson			error = EPERM;
149194819Srwatson			goto out;
150194619Srwatson		}
151132410Srwatson
152132410Srwatson		sat = satosat(&ifr->ifr_addr);
153132410Srwatson		nr = (struct netrange *)sat->sat_zero;
154132410Srwatson		if (nr->nr_phase == 1) {
155194819Srwatson			struct at_ifaddr *oaa;
156194819Srwatson
157132410Srwatson			/*
158132410Srwatson			 * Look for a phase 1 address on this interface.
159132410Srwatson			 * This may leave aa pointing to the first address on
160132410Srwatson			 * the NEXT interface!
161132410Srwatson			 */
162194819Srwatson			AT_IFADDR_RLOCK();
163194913Srwatson			for (oaa = aa; aa; aa = TAILQ_NEXT(aa, aa_link)) {
164132410Srwatson				if (aa->aa_ifp == ifp &&
165132410Srwatson				    (aa->aa_flags & AFA_PHASE2) == 0)
166132410Srwatson					break;
167132410Srwatson			}
168194819Srwatson			if (oaa != NULL && oaa != aa)
169194819Srwatson				ifa_free(&oaa->aa_ifa);
170194819Srwatson			if (aa != NULL && oaa != aa)
171194819Srwatson				ifa_ref(&aa->aa_ifa);
172194819Srwatson			AT_IFADDR_RUNLOCK();
173132410Srwatson		} else {		/* default to phase 2 */
174194819Srwatson			struct at_ifaddr *oaa;
175194819Srwatson
176132410Srwatson			/*
177132410Srwatson			 * Look for a phase 2 address on this interface.
178132410Srwatson			 * This may leave aa pointing to the first address on
179132410Srwatson			 * the NEXT interface!
180132410Srwatson			 */
181194819Srwatson			AT_IFADDR_RLOCK();
182194913Srwatson			for (oaa = aa; aa; aa = TAILQ_NEXT(aa, aa_link)) {
183132410Srwatson				if (aa->aa_ifp == ifp && (aa->aa_flags &
184132410Srwatson				    AFA_PHASE2))
185132410Srwatson					break;
186132410Srwatson			}
187194819Srwatson			if (oaa != NULL && oaa != aa)
188194819Srwatson				ifa_free(&oaa->aa_ifa);
189194819Srwatson			if (aa != NULL && oaa != aa)
190194819Srwatson				ifa_ref(&aa->aa_ifa);
191194819Srwatson			AT_IFADDR_RUNLOCK();
19215885Sjulian		}
19315885Sjulian
194132410Srwatson		if (ifp == NULL)
195132410Srwatson			panic("at_control");
19615885Sjulian
19715885Sjulian		/*
198132410Srwatson		 * If we failed to find an existing at_ifaddr entry, then we
199132410Srwatson		 * allocate a fresh one.
20015885Sjulian		 */
201132410Srwatson		if (aa == NULL) {
202194819Srwatson			aa = malloc(sizeof(struct at_ifaddr), M_IFADDR,
203194619Srwatson			    M_NOWAIT | M_ZERO);
204194819Srwatson			if (aa == NULL) {
205194819Srwatson				error = ENOBUFS;
206194819Srwatson				goto out;
207194619Srwatson			}
208194819Srwatson			callout_init(&aa->aa_callout, CALLOUT_MPSAFE);
20915885Sjulian
210132410Srwatson			ifa = (struct ifaddr *)aa;
211194602Srwatson			ifa_init(ifa);
21220407Swollman
213132410Srwatson			/*
214132410Srwatson			 * As the at_ifaddr contains the actual sockaddrs,
215194819Srwatson			 * and the ifaddr itself, link them all together
216132410Srwatson			 * correctly.
217132410Srwatson			 */
218132410Srwatson			ifa->ifa_addr = (struct sockaddr *)&aa->aa_addr;
219132410Srwatson			ifa->ifa_dstaddr = (struct sockaddr *)&aa->aa_addr;
220132410Srwatson			ifa->ifa_netmask = (struct sockaddr *)&aa->aa_netmask;
22115885Sjulian
222132410Srwatson			/*
223132410Srwatson			 * Set/clear the phase 2 bit.
224132410Srwatson			 */
225132410Srwatson			if (nr->nr_phase == 1)
226132410Srwatson				aa->aa_flags &= ~AFA_PHASE2;
227132410Srwatson			else
228132410Srwatson				aa->aa_flags |= AFA_PHASE2;
22917921Sjulian
230194913Srwatson			ifa_ref(&aa->aa_ifa);		/* at_ifaddrhead */
231194819Srwatson			AT_IFADDR_WLOCK();
232194913Srwatson			if (!TAILQ_EMPTY(&at_ifaddrhead)) {
233194819Srwatson				/*
234194819Srwatson				 * Don't let the loopback be first, since the
235194819Srwatson				 * first address is the machine's default
236194819Srwatson				 * address for binding.  If it is, stick
237194819Srwatson				 * ourself in front, otherwise go to the back
238194819Srwatson				 * of the list.
239194819Srwatson				 */
240194913Srwatson				if (TAILQ_FIRST(&at_ifaddrhead)->aa_ifp->
241194913Srwatson				    if_flags & IFF_LOOPBACK)
242194913Srwatson					TAILQ_INSERT_HEAD(&at_ifaddrhead, aa,
243194913Srwatson					    aa_link);
244194913Srwatson				else
245194913Srwatson					TAILQ_INSERT_TAIL(&at_ifaddrhead, aa,
246194913Srwatson					    aa_link);
247194819Srwatson			} else
248194913Srwatson				TAILQ_INSERT_HEAD(&at_ifaddrhead, aa,
249194913Srwatson				    aa_link);
250194819Srwatson			AT_IFADDR_WUNLOCK();
251194819Srwatson
252132410Srwatson			/*
253132410Srwatson			 * and link it all together
254132410Srwatson			 */
255132410Srwatson			aa->aa_ifp = ifp;
256194819Srwatson			ifa_ref(&aa->aa_ifa);		/* if_addrhead */
257229621Sjhb			IF_ADDR_WLOCK(ifp);
258191281Srwatson			TAILQ_INSERT_TAIL(&ifp->if_addrhead, ifa, ifa_link);
259229621Sjhb			IF_ADDR_WUNLOCK(ifp);
260132410Srwatson		} else {
261132410Srwatson			/*
262132410Srwatson			 * If we DID find one then we clobber any routes
263132410Srwatson			 * dependent on it..
264132410Srwatson			 */
265132410Srwatson			at_scrub(ifp, aa);
266132410Srwatson		}
267132410Srwatson		break;
26815885Sjulian
269132410Srwatson	case SIOCGIFADDR :
270132410Srwatson		sat = satosat(&ifr->ifr_addr);
271132410Srwatson		nr = (struct netrange *)sat->sat_zero;
272132410Srwatson		if (nr->nr_phase == 1) {
273194819Srwatson			struct at_ifaddr *oaa;
274194819Srwatson
275132410Srwatson			/*
276132410Srwatson			 * If the request is specifying phase 1, then
277132410Srwatson			 * only look at a phase one address
278132410Srwatson			 */
279196121Srwatson			AT_IFADDR_RLOCK();
280194913Srwatson			for (oaa = aa; aa; aa = TAILQ_NEXT(aa, aa_link)) {
281132410Srwatson				if (aa->aa_ifp == ifp &&
282132410Srwatson				    (aa->aa_flags & AFA_PHASE2) == 0)
283132410Srwatson					break;
284132410Srwatson			}
285194819Srwatson			if (oaa != NULL && oaa != aa)
286194819Srwatson				ifa_free(&oaa->aa_ifa);
287194819Srwatson			if (aa != NULL && oaa != aa)
288194819Srwatson				ifa_ref(&aa->aa_ifa);
289196121Srwatson			AT_IFADDR_RUNLOCK();
290132410Srwatson		} else {
291194819Srwatson			struct at_ifaddr *oaa;
292194819Srwatson
293132410Srwatson			/*
294132410Srwatson			 * default to phase 2
295132410Srwatson			 */
296194819Srwatson			AT_IFADDR_RLOCK();
297194913Srwatson			for (oaa = aa; aa; aa = TAILQ_NEXT(aa, aa_link)) {
298132410Srwatson				if (aa->aa_ifp == ifp && (aa->aa_flags &
299132410Srwatson				    AFA_PHASE2))
300132410Srwatson					break;
301132410Srwatson			}
302194819Srwatson			if (oaa != NULL && oaa != aa)
303194819Srwatson				ifa_free(&oaa->aa_ifa);
304194819Srwatson			if (aa != NULL && oaa != aa)
305194819Srwatson				ifa_ref(&aa->aa_ifa);
306194819Srwatson			AT_IFADDR_RUNLOCK();
30715885Sjulian		}
308132410Srwatson
309194619Srwatson		if (aa == NULL) {
310194819Srwatson			error = EADDRNOTAVAIL;
311194819Srwatson			goto out;
312194619Srwatson		}
313132410Srwatson		break;
31415885Sjulian	}
31515885Sjulian
31617921Sjulian	/*
317132410Srwatson	 * By the time this switch is run we should be able to assume that
318132410Srwatson	 * the "aa" pointer is valid when needed.
31917921Sjulian	 */
320132410Srwatson	switch (cmd) {
321132410Srwatson	case SIOCGIFADDR:
32217921Sjulian
323132410Srwatson		/*
324132410Srwatson		 * copy the contents of the sockaddr blindly.
325132410Srwatson		 */
326132410Srwatson		sat = (struct sockaddr_at *)&ifr->ifr_addr;
327132410Srwatson		*sat = aa->aa_addr;
32815885Sjulian
329132410Srwatson 		/*
330132410Srwatson		 * and do some cleanups
331132410Srwatson		 */
332132410Srwatson		((struct netrange *)&sat->sat_zero)->nr_phase
333132410Srwatson		    = (aa->aa_flags & AFA_PHASE2) ? 2 : 1;
334132410Srwatson		((struct netrange *)&sat->sat_zero)->nr_firstnet =
335132410Srwatson		    aa->aa_firstnet;
336132410Srwatson		((struct netrange *)&sat->sat_zero)->nr_lastnet =
337132410Srwatson		    aa->aa_lastnet;
338132410Srwatson		break;
33915885Sjulian
340132410Srwatson	case SIOCSIFADDR:
341194619Srwatson		error = at_ifinit(ifp, aa,
342194619Srwatson		    (struct sockaddr_at *)&ifr->ifr_addr);
343194819Srwatson		goto out;
34415885Sjulian
345132410Srwatson	case SIOCAIFADDR:
346194619Srwatson		if (sateqaddr(&ifra->ifra_addr, &aa->aa_addr)) {
347194819Srwatson			error = 0;
348194819Srwatson			goto out;
349194619Srwatson		}
350194619Srwatson		error = at_ifinit(ifp, aa,
351194619Srwatson		    (struct sockaddr_at *)&ifr->ifr_addr);
352194819Srwatson		goto out;
35317921Sjulian
354132410Srwatson	case SIOCDIFADDR:
355194819Srwatson
356132410Srwatson		/*
357132410Srwatson		 * remove the ifaddr from the interface
358132410Srwatson		 */
359194913Srwatson		ifa = (struct ifaddr *)aa;
360229621Sjhb		IF_ADDR_WLOCK(ifp);
361194913Srwatson		TAILQ_REMOVE(&ifp->if_addrhead, ifa, ifa_link);
362229621Sjhb		IF_ADDR_WUNLOCK(ifp);
363194913Srwatson		ifa_free(ifa);				/* if_addrhead */
36417921Sjulian
365132410Srwatson		/*
366132410Srwatson		 * Now remove the at_ifaddr from the parallel structure
367132410Srwatson		 * as well, or we'd be in deep trouble
368132410Srwatson		 */
369194819Srwatson
370194819Srwatson		AT_IFADDR_WLOCK();
371194913Srwatson		TAILQ_REMOVE(&at_ifaddrhead, aa, aa_link);
372194619Srwatson		AT_IFADDR_WUNLOCK();
373194913Srwatson		ifa_free(ifa);				/* at_ifaddrhead */
374132410Srwatson		break;
375132410Srwatson
376132410Srwatson	default:
377194819Srwatson		if (ifp == NULL || ifp->if_ioctl == NULL) {
378194819Srwatson			error = EOPNOTSUPP;
379194819Srwatson			goto out;
380194819Srwatson		}
381194819Srwatson		error = ((*ifp->if_ioctl)(ifp, cmd, data));
382132410Srwatson	}
383194819Srwatson
384194819Srwatsonout:
385194819Srwatson	if (aa != NULL)
386194819Srwatson		ifa_free(&aa->aa_ifa);
387194819Srwatson	return (error);
38815885Sjulian}
38917254Sjulian
39017921Sjulian/*
39117921Sjulian * Given an interface and an at_ifaddr (supposedly on that interface)
39217921Sjulian * remove  any routes that depend on this.
39317921Sjulian * Why ifp is needed I'm not sure,
39417921Sjulian * as aa->at_ifaddr.ifa_ifp should be the same.
39517921Sjulian */
39615885Sjulianstatic int
397132410Srwatsonat_scrub(struct ifnet *ifp, struct at_ifaddr *aa)
39815885Sjulian{
399132410Srwatson	int error;
40015885Sjulian
401132410Srwatson	if (aa->aa_flags & AFA_ROUTE) {
402132410Srwatson		if (ifp->if_flags & IFF_LOOPBACK) {
403132410Srwatson			if ((error = aa_delsingleroute(&aa->aa_ifa,
404132410Srwatson			    &aa->aa_addr.sat_addr, &aa->aa_netmask.sat_addr))
405132410Srwatson			    != 0)
406132410Srwatson	    			return (error);
407132410Srwatson		} else if (ifp->if_flags & IFF_POINTOPOINT) {
408132410Srwatson			if ((error = rtinit(&aa->aa_ifa, RTM_DELETE,
409132410Srwatson			    RTF_HOST)) != 0)
410132410Srwatson	    			return (error);
411132410Srwatson		} else if (ifp->if_flags & IFF_BROADCAST) {
412132410Srwatson			error = aa_dorangeroute(&aa->aa_ifa,
413132410Srwatson			    ntohs(aa->aa_firstnet), ntohs(aa->aa_lastnet),
414132410Srwatson			    RTM_DELETE);
41518005Sjulian		}
416132410Srwatson		aa->aa_ifa.ifa_flags &= ~IFA_ROUTE;
417132410Srwatson		aa->aa_flags &= ~AFA_ROUTE;
41815885Sjulian	}
419132410Srwatson	return (0);
42015885Sjulian}
42115885Sjulian
42217921Sjulian/*
42317921Sjulian * given an at_ifaddr,a sockaddr_at and an ifp,
42417921Sjulian * bang them all together at high speed and see what happens
42517921Sjulian */
42615885Sjulianstatic int
427132410Srwatsonat_ifinit(struct ifnet *ifp, struct at_ifaddr *aa, struct sockaddr_at *sat)
42815885Sjulian{
429132410Srwatson	struct netrange nr, onr;
430132410Srwatson	struct sockaddr_at oldaddr;
431132410Srwatson	int error = 0, i, j;
432132410Srwatson	int netinc, nodeinc, nnets;
433132410Srwatson	u_short net;
43415885Sjulian
435132410Srwatson	/*
436132410Srwatson	 * save the old addresses in the at_ifaddr just in case we need them.
437132410Srwatson	 */
438132410Srwatson	oldaddr = aa->aa_addr;
439132410Srwatson	onr.nr_firstnet = aa->aa_firstnet;
440132410Srwatson	onr.nr_lastnet = aa->aa_lastnet;
44117921Sjulian
442132410Srwatson	/*
443132410Srwatson	 * take the address supplied as an argument, and add it to the
444132410Srwatson	 * at_ifnet (also given). Remember ing to update
445132410Srwatson	 * those parts of the at_ifaddr that need special processing
446132410Srwatson	 */
447132410Srwatson	bzero(AA_SAT(aa), sizeof(struct sockaddr_at));
448132410Srwatson	bcopy(sat->sat_zero, &nr, sizeof(struct netrange));
449132410Srwatson	bcopy(sat->sat_zero, AA_SAT(aa)->sat_zero, sizeof(struct netrange));
450132410Srwatson	nnets = ntohs(nr.nr_lastnet) - ntohs(nr.nr_firstnet) + 1;
451132410Srwatson	aa->aa_firstnet = nr.nr_firstnet;
452132410Srwatson	aa->aa_lastnet = nr.nr_lastnet;
45315885Sjulian
45417254Sjulian/* XXX ALC */
45517964Sjulian#if 0
456132410Srwatson	printf("at_ifinit: %s: %u.%u range %u-%u phase %d\n",
457132410Srwatson	    ifp->if_name,
458132410Srwatson	    ntohs(sat->sat_addr.s_net), sat->sat_addr.s_node,
459132410Srwatson	    ntohs(aa->aa_firstnet), ntohs(aa->aa_lastnet),
460132410Srwatson	    (aa->aa_flags & AFA_PHASE2) ? 2 : 1);
46117964Sjulian#endif
46217254Sjulian
46317921Sjulian	/*
464132410Srwatson	 * We could eliminate the need for a second phase 1 probe (post
465132410Srwatson	 * autoconf) if we check whether we're resetting the node. Note
466132410Srwatson	 * that phase 1 probes use only nodes, not net.node pairs.  Under
467132410Srwatson	 * phase 2, both the net and node must be the same.
46817921Sjulian	 */
469132410Srwatson	if (ifp->if_flags & IFF_LOOPBACK) {
470132410Srwatson		AA_SAT(aa)->sat_len = sat->sat_len;
471132410Srwatson		AA_SAT(aa)->sat_family = AF_APPLETALK;
472132410Srwatson		AA_SAT(aa)->sat_addr.s_net = sat->sat_addr.s_net;
473132410Srwatson		AA_SAT(aa)->sat_addr.s_node = sat->sat_addr.s_node;
474132410Srwatson#if 0
475132410Srwatson	} else if (fp->if_flags & IFF_POINTOPOINT) {
476132410Srwatson		/* unimplemented */
477132410Srwatson		/*
478132410Srwatson		 * we'd have to copy the dstaddr field over from the sat
479132410Srwatson		 * but it's not clear that it would contain the right info..
480132410Srwatson		 */
48117254Sjulian#endif
482132410Srwatson	} else {
48317921Sjulian		/*
484132410Srwatson		 * We are a normal (probably ethernet) interface.
485132410Srwatson		 * apply the new address to the interface structures etc.
486132410Srwatson		 * We will probe this address on the net first, before
487132410Srwatson		 * applying it to ensure that it is free.. If it is not, then
488132410Srwatson		 * we will try a number of other randomly generated addresses
489132410Srwatson		 * in this net and then increment the net.  etc.etc. until
490132410Srwatson		 * we find an unused address.
49117921Sjulian		 */
492132410Srwatson		aa->aa_flags |= AFA_PROBING; /* not loopback we Must probe? */
493132410Srwatson		AA_SAT(aa)->sat_len = sizeof(struct sockaddr_at);
494132410Srwatson		AA_SAT(aa)->sat_family = AF_APPLETALK;
495132410Srwatson		if (aa->aa_flags & AFA_PHASE2) {
496132410Srwatson			if (sat->sat_addr.s_net == ATADDR_ANYNET) {
497132410Srwatson				/*
498132410Srwatson				 * If we are phase 2, and the net was not
499132410Srwatson				 * specified then we select a random net
500132410Srwatson				 * within the supplied netrange.
501132410Srwatson				 * XXX use /dev/random?
502132410Srwatson				 */
503132410Srwatson				if (nnets != 1)
504132410Srwatson					net = ntohs(nr.nr_firstnet) +
505132410Srwatson					    time_second % (nnets - 1);
506132410Srwatson				else
507132410Srwatson					net = ntohs(nr.nr_firstnet);
508132410Srwatson			} else {
509132410Srwatson				/*
510132410Srwatson				 * if a net was supplied, then check that it
511132410Srwatson				 * is within the netrange. If it is not then
512132410Srwatson				 * replace the old values and return an error
513132410Srwatson				 */
514132410Srwatson				if (ntohs(sat->sat_addr.s_net) <
515132410Srwatson				    ntohs(nr.nr_firstnet) ||
516132410Srwatson				    ntohs(sat->sat_addr.s_net) >
517132410Srwatson				    ntohs(nr.nr_lastnet)) {
518132410Srwatson					aa->aa_addr = oldaddr;
519132410Srwatson					aa->aa_firstnet = onr.nr_firstnet;
520132410Srwatson					aa->aa_lastnet = onr.nr_lastnet;
521132410Srwatson					return (EINVAL);
522132410Srwatson				}
523132410Srwatson				/*
524132410Srwatson				 * otherwise just use the new net number..
525132410Srwatson				 */
526132410Srwatson				net = ntohs(sat->sat_addr.s_net);
527132410Srwatson			}
52815885Sjulian		} else {
529132410Srwatson			/*
530132410Srwatson			 * we must be phase one, so just use whatever we were
531132410Srwatson			 * given.  I guess it really isn't going to be
532132410Srwatson			 * used... RIGHT?
533132410Srwatson			 */
534132410Srwatson			net = ntohs(sat->sat_addr.s_net);
53515885Sjulian		}
536132410Srwatson
537132410Srwatson		/*
538132410Srwatson		 * set the node part of the address into the ifaddr.
539132410Srwatson		 * If it's not specified, be random about it...
540132410Srwatson		 * XXX use /dev/random?
54117921Sjulian		 */
542132410Srwatson		if (sat->sat_addr.s_node == ATADDR_ANYNODE)
543132410Srwatson			AA_SAT(aa)->sat_addr.s_node = time_second;
544132410Srwatson		else
545132410Srwatson			AA_SAT(aa)->sat_addr.s_node = sat->sat_addr.s_node;
546132410Srwatson
547132410Srwatson		/*
548132410Srwatson		 * Copy the phase.
549132410Srwatson		 */
550132410Srwatson		AA_SAT(aa)->sat_range.r_netrange.nr_phase =
551132410Srwatson		    ((aa->aa_flags & AFA_PHASE2) ? 2:1);
552132410Srwatson
553132410Srwatson		/*
554132410Srwatson		 * step through the nets in the range
555132410Srwatson		 * starting at the (possibly random) start point.
556132410Srwatson		 */
557132410Srwatson		for (i = nnets, netinc = 1; i > 0; net =
558132410Srwatson		    ntohs(nr.nr_firstnet) + ((net - ntohs(nr.nr_firstnet) +
559132410Srwatson		    netinc) % nnets), i--) {
560132410Srwatson			AA_SAT(aa)->sat_addr.s_net = htons(net);
561132410Srwatson
562132410Srwatson			/*
563132410Srwatson			 * using a rather strange stepping method,
564132410Srwatson			 * stagger through the possible node addresses
565132410Srwatson			 * Once again, starting at the (possibly random)
566132410Srwatson			 * initial node address.
567132410Srwatson			 */
568132410Srwatson			for (j = 0, nodeinc = time_second | 1; j < 256;
569132410Srwatson			    j++, AA_SAT(aa)->sat_addr.s_node += nodeinc) {
570132410Srwatson				if (AA_SAT(aa)->sat_addr.s_node > 253 ||
571132410Srwatson				    AA_SAT(aa)->sat_addr.s_node < 1)
572132410Srwatson					continue;
573132410Srwatson				aa->aa_probcnt = 10;
574132410Srwatson
575132410Srwatson				/*
576132410Srwatson				 * start off the probes as an asynchronous
577132410Srwatson				 * activity.  though why wait 200mSec?
578132410Srwatson				 */
579142226Srwatson				AARPTAB_LOCK();
580142226Srwatson				callout_reset(&aa->aa_callout, hz / 5,
581142226Srwatson				    aarpprobe, ifp);
582142226Srwatson				if (msleep(aa, &aarptab_mtx, PPAUSE|PCATCH,
583142226Srwatson				    "at_ifinit", 0)) {
584142226Srwatson					AARPTAB_UNLOCK();
585132410Srwatson					/*
586132410Srwatson					 * theoretically we shouldn't time
587132410Srwatson					 * out here so if we returned with an
588132410Srwatson					 * error..
589132410Srwatson					 */
590132410Srwatson					printf("at_ifinit: why did this "
591132410Srwatson					    "happen?!\n");
592132410Srwatson					aa->aa_addr = oldaddr;
593132410Srwatson					aa->aa_firstnet = onr.nr_firstnet;
594132410Srwatson					aa->aa_lastnet = onr.nr_lastnet;
595132410Srwatson					return (EINTR);
596132410Srwatson				}
597142226Srwatson				AARPTAB_UNLOCK();
598132410Srwatson
599132410Srwatson				/*
600132410Srwatson				 * The async activity should have woken us
601132410Srwatson				 * up.  We need to see if it was successful
602132410Srwatson				 * in finding a free spot, or if we need to
603132410Srwatson				 * iterate to the next address to try.
604132410Srwatson				 */
605132410Srwatson				if ((aa->aa_flags & AFA_PROBING) == 0)
606132410Srwatson					break;
607132410Srwatson			}
608132410Srwatson
609132410Srwatson			/*
610132410Srwatson			 * of course we need to break out through two loops...
611132410Srwatson			 */
612132410Srwatson			if ((aa->aa_flags & AFA_PROBING) == 0)
613132410Srwatson				break;
614132410Srwatson			/* reset node for next network */
615132410Srwatson			AA_SAT(aa)->sat_addr.s_node = time_second;
61615885Sjulian		}
617132410Srwatson
61817921Sjulian		/*
619132410Srwatson		 * if we are still trying to probe, then we have finished all
620132410Srwatson		 * the possible addresses, so we need to give up
62117921Sjulian		 */
622132410Srwatson		if (aa->aa_flags & AFA_PROBING) {
623132410Srwatson			aa->aa_addr = oldaddr;
624132410Srwatson			aa->aa_firstnet = onr.nr_firstnet;
625132410Srwatson			aa->aa_lastnet = onr.nr_lastnet;
626132410Srwatson			return (EADDRINUSE);
627132410Srwatson		}
62815885Sjulian	}
62915885Sjulian
63017921Sjulian	/*
631132410Srwatson	 * Now that we have selected an address, we need to tell the interface
632132410Srwatson	 * about it, just in case it needs to adjust something.
63317921Sjulian	 */
634132410Srwatson	if (ifp->if_ioctl != NULL &&
635132410Srwatson	    (error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)aa))) {
636132410Srwatson		/*
637132410Srwatson		 * of course this could mean that it objects violently
638132410Srwatson		 * so if it does, we back out again..
639132410Srwatson		 */
640132410Srwatson		aa->aa_addr = oldaddr;
641132410Srwatson		aa->aa_firstnet = onr.nr_firstnet;
642132410Srwatson		aa->aa_lastnet = onr.nr_lastnet;
643132410Srwatson		return (error);
64415885Sjulian	}
64515885Sjulian
64617921Sjulian	/*
647132410Srwatson	 * set up the netmask part of the at_ifaddr
648132410Srwatson	 * and point the appropriate pointer in the ifaddr to it.
649132410Srwatson	 * probably pointless, but what the heck.. XXX
65028845Sjulian	 */
651132410Srwatson	bzero(&aa->aa_netmask, sizeof(aa->aa_netmask));
652132410Srwatson	aa->aa_netmask.sat_len = sizeof(struct sockaddr_at);
653132410Srwatson	aa->aa_netmask.sat_family = AF_APPLETALK;
654132410Srwatson	aa->aa_netmask.sat_addr.s_net = 0xffff;
655132410Srwatson	aa->aa_netmask.sat_addr.s_node = 0;
656132410Srwatson	aa->aa_ifa.ifa_netmask =(struct sockaddr *) &(aa->aa_netmask); /* XXX */
65728845Sjulian
658132410Srwatson	/*
659132410Srwatson	 * Initialize broadcast (or remote p2p) address
66017921Sjulian	 */
661132410Srwatson	bzero(&aa->aa_broadaddr, sizeof(aa->aa_broadaddr));
662132410Srwatson	aa->aa_broadaddr.sat_len = sizeof(struct sockaddr_at);
663132410Srwatson	aa->aa_broadaddr.sat_family = AF_APPLETALK;
66415885Sjulian
665132410Srwatson	aa->aa_ifa.ifa_metric = ifp->if_metric;
666132410Srwatson	if (ifp->if_flags & IFF_BROADCAST) {
667132410Srwatson		aa->aa_broadaddr.sat_addr.s_net = htons(0);
668132410Srwatson		aa->aa_broadaddr.sat_addr.s_node = 0xff;
669132410Srwatson		aa->aa_ifa.ifa_broadaddr = (struct sockaddr *)
670132410Srwatson		    &aa->aa_broadaddr;
671132410Srwatson		/* add the range of routes needed */
672132410Srwatson		error = aa_dorangeroute(&aa->aa_ifa, ntohs(aa->aa_firstnet),
673132410Srwatson		    ntohs(aa->aa_lastnet), RTM_ADD);
674132410Srwatson	} else if (ifp->if_flags & IFF_POINTOPOINT) {
675132410Srwatson		struct at_addr	rtaddr, rtmask;
67617921Sjulian
677132410Srwatson		bzero(&rtaddr, sizeof(rtaddr));
678132410Srwatson		bzero(&rtmask, sizeof(rtmask));
679132410Srwatson		/* fill in the far end if we know it here XXX */
680132410Srwatson		aa->aa_ifa.ifa_dstaddr = (struct sockaddr *) &aa->aa_dstaddr;
681132410Srwatson		error = aa_addsingleroute(&aa->aa_ifa, &rtaddr, &rtmask);
682132410Srwatson	} else if (ifp->if_flags & IFF_LOOPBACK) {
683132410Srwatson		struct at_addr	rtaddr, rtmask;
68417921Sjulian
685132410Srwatson		bzero(&rtaddr, sizeof(rtaddr));
686132410Srwatson		bzero(&rtmask, sizeof(rtmask));
687132410Srwatson		rtaddr.s_net = AA_SAT(aa)->sat_addr.s_net;
688132410Srwatson		rtaddr.s_node = AA_SAT(aa)->sat_addr.s_node;
689132410Srwatson		rtmask.s_net = 0xffff;
690132410Srwatson		 /* XXX should not be so.. should be HOST route */
691132410Srwatson		rtmask.s_node = 0x0;
692132410Srwatson		error = aa_addsingleroute(&aa->aa_ifa, &rtaddr, &rtmask);
69315885Sjulian	}
69415885Sjulian
69517921Sjulian	/*
696132410Srwatson	 * set the address of our "check if this addr is ours" routine.
69717921Sjulian	 */
698132410Srwatson	aa->aa_ifa.ifa_claim_addr = aa_claim_addr;
69917921Sjulian
700132410Srwatson	/*
701132410Srwatson	 * of course if we can't add these routes we back out, but it's
702132410Srwatson	 * getting risky by now XXX
703132410Srwatson	 */
704132410Srwatson	if (error) {
705132410Srwatson		at_scrub(ifp, aa);
706132410Srwatson		aa->aa_addr = oldaddr;
707132410Srwatson		aa->aa_firstnet = onr.nr_firstnet;
708132410Srwatson		aa->aa_lastnet = onr.nr_lastnet;
709132410Srwatson		return (error);
71015885Sjulian	}
71115885Sjulian
71217921Sjulian	/*
713132410Srwatson	 * note that the address has a route associated with it....
71417921Sjulian	 */
715132410Srwatson	aa->aa_ifa.ifa_flags |= IFA_ROUTE;
716132410Srwatson	aa->aa_flags |= AFA_ROUTE;
717132410Srwatson	return (0);
71815885Sjulian}
71915885Sjulian
72017921Sjulian/*
72117921Sjulian * check whether a given address is a broadcast address for us..
72217921Sjulian */
72315885Sjulianint
724249925Sglebiusat_broadcast(const struct sockaddr_at *sat)
72515885Sjulian{
726132410Srwatson	struct at_ifaddr *aa;
72715885Sjulian
728194619Srwatson	AT_IFADDR_LOCK_ASSERT();
729194619Srwatson
730132410Srwatson	/*
731132410Srwatson	 * If the node is not right, it can't be a broadcast
732132410Srwatson	 */
733132410Srwatson	if (sat->sat_addr.s_node != ATADDR_BCAST)
734132410Srwatson		return (0);
73517921Sjulian
736132410Srwatson	/*
737132410Srwatson	 * If the node was right then if the net is right, it's a broadcast
738132410Srwatson	 */
739132410Srwatson	if (sat->sat_addr.s_net == ATADDR_ANYNET)
740132410Srwatson		return (1);
74117921Sjulian
742132410Srwatson	/*
743132410Srwatson	 * failing that, if the net is one we have, it's a broadcast as well.
744132410Srwatson	 */
745194913Srwatson	TAILQ_FOREACH(aa, &at_ifaddrhead, aa_link) {
746132410Srwatson		if ((aa->aa_ifp->if_flags & IFF_BROADCAST)
747132410Srwatson		    && (ntohs(sat->sat_addr.s_net) >= ntohs(aa->aa_firstnet)
748132410Srwatson		    && ntohs(sat->sat_addr.s_net) <= ntohs(aa->aa_lastnet)))
749127288Srwatson			return (1);
75015885Sjulian	}
751132410Srwatson	return (0);
75215885Sjulian}
75315885Sjulian
75417254Sjulian/*
75517967Sjulian * aa_dorangeroute()
75617254Sjulian *
75717967Sjulian * Add a route for a range of networks from bot to top - 1.
75817254Sjulian * Algorithm:
75917254Sjulian *
76017967Sjulian * Split the range into two subranges such that the middle
76117967Sjulian * of the two ranges is the point where the highest bit of difference
76235256Sdes * between the two addresses makes its transition.
76317967Sjulian * Each of the upper and lower ranges might not exist, or might be
76417967Sjulian * representable by 1 or more netmasks. In addition, if both
76537965Salex * ranges can be represented by the same netmask, then they can be merged
76617967Sjulian * by using the next higher netmask..
76717254Sjulian */
76817254Sjulian
76917254Sjulianstatic int
77017967Sjulianaa_dorangeroute(struct ifaddr *ifa, u_int bot, u_int top, int cmd)
77117254Sjulian{
77217964Sjulian	u_int mask1;
77317964Sjulian	struct at_addr addr;
77417964Sjulian	struct at_addr mask;
77517964Sjulian	int error;
77617254Sjulian
77717964Sjulian	/*
77817964Sjulian	 * slight sanity check
77917964Sjulian	 */
78017964Sjulian	if (bot > top) return (EINVAL);
78117254Sjulian
78217964Sjulian	addr.s_node = 0;
78317964Sjulian	mask.s_node = 0;
78417964Sjulian	/*
78517964Sjulian	 * just start out with the lowest boundary
78617964Sjulian	 * and keep extending the mask till it's too big.
78717964Sjulian	 */
78817964Sjulian
78917964Sjulian	 while (bot <= top) {
79017964Sjulian	 	mask1 = 1;
791132410Srwatson	 	while (((bot & ~mask1) >= bot) && ((bot | mask1) <= top)) {
79217964Sjulian			mask1 <<= 1;
79317964Sjulian			mask1 |= 1;
79417964Sjulian		}
79517964Sjulian		mask1 >>= 1;
79617964Sjulian		mask.s_net = htons(~mask1);
79717964Sjulian		addr.s_net = htons(bot);
798132410Srwatson		if (cmd == RTM_ADD) {
799132410Srwatson			error =	 aa_addsingleroute(ifa,&addr,&mask);
80017967Sjulian			if (error) {
80117967Sjulian				/* XXX clean up? */
80217967Sjulian				return (error);
80317967Sjulian			}
804132410Srwatson		} else
80517967Sjulian			error =	 aa_delsingleroute(ifa,&addr,&mask);
80617964Sjulian		bot = (bot | mask1) + 1;
80717964Sjulian	}
808127288Srwatson	return (0);
80917254Sjulian}
81017254Sjulian
81117254Sjulianstatic int
812132410Srwatsonaa_addsingleroute(struct ifaddr *ifa, struct at_addr *addr,
813132410Srwatson    struct at_addr *mask)
81417254Sjulian{
81517254Sjulian
81617964Sjulian#if 0
817132410Srwatson	printf("aa_addsingleroute: %x.%x mask %x.%x ...\n",
818132410Srwatson	    ntohs(addr->s_net), addr->s_node, ntohs(mask->s_net),
819132410Srwatson	    mask->s_node);
82017964Sjulian#endif
82117254Sjulian
822194822Srwatson	return (aa_dosingleroute(ifa, addr, mask, RTM_ADD, RTF_UP));
82317254Sjulian}
82417254Sjulian
82517254Sjulianstatic int
826132410Srwatsonaa_delsingleroute(struct ifaddr *ifa, struct at_addr *addr,
827132410Srwatson    struct at_addr *mask)
82817254Sjulian{
82917254Sjulian
830194822Srwatson	return (aa_dosingleroute(ifa, addr, mask, RTM_DELETE, 0));
83117254Sjulian}
83217254Sjulian
83317254Sjulianstatic int
834132410Srwatsonaa_dosingleroute(struct ifaddr *ifa, struct at_addr *at_addr,
835132410Srwatson    struct at_addr *at_mask, int cmd, int flags)
83617254Sjulian{
837132410Srwatson	struct sockaddr_at addr, mask;
83817254Sjulian
839132410Srwatson	bzero(&addr, sizeof(addr));
840132410Srwatson	bzero(&mask, sizeof(mask));
841132410Srwatson	addr.sat_family = AF_APPLETALK;
842132410Srwatson	addr.sat_len = sizeof(struct sockaddr_at);
843132410Srwatson	addr.sat_addr.s_net = at_addr->s_net;
844132410Srwatson	addr.sat_addr.s_node = at_addr->s_node;
845132410Srwatson	mask.sat_family = AF_APPLETALK;
846132410Srwatson	mask.sat_len = sizeof(struct sockaddr_at);
847132410Srwatson	mask.sat_addr.s_net = at_mask->s_net;
848132410Srwatson	mask.sat_addr.s_node = at_mask->s_node;
849132410Srwatson	if (at_mask->s_node)
850132410Srwatson		flags |= RTF_HOST;
851132410Srwatson	return (rtrequest(cmd, (struct sockaddr *) &addr,
852132410Srwatson	    (flags & RTF_HOST)?(ifa->ifa_dstaddr):(ifa->ifa_addr),
853132410Srwatson	    (struct sockaddr *) &mask, flags, NULL));
85417254Sjulian}
85517254Sjulian
85628845Sjulianstatic int
85728845Sjulianaa_claim_addr(struct ifaddr *ifa, struct sockaddr *gw0)
85828845Sjulian{
85928845Sjulian	struct sockaddr_at *addr = (struct sockaddr_at *)ifa->ifa_addr;
86028845Sjulian	struct sockaddr_at *gw = (struct sockaddr_at *)gw0;
86128845Sjulian
86228845Sjulian	switch (gw->sat_range.r_netrange.nr_phase) {
86328845Sjulian	case 1:
86428845Sjulian		if(addr->sat_range.r_netrange.nr_phase == 1)
865127288Srwatson			return (1);
866132410Srwatson
86728845Sjulian	case 0:
86828845Sjulian	case 2:
86928845Sjulian		/*
87028845Sjulian		 * if it's our net (including 0),
87128845Sjulian		 * or netranges are valid, and we are in the range,
87228845Sjulian		 * then it's ours.
87328845Sjulian		 */
87428845Sjulian		if ((addr->sat_addr.s_net == gw->sat_addr.s_net)
875132410Srwatson		    || ((addr->sat_range.r_netrange.nr_lastnet)
876132410Srwatson		    && (ntohs(gw->sat_addr.s_net) >=
877132410Srwatson		    ntohs(addr->sat_range.r_netrange.nr_firstnet))
878132410Srwatson		    && (ntohs(gw->sat_addr.s_net) <=
879132410Srwatson		    ntohs(addr->sat_range.r_netrange.nr_lastnet))))
880127288Srwatson			return (1);
88128845Sjulian		break;
88228845Sjulian	default:
88328845Sjulian		printf("atalk: bad phase\n");
88428845Sjulian	}
885127288Srwatson	return (0);
88628845Sjulian}
887