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