xref: /freebsd/sys/compat/linsysfs/linsysfs_net.c (revision ba3c1f5972d7b90feb6e6da47905ff2757e0fe57)
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 __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/malloc.h>
36 #include <sys/mutex.h>
37 #include <sys/sbuf.h>
38 #include <sys/socket.h>
39 
40 #include <net/if.h>
41 #include <net/if_var.h>
42 #include <net/vnet.h>
43 
44 #include <compat/linux/linux.h>
45 #include <compat/linux/linux_common.h>
46 #include <fs/pseudofs/pseudofs.h>
47 
48 #include <compat/linsysfs/linsysfs.h>
49 
50 struct pfs_node *net;
51 static eventhandler_tag if_arrival_tag, if_departure_tag;
52 
53 static uint32_t net_latch_count = 0;
54 static struct mtx net_latch_mtx;
55 MTX_SYSINIT(net_latch_mtx, &net_latch_mtx, "lsfnet", MTX_DEF);
56 
57 struct ifp_nodes_queue {
58 	TAILQ_ENTRY(ifp_nodes_queue) ifp_nodes_next;
59 	if_t ifp;
60 	struct vnet *vnet;
61 	struct pfs_node *pn;
62 };
63 TAILQ_HEAD(,ifp_nodes_queue) ifp_nodes_q;
64 
65 static void
66 linsysfs_net_latch_hold(void)
67 {
68 
69 	mtx_lock(&net_latch_mtx);
70 	if (net_latch_count++ > 0)
71 		mtx_sleep(&net_latch_count, &net_latch_mtx, PDROP, "lsfnet", 0);
72 	else
73 		mtx_unlock(&net_latch_mtx);
74 }
75 
76 static void
77 linsysfs_net_latch_rele(void)
78 {
79 
80 	mtx_lock(&net_latch_mtx);
81 	if (--net_latch_count > 0)
82 		wakeup_one(&net_latch_count);
83 	mtx_unlock(&net_latch_mtx);
84 }
85 
86 static int
87 linsysfs_if_addr(PFS_FILL_ARGS)
88 {
89 	struct epoch_tracker et;
90 	struct l_sockaddr lsa;
91 	if_t ifp;
92 	int error;
93 
94 	CURVNET_SET(TD_TO_VNET(td));
95 	NET_EPOCH_ENTER(et);
96 	ifp = ifname_linux_to_ifp(td, pn->pn_parent->pn_name);
97 	if (ifp != NULL && (error = linux_ifhwaddr(ifp, &lsa)) == 0)
98 		error = sbuf_printf(sb, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n",
99 		    lsa.sa_data[0], lsa.sa_data[1], lsa.sa_data[2],
100 		    lsa.sa_data[3], lsa.sa_data[4], lsa.sa_data[5]);
101 	else
102 		error = ENOENT;
103 	NET_EPOCH_EXIT(et);
104 	CURVNET_RESTORE();
105 	return (error == -1 ? ERANGE : error);
106 }
107 
108 static int
109 linsysfs_if_addrlen(PFS_FILL_ARGS)
110 {
111 
112 	sbuf_printf(sb, "%d\n", LINUX_IFHWADDRLEN);
113 	return (0);
114 }
115 
116 static int
117 linsysfs_if_flags(PFS_FILL_ARGS)
118 {
119 	struct epoch_tracker et;
120 	if_t ifp;
121 	int error;
122 
123 	CURVNET_SET(TD_TO_VNET(td));
124 	NET_EPOCH_ENTER(et);
125 	ifp = ifname_linux_to_ifp(td, pn->pn_parent->pn_name);
126 	if (ifp != NULL)
127 		error = sbuf_printf(sb, "0x%x\n", linux_ifflags(ifp));
128 	else
129 		error = ENOENT;
130 	NET_EPOCH_EXIT(et);
131 	CURVNET_RESTORE();
132 	return (error == -1 ? ERANGE : error);
133 }
134 
135 static int
136 linsysfs_if_ifindex(PFS_FILL_ARGS)
137 {
138 	struct epoch_tracker et;
139 	if_t ifp;
140 	int error;
141 
142 	CURVNET_SET(TD_TO_VNET(td));
143 	NET_EPOCH_ENTER(et);
144 	ifp = ifname_linux_to_ifp(td, pn->pn_parent->pn_name);
145 	if (ifp != NULL)
146 		error = sbuf_printf(sb, "%u\n", if_getindex(ifp));
147 	else
148 		error = ENOENT;
149 	NET_EPOCH_EXIT(et);
150 	CURVNET_RESTORE();
151 	return (error == -1 ? ERANGE : error);
152 }
153 
154 static int
155 linsysfs_if_mtu(PFS_FILL_ARGS)
156 {
157 	struct epoch_tracker et;
158 	if_t ifp;
159 	int error;
160 
161 	CURVNET_SET(TD_TO_VNET(td));
162 	NET_EPOCH_ENTER(et);
163 	ifp = ifname_linux_to_ifp(td, pn->pn_parent->pn_name);
164 	if (ifp != NULL)
165 		error = sbuf_printf(sb, "%u\n", if_getmtu(ifp));
166 	else
167 		error = ENOENT;
168 	NET_EPOCH_EXIT(et);
169 	CURVNET_RESTORE();
170 	return (error == -1 ? ERANGE : error);
171 }
172 
173 static int
174 linsysfs_if_txq_len(PFS_FILL_ARGS)
175 {
176 
177 	/* XXX */
178 	sbuf_printf(sb, "1000\n");
179 	return (0);
180 }
181 
182 static int
183 linsysfs_if_type(PFS_FILL_ARGS)
184 {
185 	struct epoch_tracker et;
186 	struct l_sockaddr lsa;
187 	if_t ifp;
188 	int error;
189 
190 	CURVNET_SET(TD_TO_VNET(td));
191 	NET_EPOCH_ENTER(et);
192 	ifp = ifname_linux_to_ifp(td, pn->pn_parent->pn_name);
193 	if (ifp != NULL && (error = linux_ifhwaddr(ifp, &lsa)) == 0)
194 		error = sbuf_printf(sb, "%d\n", lsa.sa_family);
195 	else
196 		error = ENOENT;
197 	NET_EPOCH_EXIT(et);
198 	CURVNET_RESTORE();
199 	return (error == -1 ? ERANGE : error);
200 }
201 
202 static int
203 linsysfs_if_visible(PFS_VIS_ARGS)
204 {
205 	struct ifp_nodes_queue *nq, *nq_tmp;
206 	struct epoch_tracker et;
207 	if_t ifp;
208 	int visible;
209 
210 	visible = 0;
211 	CURVNET_SET(TD_TO_VNET(td));
212 	NET_EPOCH_ENTER(et);
213 	ifp = ifname_linux_to_ifp(td, pn->pn_name);
214 	if (ifp != NULL) {
215 		TAILQ_FOREACH_SAFE(nq, &ifp_nodes_q, ifp_nodes_next, nq_tmp) {
216 			if (nq->ifp == ifp && nq->vnet == curvnet) {
217 				visible = 1;
218 				break;
219 			}
220 		}
221 	}
222 	NET_EPOCH_EXIT(et);
223 	CURVNET_RESTORE();
224 	return (visible);
225 }
226 
227 static int
228 linsysfs_net_addif(if_t ifp, void *arg)
229 {
230 	struct ifp_nodes_queue *nq, *nq_tmp;
231 	struct pfs_node *nic, *dir = arg;
232 	char ifname[LINUX_IFNAMSIZ];
233 	struct epoch_tracker et;
234 	int ret __diagused;
235 
236 	NET_EPOCH_ENTER(et);
237 	ret = ifname_bsd_to_linux_ifp(ifp, ifname, sizeof(ifname));
238 	NET_EPOCH_EXIT(et);
239 	KASSERT(ret > 0, ("Interface (%s) is not converted", if_name(ifp)));
240 
241 	nic = pfs_find_node(dir, ifname);
242 	if (nic == NULL) {
243 		nic = pfs_create_dir(dir, ifname, NULL, linsysfs_if_visible,
244 		    NULL, 0);
245 		pfs_create_file(nic, "address", &linsysfs_if_addr,
246 		    NULL, NULL, NULL, PFS_RD);
247 		pfs_create_file(nic, "addr_len", &linsysfs_if_addrlen,
248 		    NULL, NULL, NULL, PFS_RD);
249 		pfs_create_file(nic, "flags", &linsysfs_if_flags,
250 		    NULL, NULL, NULL, PFS_RD);
251 		pfs_create_file(nic, "ifindex", &linsysfs_if_ifindex,
252 		    NULL, NULL, NULL, PFS_RD);
253 		pfs_create_file(nic, "mtu", &linsysfs_if_mtu,
254 		    NULL, NULL, NULL, PFS_RD);
255 		pfs_create_file(nic, "tx_queue_len", &linsysfs_if_txq_len,
256 		    NULL, NULL, NULL, PFS_RD);
257 		pfs_create_file(nic, "type", &linsysfs_if_type,
258 		NULL, NULL, NULL, PFS_RD);
259 	}
260 	/*
261 	 * There is a small window between registering the if_arrival
262 	 * eventhandler and creating a list of interfaces.
263 	 */
264 	TAILQ_FOREACH_SAFE(nq, &ifp_nodes_q, ifp_nodes_next, nq_tmp) {
265 		if (nq->ifp == ifp && nq->vnet == curvnet)
266 			return (0);
267 	}
268 	nq = malloc(sizeof(*nq), M_LINSYSFS, M_WAITOK);
269 	nq->pn = nic;
270 	nq->ifp = ifp;
271 	nq->vnet = curvnet;
272 	TAILQ_INSERT_TAIL(&ifp_nodes_q, nq, ifp_nodes_next);
273 	return (0);
274 }
275 
276 static void
277 linsysfs_net_delif(if_t ifp)
278 {
279 	struct ifp_nodes_queue *nq, *nq_tmp;
280 	struct pfs_node *pn;
281 
282 	pn = NULL;
283 	TAILQ_FOREACH_SAFE(nq, &ifp_nodes_q, ifp_nodes_next, nq_tmp) {
284 		if (nq->ifp == ifp && nq->vnet == curvnet) {
285 			TAILQ_REMOVE(&ifp_nodes_q, nq, ifp_nodes_next);
286 			pn = nq->pn;
287 			free(nq, M_LINSYSFS);
288 			break;
289 		}
290 	}
291 	if (pn == NULL)
292 		return;
293 	TAILQ_FOREACH_SAFE(nq, &ifp_nodes_q, ifp_nodes_next, nq_tmp) {
294 		if (nq->pn == pn)
295 			return;
296 	}
297 	pfs_destroy(pn);
298 }
299 
300 static void
301 linsysfs_if_arrival(void *arg __unused, if_t ifp)
302 {
303 
304 	linsysfs_net_latch_hold();
305 	(void)linsysfs_net_addif(ifp, net);
306 	linsysfs_net_latch_rele();
307 }
308 
309 static void
310 linsysfs_if_departure(void *arg __unused, if_t ifp)
311 {
312 
313 	linsysfs_net_latch_hold();
314 	linsysfs_net_delif(ifp);
315 	linsysfs_net_latch_rele();
316 }
317 
318 void
319 linsysfs_net_init(void)
320 {
321 	VNET_ITERATOR_DECL(vnet_iter);
322 
323 	MPASS(net != NULL);
324 	TAILQ_INIT(&ifp_nodes_q);
325 
326 	if_arrival_tag = EVENTHANDLER_REGISTER(ifnet_arrival_event,
327 	    linsysfs_if_arrival, NULL, EVENTHANDLER_PRI_ANY);
328 	if_departure_tag = EVENTHANDLER_REGISTER(ifnet_departure_event,
329 	    linsysfs_if_departure, NULL, EVENTHANDLER_PRI_ANY);
330 
331 	linsysfs_net_latch_hold();
332 	VNET_LIST_RLOCK();
333 	VNET_FOREACH(vnet_iter) {
334 		CURVNET_SET(vnet_iter);
335 		if_foreach_sleep(NULL, NULL, linsysfs_net_addif, net);
336 		CURVNET_RESTORE();
337 	}
338 	VNET_LIST_RUNLOCK();
339 	linsysfs_net_latch_rele();
340 }
341 
342 void
343 linsysfs_net_uninit(void)
344 {
345 	struct ifp_nodes_queue *nq, *nq_tmp;
346 
347 	EVENTHANDLER_DEREGISTER(ifnet_arrival_event, if_arrival_tag);
348 	EVENTHANDLER_DEREGISTER(ifnet_departure_event, if_departure_tag);
349 
350 	linsysfs_net_latch_hold();
351 	TAILQ_FOREACH_SAFE(nq, &ifp_nodes_q, ifp_nodes_next, nq_tmp) {
352 		TAILQ_REMOVE(&ifp_nodes_q, nq, ifp_nodes_next);
353 		free(nq, M_LINSYSFS);
354 	}
355 	linsysfs_net_latch_rele();
356 }
357