Commit | Line | Data |
---|---|---|
ed1f0be2 | 1 | /* $OpenBSD: pf_if.c,v 1.54 2008/06/14 16:55:28 mk Exp $ */ |
02742ec6 JS |
2 | |
3 | /* | |
70224baa JL |
4 | * Copyright 2005 Henning Brauer <henning@openbsd.org> |
5 | * Copyright 2005 Ryan McBride <mcbride@openbsd.org> | |
02742ec6 JS |
6 | * Copyright (c) 2001 Daniel Hartmeier |
7 | * Copyright (c) 2003 Cedric Berger | |
8 | * All rights reserved. | |
9 | * | |
10 | * Redistribution and use in source and binary forms, with or without | |
11 | * modification, are permitted provided that the following conditions | |
12 | * are met: | |
13 | * | |
14 | * - Redistributions of source code must retain the above copyright | |
15 | * notice, this list of conditions and the following disclaimer. | |
16 | * - Redistributions in binary form must reproduce the above | |
17 | * copyright notice, this list of conditions and the following | |
18 | * disclaimer in the documentation and/or other materials provided | |
19 | * with the distribution. | |
20 | * | |
21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
22 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
23 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS | |
24 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE | |
25 | * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, | |
26 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, | |
27 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
28 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | |
29 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
30 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN | |
31 | * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |
32 | * POSSIBILITY OF SUCH DAMAGE. | |
33 | */ | |
34 | ||
35 | #include "opt_inet.h" | |
36 | #include "opt_inet6.h" | |
37 | ||
38 | #include <sys/param.h> | |
39 | #include <sys/systm.h> | |
40 | #include <sys/malloc.h> | |
41 | #include <sys/mbuf.h> | |
42 | #include <sys/eventhandler.h> | |
43 | #include <sys/filio.h> | |
9a74b592 | 44 | #include <sys/msgport2.h> |
02742ec6 JS |
45 | #include <sys/socket.h> |
46 | #include <sys/socketvar.h> | |
47 | #include <sys/kernel.h> | |
cc6e5672 | 48 | #include <sys/thread2.h> |
02742ec6 | 49 | #include <sys/time.h> |
c686757e | 50 | #include <sys/lock.h> |
02742ec6 JS |
51 | |
52 | #include <net/if.h> | |
c686757e | 53 | #include <net/if_var.h> |
02742ec6 | 54 | #include <net/if_types.h> |
9a74b592 SZ |
55 | #include <net/netisr2.h> |
56 | #include <net/netmsg2.h> | |
e4fc90e8 | 57 | #include <net/route.h> |
02742ec6 JS |
58 | |
59 | #include <netinet/in.h> | |
60 | #include <netinet/in_var.h> | |
61 | #include <netinet/in_systm.h> | |
62 | #include <netinet/ip.h> | |
63 | #include <netinet/ip_var.h> | |
64 | ||
65 | #include <net/pf/pfvar.h> | |
66 | ||
67 | #ifdef INET6 | |
68 | #include <netinet/ip6.h> | |
69 | #endif /* INET6 */ | |
70 | ||
c686757e AL |
71 | struct pfi_kif *pfi_all = NULL; |
72 | struct pfi_ifhead pfi_ifs; | |
73 | static long pfi_update = 1; | |
74 | static struct pfr_addr *pfi_buffer; | |
75 | static int pfi_buffer_cnt; | |
76 | static int pfi_buffer_max; | |
77 | ||
78 | static eventhandler_tag pfi_attach_cookie; | |
79 | static eventhandler_tag pfi_detach_cookie; | |
80 | static eventhandler_tag pfi_attach_group_cookie; | |
81 | static eventhandler_tag pfi_detach_group_cookie; | |
82 | static eventhandler_tag pfi_change_group_cookie; | |
83 | static eventhandler_tag pfi_ifaddr_event_cookie; | |
84 | ||
85 | static void pfi_kif_update(struct pfi_kif *); | |
2949c680 | 86 | static void pfi_dynaddr_update(struct pfi_dynaddr *); |
c686757e | 87 | static void pfi_table_update(struct pfr_ktable *, struct pfi_kif *, |
02742ec6 | 88 | int, int); |
c686757e AL |
89 | static void pfi_instance_add(struct ifnet *, int, int); |
90 | static void pfi_address_add(struct sockaddr *, int, int); | |
91 | static int pfi_if_compare(struct pfi_kif *, struct pfi_kif *); | |
92 | static int pfi_skip_if(const char *, struct pfi_kif *); | |
93 | static int pfi_unmask(void *); | |
94 | ||
95 | static void pfi_attach_ifnet_event(void * __unused, struct ifnet *); | |
96 | static void pfi_detach_ifnet_event(void * __unused, struct ifnet *); | |
97 | static void pfi_attach_group_event(void * __unused, struct ifg_group *); | |
98 | static void pfi_detach_group_event(void * __unused, struct ifg_group *); | |
99 | static void pfi_change_group_event(void * __unused, char *); | |
100 | static void pfi_ifaddr_event(void * __unused, struct ifnet *, | |
101 | enum ifaddr_event __unused, struct ifaddr * __unused); | |
102 | ||
103 | static RB_PROTOTYPE(pfi_ifhead, pfi_kif, pfik_tree, pfi_if_compare); | |
104 | static RB_GENERATE(pfi_ifhead, pfi_kif, pfik_tree, pfi_if_compare); | |
02742ec6 | 105 | |
02742ec6 | 106 | #define PFI_BUFFER_MAX 0x10000 |
c686757e | 107 | static MALLOC_DEFINE(M_PFI, "pf_if", "pf interface"); |
1186cbc0 | 108 | |
02742ec6 JS |
109 | |
110 | void | |
111 | pfi_initialize(void) | |
112 | { | |
c686757e AL |
113 | struct ifnet *ifp; |
114 | struct ifg_group *ifg; | |
02742ec6 | 115 | |
c686757e AL |
116 | pfi_attach_cookie = EVENTHANDLER_REGISTER(ifnet_attach_event, |
117 | pfi_attach_ifnet_event, NULL, EVENTHANDLER_PRI_ANY); | |
118 | pfi_detach_cookie = EVENTHANDLER_REGISTER(ifnet_detach_event, | |
119 | pfi_detach_ifnet_event, NULL, EVENTHANDLER_PRI_ANY); | |
120 | pfi_attach_group_cookie = EVENTHANDLER_REGISTER(group_attach_event, | |
121 | pfi_attach_group_event, NULL, EVENTHANDLER_PRI_ANY); | |
122 | pfi_detach_group_cookie = EVENTHANDLER_REGISTER(group_detach_event, | |
123 | pfi_detach_group_event, NULL, EVENTHANDLER_PRI_ANY); | |
124 | pfi_change_group_cookie = EVENTHANDLER_REGISTER(group_change_event, | |
125 | pfi_change_group_event, NULL, EVENTHANDLER_PRI_ANY); | |
126 | pfi_ifaddr_event_cookie = EVENTHANDLER_REGISTER(ifaddr_event, | |
127 | pfi_ifaddr_event, NULL, EVENTHANDLER_PRI_ANY); | |
02742ec6 | 128 | |
02742ec6 | 129 | pfi_buffer_max = 64; |
77652cad | 130 | pfi_buffer = kmalloc(pfi_buffer_max * sizeof(*pfi_buffer), |
c686757e | 131 | M_PFI, M_WAITOK); |
70224baa JL |
132 | |
133 | if ((pfi_all = pfi_kif_get(IFG_ALL)) == NULL) | |
c686757e AL |
134 | panic("%s: pfi_kif_get(IFG_ALL) failed", __func__); |
135 | ||
136 | ifgroup_lockmgr(LK_SHARED); | |
137 | TAILQ_FOREACH(ifg, &ifg_head, ifg_next) | |
138 | pfi_attach_ifgroup(ifg); | |
139 | ifgroup_lockmgr(LK_RELEASE); | |
02742ec6 | 140 | |
b4051e25 SZ |
141 | /* XXX ALMOST MPSAFE */ |
142 | ifnet_lock(); | |
c686757e AL |
143 | TAILQ_FOREACH(ifp, &ifnetlist, if_link) |
144 | pfi_attach_ifnet(ifp); | |
b4051e25 | 145 | ifnet_unlock(); |
02742ec6 JS |
146 | } |
147 | ||
148 | void | |
149 | pfi_cleanup(void) | |
150 | { | |
c686757e | 151 | struct pfi_kif *kif; |
02742ec6 | 152 | |
c686757e AL |
153 | while ((kif = RB_MIN(pfi_ifhead, &pfi_ifs))) { |
154 | RB_REMOVE(pfi_ifhead, &pfi_ifs, kif); | |
155 | if (kif->pfik_group) | |
156 | kif->pfik_group->ifg_pf_kif = NULL; | |
157 | if (kif->pfik_ifp) | |
158 | kif->pfik_ifp->if_pf_kif = NULL; | |
159 | kfree(kif, M_PFI); | |
02742ec6 | 160 | } |
c686757e | 161 | kfree(pfi_buffer, M_PFI); |
02742ec6 | 162 | pfi_buffer = NULL; |
70224baa | 163 | pfi_all = NULL; |
5bd4422e | 164 | |
c686757e AL |
165 | EVENTHANDLER_DEREGISTER(ifnet_attach_event, pfi_attach_cookie); |
166 | EVENTHANDLER_DEREGISTER(ifnet_detach_event, pfi_detach_cookie); | |
167 | EVENTHANDLER_DEREGISTER(group_attach_event, pfi_attach_group_cookie); | |
168 | EVENTHANDLER_DEREGISTER(group_detach_event, pfi_detach_group_cookie); | |
169 | EVENTHANDLER_DEREGISTER(group_change_event, pfi_change_group_cookie); | |
170 | EVENTHANDLER_DEREGISTER(ifaddr_event, pfi_ifaddr_event_cookie); | |
02742ec6 JS |
171 | } |
172 | ||
c686757e AL |
173 | struct pfi_kif * |
174 | pfi_kif_find(const char *kif_name) | |
02742ec6 | 175 | { |
c686757e | 176 | struct pfi_kif_cmp s; |
02742ec6 | 177 | |
c686757e AL |
178 | bzero(&s, sizeof(s)); |
179 | strlcpy(s.pfik_name, kif_name, sizeof(s.pfik_name)); | |
02742ec6 | 180 | |
c686757e | 181 | return (RB_FIND(pfi_ifhead, &pfi_ifs, (struct pfi_kif *)&s)); |
02742ec6 JS |
182 | } |
183 | ||
70224baa JL |
184 | struct pfi_kif * |
185 | pfi_kif_get(const char *kif_name) | |
186 | { | |
c686757e | 187 | struct pfi_kif *kif; |
70224baa | 188 | |
c686757e | 189 | if ((kif = pfi_kif_find(kif_name))) |
70224baa JL |
190 | return (kif); |
191 | ||
c686757e AL |
192 | /* |
193 | * Create a new one | |
194 | */ | |
195 | kif = kmalloc(sizeof(*kif), M_PFI, M_WAITOK | M_ZERO); | |
70224baa JL |
196 | strlcpy(kif->pfik_name, kif_name, sizeof(kif->pfik_name)); |
197 | kif->pfik_tzero = time_second; | |
198 | TAILQ_INIT(&kif->pfik_dynaddrs); | |
199 | ||
200 | RB_INSERT(pfi_ifhead, &pfi_ifs, kif); | |
201 | return (kif); | |
202 | } | |
203 | ||
204 | void | |
205 | pfi_kif_ref(struct pfi_kif *kif, enum pfi_kif_refs what) | |
206 | { | |
207 | switch (what) { | |
208 | case PFI_KIF_REF_RULE: | |
209 | kif->pfik_rules++; | |
210 | break; | |
211 | case PFI_KIF_REF_STATE: | |
315a7da3 | 212 | kif->pfik_states++; |
70224baa JL |
213 | break; |
214 | default: | |
c686757e | 215 | panic("%s: unknown type", __func__); |
70224baa JL |
216 | } |
217 | } | |
218 | ||
02742ec6 | 219 | void |
70224baa | 220 | pfi_kif_unref(struct pfi_kif *kif, enum pfi_kif_refs what) |
02742ec6 | 221 | { |
70224baa JL |
222 | if (kif == NULL) |
223 | return; | |
224 | ||
225 | switch (what) { | |
226 | case PFI_KIF_REF_NONE: | |
227 | break; | |
228 | case PFI_KIF_REF_RULE: | |
229 | if (kif->pfik_rules <= 0) { | |
c686757e | 230 | kprintf("%s: rules refcount <= 0\n", __func__); |
70224baa JL |
231 | return; |
232 | } | |
233 | kif->pfik_rules--; | |
234 | break; | |
235 | case PFI_KIF_REF_STATE: | |
236 | if (kif->pfik_states <= 0) { | |
c686757e | 237 | kprintf("%s: state refcount <= 0\n", __func__); |
70224baa JL |
238 | return; |
239 | } | |
315a7da3 | 240 | kif->pfik_states--; |
70224baa JL |
241 | break; |
242 | default: | |
c686757e | 243 | panic("%s: unknown type", __func__); |
70224baa JL |
244 | } |
245 | ||
246 | if (kif->pfik_ifp != NULL || kif->pfik_group != NULL || kif == pfi_all) | |
247 | return; | |
248 | ||
249 | if (kif->pfik_rules || kif->pfik_states) | |
250 | return; | |
251 | ||
252 | RB_REMOVE(pfi_ifhead, &pfi_ifs, kif); | |
c686757e | 253 | kfree(kif, M_PFI); |
70224baa JL |
254 | } |
255 | ||
256 | int | |
257 | pfi_kif_match(struct pfi_kif *rule_kif, struct pfi_kif *packet_kif) | |
258 | { | |
259 | struct ifg_list *p; | |
260 | ||
261 | if (rule_kif == NULL || rule_kif == packet_kif) | |
262 | return (1); | |
263 | ||
c686757e AL |
264 | if (rule_kif->pfik_group != NULL) { |
265 | ifgroup_lockmgr(LK_SHARED); | |
266 | TAILQ_FOREACH(p, &packet_kif->pfik_ifp->if_groups, ifgl_next) { | |
267 | if (p->ifgl_group == rule_kif->pfik_group) { | |
268 | ifgroup_lockmgr(LK_RELEASE); | |
70224baa | 269 | return (1); |
c686757e AL |
270 | } |
271 | } | |
272 | ifgroup_lockmgr(LK_RELEASE); | |
273 | } | |
70224baa JL |
274 | |
275 | return (0); | |
02742ec6 JS |
276 | } |
277 | ||
278 | void | |
279 | pfi_attach_ifnet(struct ifnet *ifp) | |
280 | { | |
c686757e AL |
281 | struct pfi_kif *kif; |
282 | ||
283 | if (ifp->if_dunit == IF_DUNIT_NONE) | |
284 | return; | |
02742ec6 | 285 | |
cc6e5672 | 286 | crit_enter(); |
02742ec6 | 287 | pfi_update++; |
70224baa | 288 | if ((kif = pfi_kif_get(ifp->if_xname)) == NULL) |
c686757e | 289 | panic("%s: pfi_kif_get failed", __func__); |
70224baa | 290 | kif->pfik_ifp = ifp; |
c686757e | 291 | ifp->if_pf_kif = kif; |
70224baa | 292 | pfi_kif_update(kif); |
cc6e5672 | 293 | crit_exit(); |
02742ec6 JS |
294 | } |
295 | ||
296 | void | |
297 | pfi_detach_ifnet(struct ifnet *ifp) | |
298 | { | |
c686757e | 299 | struct pfi_kif *kif; |
02742ec6 | 300 | |
70224baa JL |
301 | if ((kif = (struct pfi_kif *)ifp->if_pf_kif) == NULL) |
302 | return; | |
ed1f0be2 | 303 | |
cc6e5672 | 304 | crit_enter(); |
02742ec6 | 305 | pfi_update++; |
70224baa | 306 | pfi_kif_update(kif); |
70224baa JL |
307 | kif->pfik_ifp = NULL; |
308 | ifp->if_pf_kif = NULL; | |
309 | pfi_kif_unref(kif, PFI_KIF_REF_NONE); | |
cc6e5672 | 310 | crit_exit(); |
02742ec6 JS |
311 | } |
312 | ||
70224baa JL |
313 | void |
314 | pfi_attach_ifgroup(struct ifg_group *ifg) | |
02742ec6 | 315 | { |
c686757e | 316 | struct pfi_kif *kif; |
02742ec6 | 317 | |
cc6e5672 | 318 | crit_enter(); |
70224baa JL |
319 | pfi_update++; |
320 | if ((kif = pfi_kif_get(ifg->ifg_group)) == NULL) | |
c686757e | 321 | panic("%s: pfi_kif_get failed", __func__); |
70224baa | 322 | kif->pfik_group = ifg; |
c686757e | 323 | ifg->ifg_pf_kif = kif; |
70224baa | 324 | crit_exit(); |
02742ec6 JS |
325 | } |
326 | ||
327 | void | |
70224baa | 328 | pfi_detach_ifgroup(struct ifg_group *ifg) |
02742ec6 | 329 | { |
c686757e | 330 | struct pfi_kif *kif; |
70224baa JL |
331 | |
332 | if ((kif = (struct pfi_kif *)ifg->ifg_pf_kif) == NULL) | |
02742ec6 | 333 | return; |
70224baa JL |
334 | |
335 | crit_enter(); | |
336 | pfi_update++; | |
70224baa JL |
337 | kif->pfik_group = NULL; |
338 | ifg->ifg_pf_kif = NULL; | |
339 | pfi_kif_unref(kif, PFI_KIF_REF_NONE); | |
340 | crit_exit(); | |
02742ec6 JS |
341 | } |
342 | ||
343 | void | |
70224baa | 344 | pfi_group_change(const char *group) |
02742ec6 | 345 | { |
c686757e | 346 | struct pfi_kif *kif; |
70224baa JL |
347 | |
348 | crit_enter(); | |
349 | pfi_update++; | |
350 | if ((kif = pfi_kif_get(group)) == NULL) | |
c686757e | 351 | panic("%s: pfi_kif_get failed", __func__); |
70224baa | 352 | pfi_kif_update(kif); |
70224baa | 353 | crit_exit(); |
02742ec6 JS |
354 | } |
355 | ||
70224baa JL |
356 | int |
357 | pfi_match_addr(struct pfi_dynaddr *dyn, struct pf_addr *a, sa_family_t af) | |
02742ec6 | 358 | { |
70224baa JL |
359 | switch (af) { |
360 | #ifdef INET | |
361 | case AF_INET: | |
362 | switch (dyn->pfid_acnt4) { | |
363 | case 0: | |
364 | return (0); | |
365 | case 1: | |
366 | return (PF_MATCHA(0, &dyn->pfid_addr4, | |
367 | &dyn->pfid_mask4, a, AF_INET)); | |
368 | default: | |
369 | return (pfr_match_addr(dyn->pfid_kt, a, AF_INET)); | |
370 | } | |
371 | break; | |
372 | #endif /* INET */ | |
373 | #ifdef INET6 | |
374 | case AF_INET6: | |
375 | switch (dyn->pfid_acnt6) { | |
376 | case 0: | |
377 | return (0); | |
378 | case 1: | |
379 | return (PF_MATCHA(0, &dyn->pfid_addr6, | |
380 | &dyn->pfid_mask6, a, AF_INET6)); | |
381 | default: | |
382 | return (pfr_match_addr(dyn->pfid_kt, a, AF_INET6)); | |
383 | } | |
384 | break; | |
385 | #endif /* INET6 */ | |
386 | default: | |
387 | return (0); | |
02742ec6 | 388 | } |
02742ec6 JS |
389 | } |
390 | ||
391 | int | |
392 | pfi_dynaddr_setup(struct pf_addr_wrap *aw, sa_family_t af) | |
393 | { | |
394 | struct pfi_dynaddr *dyn; | |
395 | char tblname[PF_TABLE_NAME_SIZE]; | |
396 | struct pf_ruleset *ruleset = NULL; | |
cc6e5672 | 397 | int rv = 0; |
02742ec6 JS |
398 | |
399 | if (aw->type != PF_ADDR_DYNIFTL) | |
400 | return (0); | |
c686757e AL |
401 | if ((dyn = kmalloc(sizeof(struct pfi_dynaddr), M_PFI, |
402 | M_WAITOK | M_NULLOK | M_ZERO)) == NULL) { | |
02742ec6 | 403 | return (1); |
c686757e | 404 | } |
02742ec6 | 405 | |
cc6e5672 | 406 | crit_enter(); |
c686757e | 407 | if (strcmp(aw->v.ifname, "self") == 0) |
70224baa JL |
408 | dyn->pfid_kif = pfi_kif_get(IFG_ALL); |
409 | else | |
410 | dyn->pfid_kif = pfi_kif_get(aw->v.ifname); | |
411 | if (dyn->pfid_kif == NULL) { | |
412 | rv = 1; | |
413 | goto _bad; | |
414 | } | |
415 | pfi_kif_ref(dyn->pfid_kif, PFI_KIF_REF_RULE); | |
02742ec6 JS |
416 | |
417 | dyn->pfid_net = pfi_unmask(&aw->v.a.mask); | |
418 | if (af == AF_INET && dyn->pfid_net == 32) | |
419 | dyn->pfid_net = 128; | |
420 | strlcpy(tblname, aw->v.ifname, sizeof(tblname)); | |
421 | if (aw->iflags & PFI_AFLAG_NETWORK) | |
422 | strlcat(tblname, ":network", sizeof(tblname)); | |
423 | if (aw->iflags & PFI_AFLAG_BROADCAST) | |
424 | strlcat(tblname, ":broadcast", sizeof(tblname)); | |
425 | if (aw->iflags & PFI_AFLAG_PEER) | |
426 | strlcat(tblname, ":peer", sizeof(tblname)); | |
427 | if (aw->iflags & PFI_AFLAG_NOALIAS) | |
428 | strlcat(tblname, ":0", sizeof(tblname)); | |
429 | if (dyn->pfid_net != 128) | |
f8c7a42d | 430 | ksnprintf(tblname + strlen(tblname), |
02742ec6 | 431 | sizeof(tblname) - strlen(tblname), "/%d", dyn->pfid_net); |
70224baa JL |
432 | if ((ruleset = pf_find_or_create_ruleset(PF_RESERVED_ANCHOR)) == NULL) { |
433 | rv = 1; | |
434 | goto _bad; | |
435 | } | |
02742ec6 | 436 | |
70224baa JL |
437 | if ((dyn->pfid_kt = pfr_attach_table(ruleset, tblname)) == NULL) { |
438 | rv = 1; | |
439 | goto _bad; | |
440 | } | |
02742ec6 JS |
441 | |
442 | dyn->pfid_kt->pfrkt_flags |= PFR_TFLAG_ACTIVE; | |
443 | dyn->pfid_iflags = aw->iflags; | |
444 | dyn->pfid_af = af; | |
02742ec6 | 445 | |
70224baa | 446 | TAILQ_INSERT_TAIL(&dyn->pfid_kif->pfik_dynaddrs, dyn, entry); |
02742ec6 | 447 | aw->p.dyn = dyn; |
70224baa | 448 | pfi_kif_update(dyn->pfid_kif); |
cc6e5672 | 449 | crit_exit(); |
02742ec6 JS |
450 | return (0); |
451 | ||
452 | _bad: | |
453 | if (dyn->pfid_kt != NULL) | |
454 | pfr_detach_table(dyn->pfid_kt); | |
455 | if (ruleset != NULL) | |
456 | pf_remove_if_empty_ruleset(ruleset); | |
457 | if (dyn->pfid_kif != NULL) | |
70224baa | 458 | pfi_kif_unref(dyn->pfid_kif, PFI_KIF_REF_RULE); |
c686757e | 459 | kfree(dyn, M_PFI); |
cc6e5672 | 460 | crit_exit(); |
02742ec6 JS |
461 | return (rv); |
462 | } | |
463 | ||
c686757e | 464 | static void |
70224baa | 465 | pfi_kif_update(struct pfi_kif *kif) |
02742ec6 | 466 | { |
70224baa JL |
467 | struct ifg_list *ifgl; |
468 | struct pfi_dynaddr *p; | |
02742ec6 | 469 | |
70224baa JL |
470 | /* update all dynaddr */ |
471 | TAILQ_FOREACH(p, &kif->pfik_dynaddrs, entry) | |
472 | pfi_dynaddr_update(p); | |
473 | ||
c686757e AL |
474 | /* and for all groups that the kif belongs to */ |
475 | if (kif->pfik_ifp != NULL) { | |
476 | ifgroup_lockmgr(LK_SHARED); | |
477 | TAILQ_FOREACH(ifgl, &kif->pfik_ifp->if_groups, ifgl_next) { | |
70224baa JL |
478 | pfi_kif_update((struct pfi_kif *) |
479 | ifgl->ifgl_group->ifg_pf_kif); | |
c686757e AL |
480 | } |
481 | ifgroup_lockmgr(LK_RELEASE); | |
482 | } | |
70224baa JL |
483 | } |
484 | ||
c686757e | 485 | static void |
70224baa JL |
486 | pfi_dynaddr_update(struct pfi_dynaddr *dyn) |
487 | { | |
488 | struct pfi_kif *kif; | |
489 | struct pfr_ktable *kt; | |
490 | ||
491 | if (dyn == NULL || dyn->pfid_kif == NULL || dyn->pfid_kt == NULL) | |
c686757e | 492 | panic("%s: bad argument", __func__); |
70224baa JL |
493 | |
494 | kif = dyn->pfid_kif; | |
495 | kt = dyn->pfid_kt; | |
496 | ||
02742ec6 JS |
497 | if (kt->pfrkt_larg != pfi_update) { |
498 | /* this table needs to be brought up-to-date */ | |
499 | pfi_table_update(kt, kif, dyn->pfid_net, dyn->pfid_iflags); | |
500 | kt->pfrkt_larg = pfi_update; | |
501 | } | |
502 | pfr_dynaddr_update(kt, dyn); | |
503 | } | |
504 | ||
c686757e | 505 | static void |
02742ec6 JS |
506 | pfi_table_update(struct pfr_ktable *kt, struct pfi_kif *kif, int net, int flags) |
507 | { | |
508 | int e, size2 = 0; | |
70224baa | 509 | struct ifg_member *ifgm; |
02742ec6 | 510 | |
02742ec6 | 511 | pfi_buffer_cnt = 0; |
70224baa | 512 | |
c686757e | 513 | if (kif->pfik_ifp != NULL) { |
02742ec6 | 514 | pfi_instance_add(kif->pfik_ifp, net, flags); |
c686757e AL |
515 | } else if (kif->pfik_group != NULL) { |
516 | ifgroup_lockmgr(LK_SHARED); | |
70224baa JL |
517 | TAILQ_FOREACH(ifgm, &kif->pfik_group->ifg_members, ifgm_next) |
518 | pfi_instance_add(ifgm->ifgm_ifp, net, flags); | |
c686757e AL |
519 | ifgroup_lockmgr(LK_RELEASE); |
520 | } | |
70224baa | 521 | |
c686757e AL |
522 | if ((e = pfr_set_addrs(&kt->pfrkt_t, pfi_buffer, pfi_buffer_cnt, |
523 | &size2, NULL, NULL, NULL, 0, | |
524 | PFR_TFLAG_ALLMASK))) { | |
525 | kprintf("%s: cannot set %d new addresses into table %s: %d\n", | |
526 | __func__, pfi_buffer_cnt, kt->pfrkt_name, e); | |
527 | } | |
02742ec6 JS |
528 | } |
529 | ||
c686757e | 530 | |
9a74b592 SZ |
531 | struct netmsg_pfiadd { |
532 | struct netmsg_base base; | |
533 | struct ifnet *ifp; | |
534 | int net; | |
535 | int flags; | |
536 | }; | |
537 | ||
538 | static void | |
539 | pfi_instance_add_dispatch(netmsg_t nmsg) | |
02742ec6 | 540 | { |
9a74b592 | 541 | struct netmsg_pfiadd *msg = (struct netmsg_pfiadd *)nmsg; |
b2632176 | 542 | struct ifaddr_container *ifac; |
c686757e AL |
543 | int got4 = 0, got6 = 0; |
544 | int net2, af; | |
9a74b592 SZ |
545 | struct ifnet *ifp = msg->ifp; |
546 | int net = msg->net, flags = msg->flags; | |
02742ec6 JS |
547 | |
548 | if (ifp == NULL) | |
9a74b592 SZ |
549 | goto done; |
550 | /* | |
551 | * The ifaddr processing in the following loop will block, | |
552 | * however, this function is called in netisr0, in which | |
553 | * ifaddr list changes happen, so we don't care about the | |
554 | * blockness of the ifaddr processing here. | |
555 | */ | |
b2632176 SZ |
556 | TAILQ_FOREACH(ifac, &ifp->if_addrheads[mycpuid], ifa_link) { |
557 | struct ifaddr *ia = ifac->ifa; | |
558 | ||
02742ec6 JS |
559 | if (ia->ifa_addr == NULL) |
560 | continue; | |
561 | af = ia->ifa_addr->sa_family; | |
562 | if (af != AF_INET && af != AF_INET6) | |
563 | continue; | |
564 | /* | |
565 | * XXX: For point-to-point interfaces, (ifname:0) and IPv4, | |
566 | * jump over address without a proper route to work | |
567 | * around a problem with ppp not fully removing the | |
568 | * address used during IPCP. | |
569 | */ | |
570 | if ((ifp->if_flags & IFF_POINTOPOINT) && | |
571 | !(ia->ifa_flags & IFA_ROUTE) && | |
572 | (flags & PFI_AFLAG_NOALIAS) && (af == AF_INET)) | |
573 | continue; | |
574 | if ((flags & PFI_AFLAG_BROADCAST) && af == AF_INET6) | |
575 | continue; | |
576 | if ((flags & PFI_AFLAG_BROADCAST) && | |
577 | !(ifp->if_flags & IFF_BROADCAST)) | |
578 | continue; | |
579 | if ((flags & PFI_AFLAG_PEER) && | |
580 | !(ifp->if_flags & IFF_POINTOPOINT)) | |
581 | continue; | |
6aa338e1 AL |
582 | if ((flags & (PFI_AFLAG_NETWORK | PFI_AFLAG_NOALIAS)) && |
583 | af == AF_INET6 && | |
02742ec6 JS |
584 | IN6_IS_ADDR_LINKLOCAL( |
585 | &((struct sockaddr_in6 *)ia->ifa_addr)->sin6_addr)) | |
586 | continue; | |
587 | if (flags & PFI_AFLAG_NOALIAS) { | |
588 | if (af == AF_INET && got4) | |
589 | continue; | |
590 | if (af == AF_INET6 && got6) | |
591 | continue; | |
592 | } | |
593 | if (af == AF_INET) | |
594 | got4 = 1; | |
70224baa | 595 | else if (af == AF_INET6) |
02742ec6 JS |
596 | got6 = 1; |
597 | net2 = net; | |
598 | if (net2 == 128 && (flags & PFI_AFLAG_NETWORK)) { | |
70224baa | 599 | if (af == AF_INET) |
02742ec6 JS |
600 | net2 = pfi_unmask(&((struct sockaddr_in *) |
601 | ia->ifa_netmask)->sin_addr); | |
70224baa | 602 | else if (af == AF_INET6) |
02742ec6 JS |
603 | net2 = pfi_unmask(&((struct sockaddr_in6 *) |
604 | ia->ifa_netmask)->sin6_addr); | |
02742ec6 JS |
605 | } |
606 | if (af == AF_INET && net2 > 32) | |
607 | net2 = 32; | |
608 | if (flags & PFI_AFLAG_BROADCAST) | |
609 | pfi_address_add(ia->ifa_broadaddr, af, net2); | |
610 | else if (flags & PFI_AFLAG_PEER) | |
611 | pfi_address_add(ia->ifa_dstaddr, af, net2); | |
612 | else | |
613 | pfi_address_add(ia->ifa_addr, af, net2); | |
614 | } | |
9a74b592 | 615 | done: |
5204e13c | 616 | netisr_replymsg(&nmsg->base, 0); |
9a74b592 SZ |
617 | } |
618 | ||
c686757e | 619 | static void |
9a74b592 SZ |
620 | pfi_instance_add(struct ifnet *ifp, int net, int flags) |
621 | { | |
622 | struct netmsg_pfiadd msg; | |
623 | ||
9a74b592 SZ |
624 | netmsg_init(&msg.base, NULL, &curthread->td_msgport, 0, |
625 | pfi_instance_add_dispatch); | |
626 | msg.ifp = ifp; | |
627 | msg.net = net; | |
628 | msg.flags = flags; | |
5204e13c | 629 | netisr_domsg(&msg.base, 0); |
02742ec6 JS |
630 | } |
631 | ||
c686757e | 632 | static void |
02742ec6 JS |
633 | pfi_address_add(struct sockaddr *sa, int af, int net) |
634 | { | |
635 | struct pfr_addr *p; | |
636 | int i; | |
637 | ||
638 | if (pfi_buffer_cnt >= pfi_buffer_max) { | |
639 | int new_max = pfi_buffer_max * 2; | |
640 | ||
641 | if (new_max > PFI_BUFFER_MAX) { | |
c686757e | 642 | kprintf("%s: address buffer full (%d/%d)\n", __func__, |
02742ec6 JS |
643 | pfi_buffer_cnt, PFI_BUFFER_MAX); |
644 | return; | |
645 | } | |
c686757e | 646 | p = kmalloc(new_max * sizeof(*pfi_buffer), M_PFI, M_WAITOK); |
02742ec6 JS |
647 | memcpy(pfi_buffer, p, pfi_buffer_cnt * sizeof(*pfi_buffer)); |
648 | /* no need to zero buffer */ | |
c686757e | 649 | kfree(pfi_buffer, M_PFI); |
02742ec6 JS |
650 | pfi_buffer = p; |
651 | pfi_buffer_max = new_max; | |
652 | } | |
653 | if (af == AF_INET && net > 32) | |
654 | net = 128; | |
655 | p = pfi_buffer + pfi_buffer_cnt++; | |
656 | bzero(p, sizeof(*p)); | |
657 | p->pfra_af = af; | |
658 | p->pfra_net = net; | |
659 | if (af == AF_INET) | |
660 | p->pfra_ip4addr = ((struct sockaddr_in *)sa)->sin_addr; | |
70224baa | 661 | else if (af == AF_INET6) { |
02742ec6 | 662 | p->pfra_ip6addr = ((struct sockaddr_in6 *)sa)->sin6_addr; |
70224baa | 663 | if (IN6_IS_SCOPE_EMBED(&p->pfra_ip6addr)) |
02742ec6 JS |
664 | p->pfra_ip6addr.s6_addr16[1] = 0; |
665 | } | |
666 | /* mask network address bits */ | |
667 | if (net < 128) | |
668 | ((caddr_t)p)[p->pfra_net/8] &= ~(0xFF >> (p->pfra_net%8)); | |
669 | for (i = (p->pfra_net+7)/8; i < sizeof(p->pfra_u); i++) | |
670 | ((caddr_t)p)[i] = 0; | |
671 | } | |
672 | ||
673 | void | |
674 | pfi_dynaddr_remove(struct pf_addr_wrap *aw) | |
675 | { | |
02742ec6 JS |
676 | if (aw->type != PF_ADDR_DYNIFTL || aw->p.dyn == NULL || |
677 | aw->p.dyn->pfid_kif == NULL || aw->p.dyn->pfid_kt == NULL) | |
678 | return; | |
679 | ||
cc6e5672 | 680 | crit_enter(); |
70224baa JL |
681 | TAILQ_REMOVE(&aw->p.dyn->pfid_kif->pfik_dynaddrs, aw->p.dyn, entry); |
682 | pfi_kif_unref(aw->p.dyn->pfid_kif, PFI_KIF_REF_RULE); | |
02742ec6 JS |
683 | aw->p.dyn->pfid_kif = NULL; |
684 | pfr_detach_table(aw->p.dyn->pfid_kt); | |
685 | aw->p.dyn->pfid_kt = NULL; | |
c686757e | 686 | kfree(aw->p.dyn, M_PFI); |
02742ec6 | 687 | aw->p.dyn = NULL; |
cc6e5672 | 688 | crit_exit(); |
02742ec6 JS |
689 | } |
690 | ||
691 | void | |
692 | pfi_dynaddr_copyout(struct pf_addr_wrap *aw) | |
693 | { | |
694 | if (aw->type != PF_ADDR_DYNIFTL || aw->p.dyn == NULL || | |
695 | aw->p.dyn->pfid_kif == NULL) | |
696 | return; | |
697 | aw->p.dyncnt = aw->p.dyn->pfid_acnt4 + aw->p.dyn->pfid_acnt6; | |
698 | } | |
699 | ||
c686757e | 700 | static int |
02742ec6 JS |
701 | pfi_if_compare(struct pfi_kif *p, struct pfi_kif *q) |
702 | { | |
703 | return (strncmp(p->pfik_name, q->pfik_name, IFNAMSIZ)); | |
704 | } | |
705 | ||
02742ec6 | 706 | void |
ed1f0be2 | 707 | pfi_update_status(const char *name, struct pf_status *pfs) |
02742ec6 | 708 | { |
70224baa | 709 | struct pfi_kif *p; |
c686757e | 710 | struct pfi_kif_cmp key; |
ed1f0be2 JL |
711 | struct ifg_member p_member, *ifgm; |
712 | TAILQ_HEAD(, ifg_member) ifg_members; | |
70224baa | 713 | int i, j, k; |
02742ec6 | 714 | |
c686757e | 715 | strlcpy(key.pfik_name, name, sizeof(key.pfik_name)); |
cc6e5672 | 716 | crit_enter(); |
70224baa | 717 | p = RB_FIND(pfi_ifhead, &pfi_ifs, (struct pfi_kif *)&key); |
02742ec6 | 718 | if (p == NULL) { |
cc6e5672 | 719 | crit_exit(); |
02742ec6 JS |
720 | return; |
721 | } | |
ed1f0be2 JL |
722 | if (p->pfik_group != NULL) { |
723 | bcopy(&p->pfik_group->ifg_members, &ifg_members, | |
724 | sizeof(ifg_members)); | |
725 | } else { | |
726 | /* build a temporary list for p only */ | |
727 | bzero(&p_member, sizeof(p_member)); | |
728 | p_member.ifgm_ifp = p->pfik_ifp; | |
729 | TAILQ_INIT(&ifg_members); | |
730 | TAILQ_INSERT_TAIL(&ifg_members, &p_member, ifgm_next); | |
731 | } | |
732 | if (pfs) { | |
733 | bzero(pfs->pcounters, sizeof(pfs->pcounters)); | |
734 | bzero(pfs->bcounters, sizeof(pfs->bcounters)); | |
735 | } | |
c686757e | 736 | ifgroup_lockmgr(LK_SHARED); |
ed1f0be2 | 737 | TAILQ_FOREACH(ifgm, &ifg_members, ifgm_next) { |
c686757e AL |
738 | if (ifgm->ifgm_ifp == NULL || |
739 | ifgm->ifgm_ifp->if_pf_kif == NULL) | |
ed1f0be2 JL |
740 | continue; |
741 | p = (struct pfi_kif *)ifgm->ifgm_ifp->if_pf_kif; | |
02742ec6 | 742 | |
ed1f0be2 JL |
743 | /* just clear statistics */ |
744 | if (pfs == NULL) { | |
745 | bzero(p->pfik_packets, sizeof(p->pfik_packets)); | |
746 | bzero(p->pfik_bytes, sizeof(p->pfik_bytes)); | |
747 | p->pfik_tzero = time_second; | |
02742ec6 | 748 | continue; |
ed1f0be2 JL |
749 | } |
750 | for (i = 0; i < 2; i++) | |
751 | for (j = 0; j < 2; j++) | |
752 | for (k = 0; k < 2; k++) { | |
753 | pfs->pcounters[i][j][k] += | |
754 | p->pfik_packets[i][j][k]; | |
755 | pfs->bcounters[i][j] += | |
756 | p->pfik_bytes[i][j][k]; | |
757 | } | |
02742ec6 | 758 | } |
c686757e | 759 | ifgroup_lockmgr(LK_RELEASE); |
cc6e5672 | 760 | crit_exit(); |
02742ec6 JS |
761 | } |
762 | ||
763 | int | |
70224baa | 764 | pfi_get_ifaces(const char *name, struct pfi_kif *buf, int *size) |
02742ec6 | 765 | { |
70224baa | 766 | struct pfi_kif *p, *nextp; |
cc6e5672 | 767 | int n = 0; |
02742ec6 | 768 | |
cc6e5672 | 769 | crit_enter(); |
70224baa JL |
770 | for (p = RB_MIN(pfi_ifhead, &pfi_ifs); p; p = nextp) { |
771 | nextp = RB_NEXT(pfi_ifhead, &pfi_ifs, p); | |
772 | if (pfi_skip_if(name, p)) | |
02742ec6 JS |
773 | continue; |
774 | if (*size > n++) { | |
775 | if (!p->pfik_tzero) | |
70224baa JL |
776 | p->pfik_tzero = time_second; |
777 | pfi_kif_ref(p, PFI_KIF_REF_RULE); | |
02742ec6 | 778 | if (copyout(p, buf++, sizeof(*buf))) { |
70224baa | 779 | pfi_kif_unref(p, PFI_KIF_REF_RULE); |
cc6e5672 | 780 | crit_exit(); |
02742ec6 JS |
781 | return (EFAULT); |
782 | } | |
70224baa JL |
783 | nextp = RB_NEXT(pfi_ifhead, &pfi_ifs, p); |
784 | pfi_kif_unref(p, PFI_KIF_REF_RULE); | |
02742ec6 JS |
785 | } |
786 | } | |
cc6e5672 | 787 | crit_exit(); |
02742ec6 JS |
788 | *size = n; |
789 | return (0); | |
790 | } | |
791 | ||
c686757e | 792 | static int |
70224baa | 793 | pfi_skip_if(const char *filter, struct pfi_kif *p) |
02742ec6 | 794 | { |
1f464430 AL |
795 | struct ifg_list *ifg; |
796 | int n; | |
02742ec6 | 797 | |
02742ec6 JS |
798 | if (filter == NULL || !*filter) |
799 | return (0); | |
c686757e | 800 | if (strcmp(p->pfik_name, filter) == 0) |
02742ec6 JS |
801 | return (0); /* exact match */ |
802 | n = strlen(filter); | |
803 | if (n < 1 || n >= IFNAMSIZ) | |
804 | return (1); /* sanity check */ | |
805 | if (filter[n-1] >= '0' && filter[n-1] <= '9') | |
1f464430 AL |
806 | return (1); /* group names may not end in a digit */ |
807 | if (p->pfik_ifp != NULL) { | |
808 | ifgroup_lockmgr(LK_SHARED); | |
809 | TAILQ_FOREACH(ifg, &p->pfik_ifp->if_groups, ifgl_next) { | |
810 | if (strcmp(ifg->ifgl_group->ifg_group, filter) == 0) { | |
811 | ifgroup_lockmgr(LK_RELEASE); | |
812 | return (0); /* iface is in group "filter" */ | |
813 | } | |
814 | } | |
815 | ifgroup_lockmgr(LK_RELEASE); | |
816 | } | |
817 | return (1); | |
02742ec6 JS |
818 | } |
819 | ||
70224baa JL |
820 | int |
821 | pfi_set_flags(const char *name, int flags) | |
822 | { | |
823 | struct pfi_kif *p; | |
824 | ||
825 | crit_enter(); | |
826 | RB_FOREACH(p, pfi_ifhead, &pfi_ifs) { | |
827 | if (pfi_skip_if(name, p)) | |
828 | continue; | |
829 | p->pfik_flags |= flags; | |
830 | } | |
831 | crit_exit(); | |
832 | return (0); | |
833 | } | |
834 | ||
835 | int | |
836 | pfi_clear_flags(const char *name, int flags) | |
837 | { | |
838 | struct pfi_kif *p; | |
839 | ||
840 | crit_enter(); | |
841 | RB_FOREACH(p, pfi_ifhead, &pfi_ifs) { | |
842 | if (pfi_skip_if(name, p)) | |
843 | continue; | |
844 | p->pfik_flags &= ~flags; | |
845 | } | |
846 | crit_exit(); | |
847 | return (0); | |
848 | } | |
849 | ||
02742ec6 | 850 | /* from pf_print_state.c */ |
c686757e | 851 | static int |
02742ec6 JS |
852 | pfi_unmask(void *addr) |
853 | { | |
854 | struct pf_addr *m = addr; | |
855 | int i = 31, j = 0, b = 0; | |
856 | u_int32_t tmp; | |
857 | ||
858 | while (j < 4 && m->addr32[j] == 0xffffffff) { | |
859 | b += 32; | |
860 | j++; | |
861 | } | |
862 | if (j < 4) { | |
863 | tmp = ntohl(m->addr32[j]); | |
864 | for (i = 31; tmp & (1 << i); --i) | |
865 | b++; | |
866 | } | |
867 | return (b); | |
868 | } | |
869 | ||
c686757e AL |
870 | |
871 | /* | |
872 | * eventhandler events | |
873 | */ | |
874 | ||
875 | static void | |
876 | pfi_attach_ifnet_event(void *arg __unused, struct ifnet *ifp) | |
877 | { | |
878 | pfi_attach_ifnet(ifp); | |
879 | } | |
880 | ||
881 | static void | |
882 | pfi_detach_ifnet_event(void *arg __unused, struct ifnet *ifp) | |
883 | { | |
884 | pfi_detach_ifnet(ifp); | |
885 | } | |
886 | ||
887 | static void | |
888 | pfi_attach_group_event(void *arg __unused, struct ifg_group *ifg) | |
889 | { | |
890 | pfi_attach_ifgroup(ifg); | |
891 | } | |
892 | ||
893 | static void | |
894 | pfi_detach_group_event(void *arg __unused, struct ifg_group *ifg) | |
895 | { | |
896 | pfi_detach_ifgroup(ifg); | |
897 | } | |
898 | ||
899 | static void | |
900 | pfi_change_group_event(void *arg __unused, char *gname) | |
901 | { | |
902 | pfi_group_change(gname); | |
903 | } | |
904 | ||
905 | static void | |
906 | pfi_ifaddr_event(void *arg __unused, struct ifnet *ifp, | |
907 | enum ifaddr_event event __unused, | |
908 | struct ifaddr *ifa __unused) | |
909 | { | |
910 | if (ifp && ifp->if_pf_kif) { | |
911 | crit_enter(); | |
912 | pfi_update++; | |
913 | pfi_kif_update(ifp->if_pf_kif); | |
914 | crit_exit(); | |
915 | } | |
916 | } |