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