1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 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 __FBSDID("$FreeBSD$"); 30 31 #include <sys/param.h> 32 #include <sys/eventhandler.h> 33 #include <sys/kernel.h> 34 #include <sys/lock.h> 35 #include <sys/mutex.h> 36 #include <sys/sbuf.h> 37 #include <sys/socket.h> 38 39 #include <net/if.h> 40 #include <net/if_var.h> 41 #include <net/if_dl.h> 42 43 #include <compat/linux/linux.h> 44 #include <compat/linux/linux_common.h> 45 #include <fs/pseudofs/pseudofs.h> 46 47 #include <compat/linsysfs/linsysfs.h> 48 49 struct pfs_node *net; 50 static eventhandler_tag if_arrival_tag, if_departure_tag; 51 52 static uint32_t net_latch_count = 0; 53 static struct mtx net_latch_mtx; 54 MTX_SYSINIT(net_latch_mtx, &net_latch_mtx, "lsfnet", MTX_DEF); 55 56 struct ifp_nodes_queue { 57 TAILQ_ENTRY(ifp_nodes_queue) ifp_nodes_next; 58 if_t ifp; 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_ifnet_addr(PFS_FILL_ARGS) 86 { 87 struct epoch_tracker et; 88 struct l_sockaddr lsa; 89 struct ifnet *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_ifnet_addrlen(PFS_FILL_ARGS) 108 { 109 110 sbuf_printf(sb, "%d\n", LINUX_IFHWADDRLEN); 111 return (0); 112 } 113 114 static int 115 linsysfs_ifnet_flags(PFS_FILL_ARGS) 116 { 117 struct epoch_tracker et; 118 struct ifnet *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_ifnet_ifindex(PFS_FILL_ARGS) 135 { 136 struct epoch_tracker et; 137 struct ifnet *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_ifnet_mtu(PFS_FILL_ARGS) 154 { 155 struct epoch_tracker et; 156 struct ifnet *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_ifnet_tx_queue_len(PFS_FILL_ARGS) 173 { 174 175 /* XXX */ 176 sbuf_printf(sb, "1000\n"); 177 return (0); 178 } 179 180 static int 181 linsysfs_ifnet_type(PFS_FILL_ARGS) 182 { 183 struct epoch_tracker et; 184 struct l_sockaddr lsa; 185 struct ifnet *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 struct pfs_node * 201 linsysfs_net_find_node(if_t ifp) 202 { 203 struct ifp_nodes_queue *nq, *nq_tmp; 204 205 TAILQ_FOREACH_SAFE(nq, &ifp_nodes_q, ifp_nodes_next, nq_tmp) { 206 if (nq->ifp == ifp) 207 return (nq->pn); 208 } 209 return (NULL); 210 } 211 212 static int 213 linsysfs_net_addnic(if_t ifp, void *arg) 214 { 215 char ifname[LINUX_IFNAMSIZ]; 216 struct ifp_nodes_queue *nq; 217 struct epoch_tracker et; 218 struct pfs_node *dir = arg; 219 struct pfs_node *nic = NULL; 220 int ret __diagused; 221 222 NET_EPOCH_ENTER(et); 223 ret = ifname_bsd_to_linux_ifp(ifp, ifname, sizeof(ifname)); 224 NET_EPOCH_EXIT(et); 225 KASSERT(ret > 0, ("Interface (%s) is not converted", if_name(ifp))); 226 227 nic = linsysfs_net_find_node(ifp); 228 MPASS(nic == NULL); 229 230 nic = pfs_create_dir(dir, ifname, NULL, NULL, NULL, 0); 231 pfs_create_file(nic, "address", &linsysfs_ifnet_addr, 232 NULL, NULL, NULL, PFS_RD); 233 pfs_create_file(nic, "addr_len", &linsysfs_ifnet_addrlen, 234 NULL, NULL, NULL, PFS_RD); 235 pfs_create_file(nic, "flags", &linsysfs_ifnet_flags, 236 NULL, NULL, NULL, PFS_RD); 237 pfs_create_file(nic, "ifindex", &linsysfs_ifnet_ifindex, 238 NULL, NULL, NULL, PFS_RD); 239 pfs_create_file(nic, "mtu", &linsysfs_ifnet_mtu, 240 NULL, NULL, NULL, PFS_RD); 241 pfs_create_file(nic, "tx_queue_len", &linsysfs_ifnet_tx_queue_len, 242 NULL, NULL, NULL, PFS_RD); 243 pfs_create_file(nic, "type", &linsysfs_ifnet_type, 244 NULL, NULL, NULL, PFS_RD); 245 246 nq = malloc(sizeof(*nq), M_LINSYSFS, M_WAITOK); 247 nq->pn = nic; 248 nq->ifp = ifp; 249 TAILQ_INSERT_TAIL(&ifp_nodes_q, nq, ifp_nodes_next); 250 return (0); 251 } 252 253 static int 254 linsysfs_net_delnic(if_t ifp, void *arg) 255 { 256 struct ifp_nodes_queue *nq, *nq_tmp; 257 258 TAILQ_FOREACH_SAFE(nq, &ifp_nodes_q, ifp_nodes_next, nq_tmp) { 259 if (nq->ifp == ifp) { 260 TAILQ_REMOVE(&ifp_nodes_q, nq, ifp_nodes_next); 261 pfs_destroy(nq->pn); 262 free(nq, M_LINSYSFS); 263 return (0); 264 } 265 } 266 return (1); 267 } 268 269 static void 270 linsysfs_net_listnics(struct pfs_node *dir) 271 { 272 273 CURVNET_SET(TD_TO_VNET(curthread)); 274 if_foreach_sleep(NULL, NULL, linsysfs_net_addnic, dir); 275 CURVNET_RESTORE(); 276 } 277 278 static void 279 linsysfs_ifnet_arrival(void *arg __unused, struct ifnet *ifp) 280 { 281 282 linsysfs_net_latch_hold(); 283 linsysfs_net_addnic(ifp, net); 284 linsysfs_net_latch_rele(); 285 } 286 287 static void 288 linsysfs_ifnet_departure(void *arg __unused, struct ifnet *ifp) 289 { 290 291 linsysfs_net_latch_hold(); 292 linsysfs_net_delnic(ifp, net); 293 linsysfs_net_latch_rele(); 294 } 295 296 void 297 linsysfs_net_init(void) 298 { 299 300 MPASS(net != NULL); 301 TAILQ_INIT(&ifp_nodes_q); 302 if_arrival_tag = EVENTHANDLER_REGISTER(ifnet_arrival_event, 303 linsysfs_ifnet_arrival, NULL, EVENTHANDLER_PRI_ANY); 304 if_departure_tag = EVENTHANDLER_REGISTER(ifnet_departure_event, 305 linsysfs_ifnet_departure, NULL, EVENTHANDLER_PRI_ANY); 306 307 linsysfs_net_latch_hold(); 308 linsysfs_net_listnics(net); 309 linsysfs_net_latch_rele(); 310 } 311 312 void 313 linsysfs_net_uninit(void) 314 { 315 struct ifp_nodes_queue *nq, *nq_tmp; 316 317 EVENTHANDLER_DEREGISTER(ifnet_arrival_event, if_arrival_tag); 318 EVENTHANDLER_DEREGISTER(ifnet_departure_event, if_departure_tag); 319 320 linsysfs_net_latch_hold(); 321 TAILQ_FOREACH_SAFE(nq, &ifp_nodes_q, ifp_nodes_next, nq_tmp) { 322 TAILQ_REMOVE(&ifp_nodes_q, nq, ifp_nodes_next); 323 free(nq, M_LINSYSFS); 324 } 325 linsysfs_net_latch_rele(); 326 } 327