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 * 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 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 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 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 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