]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/compat/linsysfs/linsysfs_net.c
zfs: merge openzfs/zfs@009d3288d
[FreeBSD/FreeBSD.git] / sys / compat / linsysfs / linsysfs_net.c
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 }