1 /*-
2 * Copyright (c) 2014 The NetBSD Foundation, Inc.
3 * All rights reserved.
4 *
5 * This code is derived from software contributed to The NetBSD Foundation
6 * by Mindaugas Rasiukevicius.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
21 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 /*
31 * NPF network interface handling module.
32 */
33
34 #ifdef _KERNEL
35 #include <sys/cdefs.h>
36 __KERNEL_RCSID(0, "$NetBSD: npf_ifaddr.c,v 1.8 2022/02/13 19:20:11 riastradh Exp $");
37
38 #include <sys/param.h>
39 #include <sys/types.h>
40 #include <sys/kmem.h>
41
42 #include <net/if.h>
43 #include <netinet/in.h>
44 #include <netinet6/in6_var.h>
45 #endif
46
47 #include "npf_impl.h"
48
49 static npf_table_t *
lookup_ifnet_table(npf_t * npf,ifnet_t * ifp)50 lookup_ifnet_table(npf_t *npf, ifnet_t *ifp)
51 {
52 const npf_ifops_t *ifops = npf->ifops;
53 char tname[NPF_TABLE_MAXNAMELEN];
54 const char *ifname;
55 npf_config_t *nc;
56 npf_table_t *t;
57 unsigned tid;
58
59 /* Get the interface name and prefix it. */
60 ifname = ifops->getname(npf, ifp);
61 snprintf(tname, sizeof(tname), ".ifnet-%s", ifname);
62
63 KERNEL_LOCK(1, NULL);
64 nc = npf_config_enter(npf);
65
66 /*
67 * Check whether this interface is of any interest to us.
68 */
69 t = npf_tableset_getbyname(nc->tableset, tname);
70 if (!t) {
71 goto out;
72 }
73 tid = npf_table_getid(t);
74
75 /* Create a new NPF table for the interface. */
76 t = npf_table_create(tname, tid, NPF_TABLE_IFADDR, NULL, 0);
77 if (!t) {
78 goto out;
79 }
80 return t;
81 out:
82 npf_config_exit(npf);
83 KERNEL_UNLOCK_ONE(NULL);
84 return NULL;
85 }
86
87 static void
replace_ifnet_table(npf_t * npf,npf_table_t * newt)88 replace_ifnet_table(npf_t *npf, npf_table_t *newt)
89 {
90 npf_tableset_t *ts = atomic_load_relaxed(&npf->config)->tableset;
91 npf_table_t *oldt;
92
93 KASSERT(npf_config_locked_p(npf));
94
95 KERNEL_UNLOCK_ONE(NULL);
96
97 /*
98 * Finally, swap the tables and issue a sync barrier.
99 */
100 oldt = npf_tableset_swap(ts, newt);
101 npf_config_sync(npf);
102 npf_config_exit(npf);
103
104 /* At this point, it is safe to destroy the old table. */
105 npf_table_destroy(oldt);
106 }
107
108 void
npf_ifaddr_sync(npf_t * npf,ifnet_t * ifp)109 npf_ifaddr_sync(npf_t *npf, ifnet_t *ifp)
110 {
111 npf_table_t *t;
112 struct ifaddr *ifa;
113
114 /*
115 * First, check whether this interface is of any interest to us.
116 *
117 * => Acquires npf-config-lock and kernel-lock on success.
118 */
119 t = lookup_ifnet_table(npf, ifp);
120 if (!t)
121 return;
122
123 /*
124 * Populate the table with the interface addresses.
125 * Note: currently, this list is protected by the kernel-lock.
126 */
127 IFADDR_FOREACH(ifa, ifp) {
128 struct sockaddr *sa = ifa->ifa_addr;
129 const void *p = NULL;
130 int alen = 0;
131
132 if (sa->sa_family == AF_INET) {
133 const struct sockaddr_in *sin4 = satosin(sa);
134 alen = sizeof(struct in_addr);
135 p = &sin4->sin_addr;
136 }
137 if (sa->sa_family == AF_INET6) {
138 const struct sockaddr_in6 *sin6 = satosin6(sa);
139 alen = sizeof(struct in6_addr);
140 p = &sin6->sin6_addr;
141 }
142 if (alen) {
143 npf_addr_t addr;
144 memcpy(&addr, p, alen);
145 npf_table_insert(t, alen, &addr, NPF_NO_NETMASK);
146 }
147 }
148
149 /* Publish the new table. */
150 replace_ifnet_table(npf, t);
151 }
152
153 void
npf_ifaddr_flush(npf_t * npf,ifnet_t * ifp)154 npf_ifaddr_flush(npf_t *npf, ifnet_t *ifp)
155 {
156 npf_table_t *t;
157
158 /*
159 * Flush: just load an empty table.
160 */
161 t = lookup_ifnet_table(npf, ifp);
162 if (!t) {
163 return;
164 }
165 replace_ifnet_table(npf, t);
166 }
167
168 void
npf_ifaddr_syncall(npf_t * npf)169 npf_ifaddr_syncall(npf_t *npf)
170 {
171 ifnet_t *ifp;
172
173 KERNEL_LOCK(1, NULL);
174 IFNET_GLOBAL_LOCK();
175 IFNET_WRITER_FOREACH(ifp) {
176 npf_ifaddr_sync(npf, ifp);
177 }
178 IFNET_GLOBAL_UNLOCK();
179 KERNEL_UNLOCK_ONE(NULL);
180 }
181