xref: /netbsd-src/sys/netinet/in_selsrc.c (revision 9e4c2bda8aed156eb124da03a53cec6ab681a8b0)
1*9e4c2bdaSozaki-r /*	$NetBSD: in_selsrc.c,v 1.17 2016/07/07 09:32:02 ozaki-r Exp $	*/
2a25eaedeSdyoung 
3a25eaedeSdyoung /*-
4a25eaedeSdyoung  * Copyright (c) 2005 David Young.  All rights reserved.
5a25eaedeSdyoung  *
6a25eaedeSdyoung  * This code was written by David Young.
7a25eaedeSdyoung  *
8a25eaedeSdyoung  * Redistribution and use in source and binary forms, with or without
9a25eaedeSdyoung  * modification, are permitted provided that the following conditions
10a25eaedeSdyoung  * are met:
11a25eaedeSdyoung  * 1. Redistributions of source code must retain the above copyright
12a25eaedeSdyoung  *    notice, this list of conditions and the following disclaimer.
13a25eaedeSdyoung  * 2. Redistributions in binary form must reproduce the above copyright
14a25eaedeSdyoung  *    notice, this list of conditions and the following disclaimer in the
15a25eaedeSdyoung  *    documentation and/or other materials provided with the distribution.
16a25eaedeSdyoung  *
17a25eaedeSdyoung  * THIS SOFTWARE IS PROVIDED BY DAVID YOUNG ``AS IS'' AND ANY
18a25eaedeSdyoung  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
19a25eaedeSdyoung  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20a25eaedeSdyoung  * PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
21a25eaedeSdyoung  * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
22a25eaedeSdyoung  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23a25eaedeSdyoung  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24a25eaedeSdyoung  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25a25eaedeSdyoung  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
26a25eaedeSdyoung  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27a25eaedeSdyoung  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28a25eaedeSdyoung  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29a25eaedeSdyoung  */
30a25eaedeSdyoung 
31a25eaedeSdyoung #include <sys/cdefs.h>
32*9e4c2bdaSozaki-r __KERNEL_RCSID(0, "$NetBSD: in_selsrc.c,v 1.17 2016/07/07 09:32:02 ozaki-r Exp $");
33a25eaedeSdyoung 
341c4a50f1Spooka #ifdef _KERNEL_OPT
35a25eaedeSdyoung #include "opt_inet.h"
36a25eaedeSdyoung #include "opt_inet_conf.h"
371c4a50f1Spooka #endif
38a25eaedeSdyoung 
39a25eaedeSdyoung #include <lib/libkern/libkern.h>
40a25eaedeSdyoung 
41a25eaedeSdyoung #include <sys/param.h>
42a25eaedeSdyoung #include <sys/ioctl.h>
43a25eaedeSdyoung #include <sys/errno.h>
44a25eaedeSdyoung #include <sys/malloc.h>
45a25eaedeSdyoung #include <sys/socket.h>
46a25eaedeSdyoung #include <sys/socketvar.h>
47a25eaedeSdyoung #include <sys/sysctl.h>
48a25eaedeSdyoung #include <sys/systm.h>
49a25eaedeSdyoung #include <sys/proc.h>
50a25eaedeSdyoung #include <sys/syslog.h>
51a25eaedeSdyoung 
52a25eaedeSdyoung #include <net/if.h>
53a25eaedeSdyoung 
54a25eaedeSdyoung #include <net/if_ether.h>
55a25eaedeSdyoung 
56a25eaedeSdyoung #include <netinet/in_systm.h>
57a25eaedeSdyoung #include <netinet/in.h>
58a25eaedeSdyoung #include <netinet/in_var.h>
59a25eaedeSdyoung #include <netinet/ip.h>
60a25eaedeSdyoung #include <netinet/ip_var.h>
61a25eaedeSdyoung #include <netinet/in_ifattach.h>
62a25eaedeSdyoung #include <netinet/in_pcb.h>
63a25eaedeSdyoung #include <netinet/if_inarp.h>
64a25eaedeSdyoung #include <netinet/ip_mroute.h>
65a25eaedeSdyoung #include <netinet/igmp_var.h>
66a25eaedeSdyoung #include <netinet/in_selsrc.h>
67a25eaedeSdyoung 
68a25eaedeSdyoung #ifdef INET
69a25eaedeSdyoung struct score_src_name {
70a25eaedeSdyoung 	const char		*sn_name;
71a25eaedeSdyoung 	const in_score_src_t	sn_score_src;
72a25eaedeSdyoung };
73a25eaedeSdyoung 
74a25eaedeSdyoung static const struct sysctlnode *in_domifattach_sysctl(struct in_ifsysctl *);
75a25eaedeSdyoung static int in_preference(const struct in_addr *, int, int,
76a25eaedeSdyoung     const struct in_addr *);
77a25eaedeSdyoung static int in_index(const struct in_addr *, int, int, const struct in_addr *);
78a25eaedeSdyoung static int in_matchlen(const struct in_addr *, int, int,
79a25eaedeSdyoung     const struct in_addr *);
80a25eaedeSdyoung static int in_match_category(const struct in_addr *, int, int,
81a25eaedeSdyoung     const struct in_addr *);
82a25eaedeSdyoung static size_t in_get_selectsrc(const struct in_ifselsrc *, char *,
83a25eaedeSdyoung     const size_t);
84a25eaedeSdyoung static int in_set_selectsrc(struct in_ifselsrc *, char *buf);
85a25eaedeSdyoung static int in_sysctl_selectsrc(SYSCTLFN_PROTO);
86a25eaedeSdyoung static in_score_src_t name_to_score_src(const char *);
87a25eaedeSdyoung static const char *score_src_to_name(const in_score_src_t);
88a25eaedeSdyoung static void in_score(const in_score_src_t *, int *, int *,
89a25eaedeSdyoung     const struct in_addr *, int, int, const struct in_addr *);
90a25eaedeSdyoung 
91a25eaedeSdyoung static const struct score_src_name score_src_names[] = {
92a25eaedeSdyoung 	  {"same-category", in_match_category}
93a25eaedeSdyoung 	, {"common-prefix-len", in_matchlen}
94a25eaedeSdyoung 	, {"index", in_index}
95a25eaedeSdyoung 	, {"preference", in_preference}
96a25eaedeSdyoung 	, {NULL, NULL}
97a25eaedeSdyoung };
98a25eaedeSdyoung 
99a25eaedeSdyoung static const struct in_ifselsrc initial_iss = { 0, {NULL} };
100a25eaedeSdyoung 
101a25eaedeSdyoung static struct in_ifselsrc default_iss = { 0, {in_index} };
102a25eaedeSdyoung 
103a25eaedeSdyoung #ifdef GETIFA_DEBUG
104a25eaedeSdyoung int in_selsrc_debug = 0;
1059111c8b6Sdyoung #endif /* GETIFA_DEBUG */
106a25eaedeSdyoung 
107a25eaedeSdyoung SYSCTL_SETUP(sysctl_selectsrc_setup, "sysctl selectsrc subtree setup")
108a25eaedeSdyoung {
109a25eaedeSdyoung 	int rc;
110a25eaedeSdyoung 	const struct sysctlnode *rnode, *cnode;
111a25eaedeSdyoung 
112a25eaedeSdyoung 	if ((rc = sysctl_createv(clog, 0, NULL, &rnode,
113a25eaedeSdyoung 	    CTLFLAG_PERMANENT, CTLTYPE_NODE, "inet",
114a25eaedeSdyoung 	    NULL, NULL, 0, NULL, 0, CTL_NET, PF_INET, CTL_EOL)) != 0) {
115a25eaedeSdyoung 		printf("%s: could not create net.inet, rc = %d\n", __func__,
116a25eaedeSdyoung 		    rc);
117a25eaedeSdyoung 		return;
118a25eaedeSdyoung 	}
119a25eaedeSdyoung 	if ((rc = sysctl_createv(clog, 0, NULL, &rnode,
120a25eaedeSdyoung 	    CTLFLAG_PERMANENT, CTLTYPE_NODE, "ip",
121a25eaedeSdyoung 	    NULL, NULL, 0, NULL, 0,
122a25eaedeSdyoung 	    CTL_NET, PF_INET, IPPROTO_IP, CTL_EOL)) != 0) {
123a25eaedeSdyoung 		printf("%s: could not create net.inet.ip, rc = %d\n", __func__,
124a25eaedeSdyoung 		    rc);
125a25eaedeSdyoung 		return;
126a25eaedeSdyoung 	}
127a25eaedeSdyoung 	if ((rc = sysctl_createv(clog, 0, NULL, &rnode,
128a25eaedeSdyoung 	    CTLFLAG_PERMANENT, CTLTYPE_NODE, "selectsrc",
129a25eaedeSdyoung 	    NULL, NULL, 0, NULL, 0,
130a25eaedeSdyoung 	    CTL_NET, PF_INET, IPPROTO_IP, CTL_CREATE, CTL_EOL)) != 0) {
131a25eaedeSdyoung 		printf("%s: could not create net.inet.ip.selectsrc, "
132a25eaedeSdyoung 		       "rc = %d\n", __func__, rc);
133a25eaedeSdyoung 		return;
134a25eaedeSdyoung 	}
1359111c8b6Sdyoung #ifdef GETIFA_DEBUG
136a25eaedeSdyoung 	if ((rc = sysctl_createv(clog, 0, &rnode, &cnode,
137a25eaedeSdyoung 	    CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT, "debug",
138a25eaedeSdyoung 	    SYSCTL_DESCR("enable source-selection debug messages"),
139a25eaedeSdyoung 	    NULL, 0, &in_selsrc_debug, 0, CTL_CREATE, CTL_EOL)) != 0) {
140a25eaedeSdyoung 		printf("%s: could not create net.inet.ip.selectsrc.debug, "
141a25eaedeSdyoung 		       "rc = %d\n", __func__, rc);
142a25eaedeSdyoung 		return;
143a25eaedeSdyoung 	}
1449111c8b6Sdyoung #endif /* GETIFA_DEBUG */
145a25eaedeSdyoung 	if ((rc = sysctl_createv(clog, 0, &rnode, &cnode,
146ffd93408Schristos 	    CTLFLAG_READWRITE, CTLTYPE_STRUCT, "default",
147a25eaedeSdyoung 	    SYSCTL_DESCR("default source selection policy"),
148a25eaedeSdyoung 	    in_sysctl_selectsrc, 0, &default_iss, IN_SELECTSRC_LEN,
149a25eaedeSdyoung 	    CTL_CREATE, CTL_EOL)) != 0) {
150a25eaedeSdyoung 		printf(
151a25eaedeSdyoung 		    "%s: could not create net.inet.ip.selectsrc.default (%d)\n",
152a25eaedeSdyoung 		    __func__, rc);
153a25eaedeSdyoung 		return;
154a25eaedeSdyoung 	}
155a25eaedeSdyoung }
156a25eaedeSdyoung 
157a25eaedeSdyoung /*
158bc99546fSdyoung  * Score by address preference: prefer addresses with higher preference
159a25eaedeSdyoung  * number.  Preference numbers are assigned with ioctl SIOCSIFADDRPREF.
160a25eaedeSdyoung  */
161a25eaedeSdyoung static int
in_preference(const struct in_addr * src,int preference,int idx,const struct in_addr * dst)162168cd830Schristos in_preference(const struct in_addr *src, int preference,
163168cd830Schristos     int idx, const struct in_addr *dst)
164a25eaedeSdyoung {
165bc99546fSdyoung 	return preference;
166a25eaedeSdyoung }
167a25eaedeSdyoung 
168a25eaedeSdyoung /*
169a25eaedeSdyoung  * Score by address "index": prefer addresses nearer the head of
170a25eaedeSdyoung  * the ifaddr list.
171a25eaedeSdyoung  */
172a25eaedeSdyoung static int
in_index(const struct in_addr * src,int preference,int idx,const struct in_addr * dst)173168cd830Schristos in_index(const struct in_addr *src, int preference, int idx,
174168cd830Schristos     const struct in_addr *dst)
175a25eaedeSdyoung {
176a25eaedeSdyoung 	return -idx;
177a25eaedeSdyoung }
178a25eaedeSdyoung 
179a25eaedeSdyoung /*
180a25eaedeSdyoung  * Length of longest common prefix of src and dst.
181a25eaedeSdyoung  *
182a25eaedeSdyoung  * (Derived from in6_matchlen.)
183a25eaedeSdyoung  */
184a25eaedeSdyoung static int
in_matchlen(const struct in_addr * src,int preference,int idx,const struct in_addr * dst)185168cd830Schristos in_matchlen(const struct in_addr *src, int preference,
186168cd830Schristos     int idx, const struct in_addr *dst)
187a25eaedeSdyoung {
188a25eaedeSdyoung 	int match = 0;
189a25eaedeSdyoung 	const uint8_t *s = (const uint8_t *)src, *d = (const uint8_t *)dst;
190a25eaedeSdyoung 	const uint8_t *lim = s + 4;
191a25eaedeSdyoung 	uint_fast8_t r = 0;
192a25eaedeSdyoung 
193a25eaedeSdyoung 	while (s < lim && (r = (*d++ ^ *s++)) == 0)
194a25eaedeSdyoung 		match += 8;
195a25eaedeSdyoung 
196a25eaedeSdyoung 	if (s == lim)
197a25eaedeSdyoung 		return match;
198a25eaedeSdyoung 
199a25eaedeSdyoung 	while ((r & 0x80) == 0) {
200a25eaedeSdyoung 		match++;
201a25eaedeSdyoung 		r <<= 1;
202a25eaedeSdyoung 	}
203a25eaedeSdyoung 	return match;
204a25eaedeSdyoung }
205a25eaedeSdyoung 
206a25eaedeSdyoung static enum in_category
in_categorize(const struct in_addr * s)207a25eaedeSdyoung in_categorize(const struct in_addr *s)
208a25eaedeSdyoung {
209a25eaedeSdyoung 	if (IN_ANY_LOCAL(s->s_addr))
210a25eaedeSdyoung 		return IN_CATEGORY_LINKLOCAL;
211a25eaedeSdyoung 	else if (IN_PRIVATE(s->s_addr))
212a25eaedeSdyoung 		return IN_CATEGORY_PRIVATE;
213a25eaedeSdyoung 	else
214a25eaedeSdyoung 		return IN_CATEGORY_OTHER;
215a25eaedeSdyoung }
216a25eaedeSdyoung 
217a25eaedeSdyoung static int
in_match_category(const struct in_addr * src,int preference,int idx,const struct in_addr * dst)218168cd830Schristos in_match_category(const struct in_addr *src, int preference,
219168cd830Schristos     int idx, const struct in_addr *dst)
220a25eaedeSdyoung {
221a25eaedeSdyoung 	enum in_category dst_c = in_categorize(dst),
222a25eaedeSdyoung 	                 src_c = in_categorize(src);
223a25eaedeSdyoung #ifdef GETIFA_DEBUG
224a25eaedeSdyoung 	if (in_selsrc_debug) {
225a25eaedeSdyoung 		printf("%s: dst %#08" PRIx32 " categ %d, src %#08" PRIx32
226a25eaedeSdyoung 		    " categ %d\n", __func__, ntohl(dst->s_addr), dst_c,
227a25eaedeSdyoung 		    ntohl(src->s_addr), src_c);
228a25eaedeSdyoung 	}
229a25eaedeSdyoung #endif /* GETIFA_DEBUG */
230a25eaedeSdyoung 
231a25eaedeSdyoung 	if (dst_c == src_c)
232a25eaedeSdyoung 		return 2;
233a25eaedeSdyoung 	else if (dst_c == IN_CATEGORY_LINKLOCAL && src_c == IN_CATEGORY_PRIVATE)
234a25eaedeSdyoung 		return 1;
235a25eaedeSdyoung 	else if (dst_c == IN_CATEGORY_PRIVATE && src_c == IN_CATEGORY_LINKLOCAL)
236a25eaedeSdyoung 		return 1;
237a25eaedeSdyoung 	else if (dst_c == IN_CATEGORY_OTHER && src_c == IN_CATEGORY_PRIVATE)
238a25eaedeSdyoung 		return 1;
239a25eaedeSdyoung 	else
240a25eaedeSdyoung 		return 0;
241a25eaedeSdyoung }
242a25eaedeSdyoung 
243a25eaedeSdyoung static void
in_score(const in_score_src_t * score_src,int * score,int * scorelenp,const struct in_addr * src,int preference,int idx,const struct in_addr * dst)244a25eaedeSdyoung in_score(const in_score_src_t *score_src, int *score, int *scorelenp,
245a25eaedeSdyoung     const struct in_addr *src, int preference, int idx,
246a25eaedeSdyoung     const struct in_addr *dst)
247a25eaedeSdyoung {
248a25eaedeSdyoung 	int i;
249a25eaedeSdyoung 
250a25eaedeSdyoung 	for (i = 0; i < IN_SCORE_SRC_MAX && score_src[i] != NULL; i++)
251a25eaedeSdyoung 		score[i] = (*score_src[i])(src, preference, idx, dst);
252a25eaedeSdyoung 	if (scorelenp != NULL)
253a25eaedeSdyoung 		*scorelenp = i;
254a25eaedeSdyoung }
255a25eaedeSdyoung 
256a25eaedeSdyoung static int
in_score_cmp(int * score1,int * score2,int scorelen)257a25eaedeSdyoung in_score_cmp(int *score1, int *score2, int scorelen)
258a25eaedeSdyoung {
259a25eaedeSdyoung 	int i;
260a25eaedeSdyoung 
261a25eaedeSdyoung 	for (i = 0; i < scorelen; i++) {
262a25eaedeSdyoung 		if (score1[i] == score2[i])
263a25eaedeSdyoung 			continue;
264a25eaedeSdyoung 		return score1[i] - score2[i];
265a25eaedeSdyoung 	}
266a25eaedeSdyoung 	return 0;
267a25eaedeSdyoung }
268a25eaedeSdyoung 
269a25eaedeSdyoung #ifdef GETIFA_DEBUG
270a25eaedeSdyoung static void
in_score_println(int * score,int scorelen)271a25eaedeSdyoung in_score_println(int *score, int scorelen)
272a25eaedeSdyoung {
273a25eaedeSdyoung 	int i;
274a25eaedeSdyoung 	const char *delim = "[";
275a25eaedeSdyoung 
276a25eaedeSdyoung 	for (i = 0; i < scorelen; i++) {
277a25eaedeSdyoung 		printf("%s%d", delim, score[i]);
278a25eaedeSdyoung 		delim = ", ";
279a25eaedeSdyoung 	}
280a25eaedeSdyoung 	printf("]\n");
281a25eaedeSdyoung }
282a25eaedeSdyoung #endif /* GETIFA_DEBUG */
283a25eaedeSdyoung 
284a25eaedeSdyoung /* Scan the interface addresses on the interface ifa->ifa_ifp for
285a25eaedeSdyoung  * the source address that best matches the destination, dst0,
286a25eaedeSdyoung  * according to the source address-selection policy for this
287a25eaedeSdyoung  * interface.  If there is no better match than `ifa', return `ifa'.
288a25eaedeSdyoung  * Otherwise, return the best address.
289a25eaedeSdyoung  *
290a25eaedeSdyoung  * Note that in_getifa is called after the kernel has decided which
291a25eaedeSdyoung  * output interface to use (ifa->ifa_ifp), and in_getifa will not
292a25eaedeSdyoung  * scan an address belonging to any other interface.
293a25eaedeSdyoung  */
294a25eaedeSdyoung struct ifaddr *
in_getifa(struct ifaddr * ifa,const struct sockaddr * dst0)295a25eaedeSdyoung in_getifa(struct ifaddr *ifa, const struct sockaddr *dst0)
296a25eaedeSdyoung {
297a25eaedeSdyoung 	const in_score_src_t *score_src;
298a25eaedeSdyoung 	int idx, scorelen;
299a25eaedeSdyoung 	const struct sockaddr_in *dst, *src;
300a25eaedeSdyoung 	struct ifaddr *alt_ifa, *best_ifa;
301a25eaedeSdyoung 	struct ifnet *ifp;
302a25eaedeSdyoung 	struct in_ifsysctl *isc;
303a25eaedeSdyoung 	struct in_ifselsrc *iss;
304a25eaedeSdyoung 	int best_score[IN_SCORE_SRC_MAX], score[IN_SCORE_SRC_MAX];
305505639d2Sroy 	struct in_ifaddr *ia;
306a25eaedeSdyoung 
307a25eaedeSdyoung 	if (ifa->ifa_addr->sa_family != AF_INET ||
308a25eaedeSdyoung 	    dst0 == NULL || dst0->sa_family != AF_INET) {	/* Possible. */
309a25eaedeSdyoung 		ifa->ifa_seqno = NULL;
310a25eaedeSdyoung 		return ifa;
311a25eaedeSdyoung 	}
312a25eaedeSdyoung 
313a25eaedeSdyoung 	ifp = ifa->ifa_ifp;
314879526daSozaki-r 	KASSERT(ifp->if_afdata[AF_INET] != NULL);
315e22ecac8Sskrll 	isc = ((struct in_ifinfo *)(ifp)->if_afdata[AF_INET])->ii_selsrc;
316a25eaedeSdyoung 	if (isc != NULL && isc->isc_selsrc != NULL &&
317a25eaedeSdyoung 	    isc->isc_selsrc->iss_score_src[0] != NULL)
318a25eaedeSdyoung 		iss = isc->isc_selsrc;
319a25eaedeSdyoung 	else
320a25eaedeSdyoung 		iss = &default_iss;
321a25eaedeSdyoung 	score_src = &iss->iss_score_src[0];
322a25eaedeSdyoung 
323a25eaedeSdyoung 	dst = (const struct sockaddr_in *)dst0;
324a25eaedeSdyoung 
325a25eaedeSdyoung 	best_ifa = ifa;
326a25eaedeSdyoung 
327a25eaedeSdyoung 	/* Find out the index of this ifaddr. */
328a25eaedeSdyoung 	idx = 0;
329*9e4c2bdaSozaki-r 	IFADDR_READER_FOREACH(alt_ifa, ifa->ifa_ifp) {
330a25eaedeSdyoung 		if (alt_ifa == best_ifa)
331a25eaedeSdyoung 			break;
332a25eaedeSdyoung 		idx++;
333a25eaedeSdyoung 	}
334a25eaedeSdyoung 	in_score(score_src, best_score, &scorelen, &IA_SIN(best_ifa)->sin_addr,
335a25eaedeSdyoung 	    best_ifa->ifa_preference, idx, &dst->sin_addr);
336a25eaedeSdyoung 
337a25eaedeSdyoung #ifdef GETIFA_DEBUG
338a25eaedeSdyoung 	if (in_selsrc_debug) {
339a25eaedeSdyoung 		printf("%s: enter dst %#" PRIx32 " src %#" PRIx32 " score ",
340a25eaedeSdyoung 		    __func__, ntohl(dst->sin_addr.s_addr),
341a25eaedeSdyoung 		    ntohl(satosin(best_ifa->ifa_addr)->sin_addr.s_addr));
342a25eaedeSdyoung 		in_score_println(best_score, scorelen);
343a25eaedeSdyoung 	}
344a25eaedeSdyoung #endif /* GETIFA_DEBUG */
345a25eaedeSdyoung 
346a25eaedeSdyoung 	idx = -1;
347*9e4c2bdaSozaki-r 	IFADDR_READER_FOREACH(alt_ifa, ifa->ifa_ifp) {
348a25eaedeSdyoung 		++idx;
349a25eaedeSdyoung 		src = IA_SIN(alt_ifa);
350a25eaedeSdyoung 
351a25eaedeSdyoung 		if (alt_ifa == ifa || src->sin_family != AF_INET)
352a25eaedeSdyoung 			continue;
353505639d2Sroy 		ia = (struct in_ifaddr *)alt_ifa;
354505639d2Sroy 		if (ia->ia4_flags & IN_IFF_NOTREADY)
355505639d2Sroy 			continue;
356a25eaedeSdyoung 
357a25eaedeSdyoung 		in_score(score_src, score, NULL, &src->sin_addr,
358a25eaedeSdyoung 		         alt_ifa->ifa_preference, idx, &dst->sin_addr);
359a25eaedeSdyoung 
360a25eaedeSdyoung #ifdef GETIFA_DEBUG
361a25eaedeSdyoung 		if (in_selsrc_debug) {
362a25eaedeSdyoung 			printf("%s: src %#" PRIx32 " score ", __func__,
363a25eaedeSdyoung 			    ntohl(src->sin_addr.s_addr));
364a25eaedeSdyoung 			in_score_println(score, scorelen);
365a25eaedeSdyoung 		}
366a25eaedeSdyoung #endif /* GETIFA_DEBUG */
367a25eaedeSdyoung 
368a25eaedeSdyoung 		if (in_score_cmp(score, best_score, scorelen) > 0) {
369a25eaedeSdyoung 			(void)memcpy(best_score, score, sizeof(best_score));
370a25eaedeSdyoung 			best_ifa = alt_ifa;
371a25eaedeSdyoung 		}
372a25eaedeSdyoung 	}
373505639d2Sroy 
374505639d2Sroy 	ia = (struct in_ifaddr *)best_ifa;
375be5b0a3aSroy 	if (ia->ia4_flags & IN_IFF_NOTREADY)
376505639d2Sroy 		return NULL;
377505639d2Sroy 
378a25eaedeSdyoung #ifdef GETIFA_DEBUG
379a25eaedeSdyoung 	if (in_selsrc_debug) {
380a25eaedeSdyoung 		printf("%s: choose src %#" PRIx32 " score ", __func__,
381a25eaedeSdyoung 		    ntohl(IA_SIN(best_ifa)->sin_addr.s_addr));
382a25eaedeSdyoung 		in_score_println(best_score, scorelen);
383a25eaedeSdyoung 	}
384a25eaedeSdyoung #endif /* GETIFA_DEBUG */
385a25eaedeSdyoung 
386a25eaedeSdyoung 	best_ifa->ifa_seqno = &iss->iss_seqno;
387a25eaedeSdyoung 	return best_ifa;
388a25eaedeSdyoung }
389a25eaedeSdyoung 
390a25eaedeSdyoung static in_score_src_t
name_to_score_src(const char * name)391a25eaedeSdyoung name_to_score_src(const char *name)
392a25eaedeSdyoung {
393a25eaedeSdyoung 	int i;
394a25eaedeSdyoung 
395a25eaedeSdyoung 	for (i = 0; score_src_names[i].sn_name != NULL; i++) {
396a25eaedeSdyoung 		if (strcmp(score_src_names[i].sn_name, name) == 0)
397a25eaedeSdyoung 			return score_src_names[i].sn_score_src;
398a25eaedeSdyoung 	}
399a25eaedeSdyoung 	return NULL;
400a25eaedeSdyoung }
401a25eaedeSdyoung 
402a25eaedeSdyoung static const char *
score_src_to_name(const in_score_src_t score_src)403a25eaedeSdyoung score_src_to_name(const in_score_src_t score_src)
404a25eaedeSdyoung {
405a25eaedeSdyoung 	int i;
406a25eaedeSdyoung 	for (i = 0; score_src_names[i].sn_name != NULL; i++) {
407a25eaedeSdyoung 		if (score_src == score_src_names[i].sn_score_src)
408a25eaedeSdyoung 			return score_src_names[i].sn_name;
409a25eaedeSdyoung 	}
410a25eaedeSdyoung 	return "<unknown>";
411a25eaedeSdyoung }
412a25eaedeSdyoung 
413a25eaedeSdyoung static size_t
in_get_selectsrc(const struct in_ifselsrc * iss,char * buf0,const size_t buflen0)414a25eaedeSdyoung in_get_selectsrc(const struct in_ifselsrc *iss, char *buf0,
415a25eaedeSdyoung     const size_t buflen0)
416a25eaedeSdyoung {
417a25eaedeSdyoung 	int i, rc;
418a25eaedeSdyoung 	char *buf = buf0;
419a25eaedeSdyoung 	const char *delim;
420a25eaedeSdyoung 	size_t buflen = buflen0;
421a25eaedeSdyoung 
422a25eaedeSdyoung 	KASSERT(buflen >= 1);
423a25eaedeSdyoung 
424a25eaedeSdyoung 	for (delim = "", i = 0;
425a25eaedeSdyoung 	     i < IN_SCORE_SRC_MAX && iss->iss_score_src[i] != NULL;
426a25eaedeSdyoung 	     delim = ",", i++) {
427a25eaedeSdyoung 		rc = snprintf(buf, buflen, "%s%s",
428a25eaedeSdyoung 		    delim, score_src_to_name(iss->iss_score_src[i]));
429a25eaedeSdyoung 		if (rc == -1)
430a25eaedeSdyoung 			return buflen0 - buflen;
431a25eaedeSdyoung 		if (rc >= buflen)
432a25eaedeSdyoung 			return buflen0 + rc - buflen;
433a25eaedeSdyoung 		buf += rc;
434a25eaedeSdyoung 		buflen -= rc;
435a25eaedeSdyoung 	}
436a25eaedeSdyoung 	if (buf == buf0)
437a25eaedeSdyoung 		*buf++ = '\0';
438a25eaedeSdyoung 	return buf - buf0;
439a25eaedeSdyoung }
440a25eaedeSdyoung 
441a25eaedeSdyoung static int
in_set_selectsrc(struct in_ifselsrc * iss,char * buf)442a25eaedeSdyoung in_set_selectsrc(struct in_ifselsrc *iss, char *buf)
443a25eaedeSdyoung {
444a25eaedeSdyoung 	int i, s;
445a25eaedeSdyoung 	char *next = buf;
446a25eaedeSdyoung 	const char *name;
447a25eaedeSdyoung 	in_score_src_t score_src;
448a25eaedeSdyoung 	in_score_src_t scorers[IN_SCORE_SRC_MAX];
449a25eaedeSdyoung 
450a25eaedeSdyoung 	memset(&scorers, 0, sizeof(scorers));
451a25eaedeSdyoung 	for (i = 0;
452a25eaedeSdyoung 	     (name = strsep(&next, ",")) != NULL && i < IN_SCORE_SRC_MAX;
453a25eaedeSdyoung 	     i++) {
454a25eaedeSdyoung 		if (strcmp(name, "") == 0)
455a25eaedeSdyoung 			break;
456a25eaedeSdyoung 		if ((score_src = name_to_score_src(name)) == NULL)
457a25eaedeSdyoung 			return EINVAL;
458a25eaedeSdyoung 		scorers[i] = score_src;
459a25eaedeSdyoung 	}
460a25eaedeSdyoung 	if (i == IN_SCORE_SRC_MAX && name != NULL)
461a25eaedeSdyoung 		return EFBIG;
462a25eaedeSdyoung 	s = splnet();
463a25eaedeSdyoung 	(void)memcpy(iss->iss_score_src, scorers, sizeof(iss->iss_score_src));
464a25eaedeSdyoung         /* If iss affects a specific interface that used to use
465a25eaedeSdyoung          * the default policy, increase the sequence number on the
466a25eaedeSdyoung          * default policy, forcing routes that cache a source
467a25eaedeSdyoung          * (rt_ifa) found by the default policy to refresh their
468a25eaedeSdyoung          * cache.
469a25eaedeSdyoung 	 */
470a25eaedeSdyoung 	if (iss != &default_iss && iss->iss_score_src[0] == NULL &&
471a25eaedeSdyoung 	    scorers[0] != NULL)
472a25eaedeSdyoung 		default_iss.iss_seqno++;
473a25eaedeSdyoung 	iss->iss_seqno++;
474a25eaedeSdyoung 	splx(s);
475a25eaedeSdyoung 	return 0;
476a25eaedeSdyoung }
477a25eaedeSdyoung 
478a25eaedeSdyoung /*
479a25eaedeSdyoung  * sysctl helper routine for net.inet.ip.interfaces.<interface>.selectsrc.
480a25eaedeSdyoung  * Pulls the old value out as a human-readable string, interprets
481a25eaedeSdyoung  * and records the new value.
482a25eaedeSdyoung  */
483a25eaedeSdyoung static int
in_sysctl_selectsrc(SYSCTLFN_ARGS)484a25eaedeSdyoung in_sysctl_selectsrc(SYSCTLFN_ARGS)
485a25eaedeSdyoung {
486a25eaedeSdyoung 	char policy[IN_SELECTSRC_LEN];
487a25eaedeSdyoung 	int error;
488a25eaedeSdyoung 	struct sysctlnode node;
489a25eaedeSdyoung 	struct in_ifselsrc *iss;
490a25eaedeSdyoung 
491a25eaedeSdyoung 	node = *rnode;
492a25eaedeSdyoung 	iss = (struct in_ifselsrc *)node.sysctl_data;
493a25eaedeSdyoung 	if (oldp != NULL &&
494a25eaedeSdyoung 	    (error = in_get_selectsrc(iss, policy, sizeof(policy))) >= sizeof(policy))
495a25eaedeSdyoung 		return error;
496a25eaedeSdyoung 	node.sysctl_data = &policy[0];
497a25eaedeSdyoung 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
498a25eaedeSdyoung 	if (error || newp == NULL)
499a25eaedeSdyoung 		return (error);
500a25eaedeSdyoung 
501a25eaedeSdyoung 	return in_set_selectsrc(iss, policy);
502a25eaedeSdyoung }
503a25eaedeSdyoung 
504a25eaedeSdyoung static const struct sysctlnode *
in_domifattach_sysctl(struct in_ifsysctl * isc)505a25eaedeSdyoung in_domifattach_sysctl(struct in_ifsysctl *isc)
506a25eaedeSdyoung {
507a25eaedeSdyoung 	int rc;
508a25eaedeSdyoung 	const struct sysctlnode *rnode;
509a25eaedeSdyoung 
510a25eaedeSdyoung 	if ((rc = sysctl_createv(&isc->isc_log, 0, NULL, &rnode,
511ce7dbb45Sdyoung 	                         CTLFLAG_READONLY, CTLTYPE_NODE,
512a25eaedeSdyoung 				 "interfaces", NULL,
513a25eaedeSdyoung 				 NULL, 0, NULL, 0,
514a25eaedeSdyoung 				 CTL_NET, PF_INET, IPPROTO_IP, CTL_CREATE,
515a25eaedeSdyoung 				 CTL_EOL)) != 0) {
516a25eaedeSdyoung 		printf("%s: could not create net.inet.ip.interfaces, rc = %d\n",
517a25eaedeSdyoung 		    __func__, rc);
518a25eaedeSdyoung 		return NULL;
519a25eaedeSdyoung 	}
520a25eaedeSdyoung 	if ((rc = sysctl_createv(&isc->isc_log, 0, &rnode, &rnode,
521ce7dbb45Sdyoung 	                         CTLFLAG_READONLY, CTLTYPE_NODE,
522a25eaedeSdyoung 				 isc->isc_ifp->if_xname,
523a25eaedeSdyoung 				 SYSCTL_DESCR("interface ip options"),
524a25eaedeSdyoung 				 NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL)) != 0) {
525a25eaedeSdyoung 		printf("%s: could not create net.inet.ip.interfaces.%s, "
526a25eaedeSdyoung 		       "rc = %d\n", __func__, isc->isc_ifp->if_xname, rc);
527a25eaedeSdyoung 		goto err;
528a25eaedeSdyoung 	}
529a25eaedeSdyoung 	if ((rc = sysctl_createv(&isc->isc_log, 0, &rnode, &rnode,
530a25eaedeSdyoung 	                         CTLFLAG_READWRITE, CTLTYPE_STRING,
531a25eaedeSdyoung 				 "selectsrc",
532a25eaedeSdyoung 				 SYSCTL_DESCR("source selection policy"),
533a25eaedeSdyoung 				 in_sysctl_selectsrc, 0,
534e21a34c2Sdsl 				 (void *)isc->isc_selsrc, IN_SELECTSRC_LEN,
535a25eaedeSdyoung 				 CTL_CREATE, CTL_EOL)) != 0) {
536a25eaedeSdyoung 		printf(
537a25eaedeSdyoung 		    "%s: could not create net.inet.ip.%s.selectsrc, rc = %d\n",
538a25eaedeSdyoung 		    __func__, isc->isc_ifp->if_xname, rc);
539a25eaedeSdyoung 		goto err;
540a25eaedeSdyoung 	}
541a25eaedeSdyoung 	return rnode;
542a25eaedeSdyoung err:
543a25eaedeSdyoung 	sysctl_teardown(&isc->isc_log);
544a25eaedeSdyoung 	return NULL;
545a25eaedeSdyoung }
546a25eaedeSdyoung 
547a25eaedeSdyoung void *
in_selsrc_domifattach(struct ifnet * ifp)548879526daSozaki-r in_selsrc_domifattach(struct ifnet *ifp)
549a25eaedeSdyoung {
550a25eaedeSdyoung 	struct in_ifsysctl *isc;
551a25eaedeSdyoung 	struct in_ifselsrc *iss;
552a25eaedeSdyoung 
553a25eaedeSdyoung 	isc = (struct in_ifsysctl *)malloc(sizeof(*isc), M_IFADDR,
554a25eaedeSdyoung 	    M_WAITOK | M_ZERO);
555a25eaedeSdyoung 
556a25eaedeSdyoung 	iss = (struct in_ifselsrc *)malloc(sizeof(*iss), M_IFADDR,
557a25eaedeSdyoung 	    M_WAITOK | M_ZERO);
558a25eaedeSdyoung 
559a25eaedeSdyoung 	memcpy(&iss->iss_score_src[0], &initial_iss.iss_score_src[0],
560a25eaedeSdyoung 	    MIN(sizeof(iss->iss_score_src), sizeof(initial_iss.iss_score_src)));
561a25eaedeSdyoung 
562a25eaedeSdyoung 	isc->isc_ifp = ifp;
563a25eaedeSdyoung 	isc->isc_selsrc = iss;
564a25eaedeSdyoung 
565a25eaedeSdyoung 	if (in_domifattach_sysctl(isc) == NULL)
566a25eaedeSdyoung 		goto err;
567a25eaedeSdyoung 
568a25eaedeSdyoung 	return isc;
569a25eaedeSdyoung err:
570a25eaedeSdyoung 	free(iss, M_IFADDR);
571a25eaedeSdyoung 	free(isc, M_IFADDR);
572a25eaedeSdyoung 	return NULL;
573a25eaedeSdyoung }
574a25eaedeSdyoung 
575a25eaedeSdyoung void
in_selsrc_domifdetach(struct ifnet * ifp,void * aux)576879526daSozaki-r in_selsrc_domifdetach(struct ifnet *ifp, void *aux)
577a25eaedeSdyoung {
578a25eaedeSdyoung 	struct in_ifsysctl *isc;
5792d1d7071Sdyoung 	struct in_ifselsrc *iss;
580a25eaedeSdyoung 
581a25eaedeSdyoung 	if (aux == NULL)
582a25eaedeSdyoung 		return;
583a25eaedeSdyoung 	isc = (struct in_ifsysctl *)aux;
5842d1d7071Sdyoung 	iss = isc->isc_selsrc;
585a25eaedeSdyoung 	sysctl_teardown(&isc->isc_log);
586a25eaedeSdyoung 	free(isc, M_IFADDR);
5872d1d7071Sdyoung 	free(iss, M_IFADDR);
588a25eaedeSdyoung }
589a25eaedeSdyoung #endif /* INET */
590