1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2023 Dmitry Chagin <dchagin@FreeBSD.org> 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28 #include <sys/cdefs.h> 29 #include <sys/param.h> 30 #include <sys/eventhandler.h> 31 #include <sys/kernel.h> 32 #include <sys/lock.h> 33 #include <sys/malloc.h> 34 #include <sys/mutex.h> 35 #include <sys/sbuf.h> 36 #include <sys/socket.h> 37 38 #include <net/if.h> 39 #include <net/if_var.h> 40 #include <net/vnet.h> 41 42 #include <compat/linux/linux.h> 43 #include <compat/linux/linux_common.h> 44 #include <fs/pseudofs/pseudofs.h> 45 46 #include <compat/linsysfs/linsysfs.h> 47 48 struct pfs_node *net; 49 static eventhandler_tag if_arrival_tag, if_departure_tag; 50 51 static uint32_t net_latch_count = 0; 52 static struct mtx net_latch_mtx; 53 MTX_SYSINIT(net_latch_mtx, &net_latch_mtx, "lsfnet", MTX_DEF); 54 55 struct ifp_nodes_queue { 56 TAILQ_ENTRY(ifp_nodes_queue) ifp_nodes_next; 57 if_t ifp; 58 struct vnet *vnet; 59 struct pfs_node *pn; 60 }; 61 TAILQ_HEAD(,ifp_nodes_queue) ifp_nodes_q; 62 63 static void 64 linsysfs_net_latch_hold(void) 65 { 66 67 mtx_lock(&net_latch_mtx); 68 if (net_latch_count++ > 0) 69 mtx_sleep(&net_latch_count, &net_latch_mtx, PDROP, "lsfnet", 0); 70 else 71 mtx_unlock(&net_latch_mtx); 72 } 73 74 static void 75 linsysfs_net_latch_rele(void) 76 { 77 78 mtx_lock(&net_latch_mtx); 79 if (--net_latch_count > 0) 80 wakeup_one(&net_latch_count); 81 mtx_unlock(&net_latch_mtx); 82 } 83 84 static int 85 linsysfs_if_addr(PFS_FILL_ARGS) 86 { 87 struct epoch_tracker et; 88 struct l_sockaddr lsa; 89 if_t ifp; 90 int error; 91 92 CURVNET_SET(TD_TO_VNET(td)); 93 NET_EPOCH_ENTER(et); 94 ifp = ifname_linux_to_ifp(td, pn->pn_parent->pn_name); 95 if (ifp != NULL && (error = linux_ifhwaddr(ifp, &lsa)) == 0) 96 error = sbuf_printf(sb, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n", 97 lsa.sa_data[0], lsa.sa_data[1], lsa.sa_data[2], 98 lsa.sa_data[3], lsa.sa_data[4], lsa.sa_data[5]); 99 else 100 error = ENOENT; 101 NET_EPOCH_EXIT(et); 102 CURVNET_RESTORE(); 103 return (error == -1 ? ERANGE : error); 104 } 105 106 static int 107 linsysfs_if_addrlen(PFS_FILL_ARGS) 108 { 109 110 sbuf_printf(sb, "%d\n", LINUX_IFHWADDRLEN); 111 return (0); 112 } 113 114 static int 115 linsysfs_if_flags(PFS_FILL_ARGS) 116 { 117 struct epoch_tracker et; 118 if_t ifp; 119 int error; 120 121 CURVNET_SET(TD_TO_VNET(td)); 122 NET_EPOCH_ENTER(et); 123 ifp = ifname_linux_to_ifp(td, pn->pn_parent->pn_name); 124 if (ifp != NULL) 125 error = sbuf_printf(sb, "0x%x\n", linux_ifflags(ifp)); 126 else 127 error = ENOENT; 128 NET_EPOCH_EXIT(et); 129 CURVNET_RESTORE(); 130 return (error == -1 ? ERANGE : error); 131 } 132 133 static int 134 linsysfs_if_ifindex(PFS_FILL_ARGS) 135 { 136 struct epoch_tracker et; 137 if_t ifp; 138 int error; 139 140 CURVNET_SET(TD_TO_VNET(td)); 141 NET_EPOCH_ENTER(et); 142 ifp = ifname_linux_to_ifp(td, pn->pn_parent->pn_name); 143 if (ifp != NULL) 144 error = sbuf_printf(sb, "%u\n", if_getindex(ifp)); 145 else 146 error = ENOENT; 147 NET_EPOCH_EXIT(et); 148 CURVNET_RESTORE(); 149 return (error == -1 ? ERANGE : error); 150 } 151 152 static int 153 linsysfs_if_mtu(PFS_FILL_ARGS) 154 { 155 struct epoch_tracker et; 156 if_t ifp; 157 int error; 158 159 CURVNET_SET(TD_TO_VNET(td)); 160 NET_EPOCH_ENTER(et); 161 ifp = ifname_linux_to_ifp(td, pn->pn_parent->pn_name); 162 if (ifp != NULL) 163 error = sbuf_printf(sb, "%u\n", if_getmtu(ifp)); 164 else 165 error = ENOENT; 166 NET_EPOCH_EXIT(et); 167 CURVNET_RESTORE(); 168 return (error == -1 ? ERANGE : error); 169 } 170 171 static int 172 linsysfs_if_txq_len(PFS_FILL_ARGS) 173 { 174 175 /* XXX */ 176 sbuf_printf(sb, "1000\n"); 177 return (0); 178 } 179 180 static int 181 linsysfs_if_type(PFS_FILL_ARGS) 182 { 183 struct epoch_tracker et; 184 struct l_sockaddr lsa; 185 if_t ifp; 186 int error; 187 188 CURVNET_SET(TD_TO_VNET(td)); 189 NET_EPOCH_ENTER(et); 190 ifp = ifname_linux_to_ifp(td, pn->pn_parent->pn_name); 191 if (ifp != NULL && (error = linux_ifhwaddr(ifp, &lsa)) == 0) 192 error = sbuf_printf(sb, "%d\n", lsa.sa_family); 193 else 194 error = ENOENT; 195 NET_EPOCH_EXIT(et); 196 CURVNET_RESTORE(); 197 return (error == -1 ? ERANGE : error); 198 } 199 200 static int 201 linsysfs_if_visible(PFS_VIS_ARGS) 202 { 203 struct ifp_nodes_queue *nq, *nq_tmp; 204 struct epoch_tracker et; 205 if_t ifp; 206 int visible; 207 208 visible = 0; 209 CURVNET_SET(TD_TO_VNET(td)); 210 NET_EPOCH_ENTER(et); 211 ifp = ifname_linux_to_ifp(td, pn->pn_name); 212 if (ifp != NULL) { 213 TAILQ_FOREACH_SAFE(nq, &ifp_nodes_q, ifp_nodes_next, nq_tmp) { 214 if (nq->ifp == ifp && nq->vnet == curvnet) { 215 visible = 1; 216 break; 217 } 218 } 219 } 220 NET_EPOCH_EXIT(et); 221 CURVNET_RESTORE(); 222 return (visible); 223 } 224 225 static int 226 linsysfs_net_addif(if_t ifp, void *arg) 227 { 228 struct ifp_nodes_queue *nq, *nq_tmp; 229 struct pfs_node *nic, *dir = arg; 230 char ifname[LINUX_IFNAMSIZ]; 231 struct epoch_tracker et; 232 int ret __diagused; 233 234 NET_EPOCH_ENTER(et); 235 ret = ifname_bsd_to_linux_ifp(ifp, ifname, sizeof(ifname)); 236 NET_EPOCH_EXIT(et); 237 KASSERT(ret > 0, ("Interface (%s) is not converted", if_name(ifp))); 238 239 nic = pfs_find_node(dir, ifname); 240 if (nic == NULL) { 241 nic = pfs_create_dir(dir, ifname, NULL, linsysfs_if_visible, 242 NULL, 0); 243 pfs_create_file(nic, "address", &linsysfs_if_addr, 244 NULL, NULL, NULL, PFS_RD); 245 pfs_create_file(nic, "addr_len", &linsysfs_if_addrlen, 246 NULL, NULL, NULL, PFS_RD); 247 pfs_create_file(nic, "flags", &linsysfs_if_flags, 248 NULL, NULL, NULL, PFS_RD); 249 pfs_create_file(nic, "ifindex", &linsysfs_if_ifindex, 250 NULL, NULL, NULL, PFS_RD); 251 pfs_create_file(nic, "mtu", &linsysfs_if_mtu, 252 NULL, NULL, NULL, PFS_RD); 253 pfs_create_file(nic, "tx_queue_len", &linsysfs_if_txq_len, 254 NULL, NULL, NULL, PFS_RD); 255 pfs_create_file(nic, "type", &linsysfs_if_type, 256 NULL, NULL, NULL, PFS_RD); 257 } 258 /* 259 * There is a small window between registering the if_arrival 260 * eventhandler and creating a list of interfaces. 261 */ 262 TAILQ_FOREACH_SAFE(nq, &ifp_nodes_q, ifp_nodes_next, nq_tmp) { 263 if (nq->ifp == ifp && nq->vnet == curvnet) 264 return (0); 265 } 266 nq = malloc(sizeof(*nq), M_LINSYSFS, M_WAITOK); 267 nq->pn = nic; 268 nq->ifp = ifp; 269 nq->vnet = curvnet; 270 TAILQ_INSERT_TAIL(&ifp_nodes_q, nq, ifp_nodes_next); 271 return (0); 272 } 273 274 static void 275 linsysfs_net_delif(if_t ifp) 276 { 277 struct ifp_nodes_queue *nq, *nq_tmp; 278 struct pfs_node *pn; 279 280 pn = NULL; 281 TAILQ_FOREACH_SAFE(nq, &ifp_nodes_q, ifp_nodes_next, nq_tmp) { 282 if (nq->ifp == ifp && nq->vnet == curvnet) { 283 TAILQ_REMOVE(&ifp_nodes_q, nq, ifp_nodes_next); 284 pn = nq->pn; 285 free(nq, M_LINSYSFS); 286 break; 287 } 288 } 289 if (pn == NULL) 290 return; 291 TAILQ_FOREACH_SAFE(nq, &ifp_nodes_q, ifp_nodes_next, nq_tmp) { 292 if (nq->pn == pn) 293 return; 294 } 295 pfs_destroy(pn); 296 } 297 298 static void 299 linsysfs_if_arrival(void *arg __unused, if_t ifp) 300 { 301 302 linsysfs_net_latch_hold(); 303 (void)linsysfs_net_addif(ifp, net); 304 linsysfs_net_latch_rele(); 305 } 306 307 static void 308 linsysfs_if_departure(void *arg __unused, if_t ifp) 309 { 310 311 linsysfs_net_latch_hold(); 312 linsysfs_net_delif(ifp); 313 linsysfs_net_latch_rele(); 314 } 315 316 void 317 linsysfs_net_init(void) 318 { 319 VNET_ITERATOR_DECL(vnet_iter); 320 321 MPASS(net != NULL); 322 TAILQ_INIT(&ifp_nodes_q); 323 324 if_arrival_tag = EVENTHANDLER_REGISTER(ifnet_arrival_event, 325 linsysfs_if_arrival, NULL, EVENTHANDLER_PRI_ANY); 326 if_departure_tag = EVENTHANDLER_REGISTER(ifnet_departure_event, 327 linsysfs_if_departure, NULL, EVENTHANDLER_PRI_ANY); 328 329 linsysfs_net_latch_hold(); 330 VNET_LIST_RLOCK(); 331 VNET_FOREACH(vnet_iter) { 332 CURVNET_SET(vnet_iter); 333 if_foreach_sleep(NULL, NULL, linsysfs_net_addif, net); 334 CURVNET_RESTORE(); 335 } 336 VNET_LIST_RUNLOCK(); 337 linsysfs_net_latch_rele(); 338 } 339 340 void 341 linsysfs_net_uninit(void) 342 { 343 struct ifp_nodes_queue *nq, *nq_tmp; 344 345 EVENTHANDLER_DEREGISTER(ifnet_arrival_event, if_arrival_tag); 346 EVENTHANDLER_DEREGISTER(ifnet_departure_event, if_departure_tag); 347 348 linsysfs_net_latch_hold(); 349 TAILQ_FOREACH_SAFE(nq, &ifp_nodes_q, ifp_nodes_next, nq_tmp) { 350 TAILQ_REMOVE(&ifp_nodes_q, nq, ifp_nodes_next); 351 free(nq, M_LINSYSFS); 352 } 353 linsysfs_net_latch_rele(); 354 } 355