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.7 2020/05/30 14:16:56 rmind 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 = npf->config->tableset; 91 npf_table_t *oldt; 92 93 KERNEL_UNLOCK_ONE(NULL); 94 95 /* 96 * Finally, swap the tables and issue a sync barrier. 97 */ 98 oldt = npf_tableset_swap(ts, newt); 99 npf_config_sync(npf); 100 npf_config_exit(npf); 101 102 /* At this point, it is safe to destroy the old table. */ 103 npf_table_destroy(oldt); 104 } 105 106 void 107 npf_ifaddr_sync(npf_t *npf, ifnet_t *ifp) 108 { 109 npf_table_t *t; 110 struct ifaddr *ifa; 111 112 /* 113 * First, check whether this interface is of any interest to us. 114 * 115 * => Acquires npf-config-lock and kernel-lock on success. 116 */ 117 t = lookup_ifnet_table(npf, ifp); 118 if (!t) 119 return; 120 121 /* 122 * Populate the table with the interface addresses. 123 * Note: currently, this list is protected by the kernel-lock. 124 */ 125 IFADDR_FOREACH(ifa, ifp) { 126 struct sockaddr *sa = ifa->ifa_addr; 127 const void *p = NULL; 128 int alen = 0; 129 130 if (sa->sa_family == AF_INET) { 131 const struct sockaddr_in *sin4 = satosin(sa); 132 alen = sizeof(struct in_addr); 133 p = &sin4->sin_addr; 134 } 135 if (sa->sa_family == AF_INET6) { 136 const struct sockaddr_in6 *sin6 = satosin6(sa); 137 alen = sizeof(struct in6_addr); 138 p = &sin6->sin6_addr; 139 } 140 if (alen) { 141 npf_addr_t addr; 142 memcpy(&addr, p, alen); 143 npf_table_insert(t, alen, &addr, NPF_NO_NETMASK); 144 } 145 } 146 147 /* Publish the new table. */ 148 replace_ifnet_table(npf, t); 149 } 150 151 void 152 npf_ifaddr_flush(npf_t *npf, ifnet_t *ifp) 153 { 154 npf_table_t *t; 155 156 /* 157 * Flush: just load an empty table. 158 */ 159 t = lookup_ifnet_table(npf, ifp); 160 if (!t) { 161 return; 162 } 163 replace_ifnet_table(npf, t); 164 } 165 166 void 167 npf_ifaddr_syncall(npf_t *npf) 168 { 169 ifnet_t *ifp; 170 171 KERNEL_LOCK(1, NULL); 172 IFNET_GLOBAL_LOCK(); 173 IFNET_WRITER_FOREACH(ifp) { 174 npf_ifaddr_sync(npf, ifp); 175 } 176 IFNET_GLOBAL_UNLOCK(); 177 KERNEL_UNLOCK_ONE(NULL); 178 } 179