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