xref: /freebsd/sys/compat/linsysfs/linsysfs_net.c (revision 1d386b48)
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