Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/compat/linux/linux_netlink.c
103371 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 2022 Alexander V. Chernikov
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 "opt_inet.h"
29
#include "opt_inet6.h"
30
31
#include <sys/types.h>
32
#include <sys/ck.h>
33
#include <sys/lock.h>
34
#include <sys/socket.h>
35
#include <sys/vnode.h>
36
37
#include <net/if.h>
38
#include <net/if_dl.h>
39
#include <net/route.h>
40
#include <net/route/nhop.h>
41
#include <net/route/route_ctl.h>
42
#include <netlink/netlink.h>
43
#include <netlink/netlink_ctl.h>
44
#include <netlink/netlink_linux.h>
45
#include <netlink/netlink_var.h>
46
#include <netlink/netlink_route.h>
47
48
#include <compat/linux/linux.h>
49
#include <compat/linux/linux_common.h>
50
#include <compat/linux/linux_util.h>
51
52
#define DEBUG_MOD_NAME nl_linux
53
#define DEBUG_MAX_LEVEL LOG_DEBUG3
54
#include <netlink/netlink_debug.h>
55
_DECLARE_DEBUG(LOG_INFO);
56
57
static bool
58
valid_rta_size(const struct rtattr *rta, int sz)
59
{
60
return (NL_RTA_DATA_LEN(rta) == sz);
61
}
62
63
static bool
64
valid_rta_u32(const struct rtattr *rta)
65
{
66
return (valid_rta_size(rta, sizeof(uint32_t)));
67
}
68
69
static uint32_t
70
_rta_get_uint32(const struct rtattr *rta)
71
{
72
return (*((const uint32_t *)NL_RTA_DATA_CONST(rta)));
73
}
74
75
static int
76
rtnl_neigh_from_linux(struct nlmsghdr *hdr, struct nl_pstate *npt)
77
{
78
struct ndmsg *ndm = (struct ndmsg *)(hdr + 1);
79
sa_family_t f;
80
81
if (hdr->nlmsg_len < sizeof(struct nlmsghdr) + sizeof(struct ndmsg))
82
return (EBADMSG);
83
if ((f = linux_to_bsd_domain(ndm->ndm_family)) == AF_UNKNOWN)
84
return (EPFNOSUPPORT);
85
86
ndm->ndm_family = f;
87
88
return (0);
89
}
90
91
static int
92
rtnl_ifaddr_from_linux(struct nlmsghdr *hdr, struct nl_pstate *npt)
93
{
94
struct ifaddrmsg *ifam = (struct ifaddrmsg *)(hdr + 1);
95
sa_family_t f;
96
97
if (hdr->nlmsg_len < sizeof(struct nlmsghdr) +
98
offsetof(struct ifaddrmsg, ifa_family) + sizeof(ifam->ifa_family))
99
return (EBADMSG);
100
if ((f = linux_to_bsd_domain(ifam->ifa_family)) == AF_UNKNOWN)
101
return (EPFNOSUPPORT);
102
103
ifam->ifa_family = f;
104
105
return (0);
106
}
107
108
/*
109
* XXX: in case of error state of hdr is inconsistent.
110
*/
111
static int
112
rtnl_route_from_linux(struct nlmsghdr *hdr, struct nl_pstate *npt)
113
{
114
/* Tweak address families and default fib only */
115
struct rtmsg *rtm = (struct rtmsg *)(hdr + 1);
116
struct nlattr *nla, *nla_head;
117
int attrs_len;
118
sa_family_t f;
119
120
if (hdr->nlmsg_len < sizeof(struct nlmsghdr) + sizeof(struct rtmsg))
121
return (EBADMSG);
122
if ((f = linux_to_bsd_domain(rtm->rtm_family)) == AF_UNKNOWN)
123
return (EPFNOSUPPORT);
124
rtm->rtm_family = f;
125
126
if (rtm->rtm_table == 254)
127
rtm->rtm_table = 0;
128
129
attrs_len = hdr->nlmsg_len - sizeof(struct nlmsghdr);
130
attrs_len -= NETLINK_ALIGN(sizeof(struct rtmsg));
131
nla_head = (struct nlattr *)((char *)rtm + NETLINK_ALIGN(sizeof(struct rtmsg)));
132
133
NLA_FOREACH(nla, nla_head, attrs_len) {
134
RT_LOG(LOG_DEBUG3, "GOT type %d len %d total %d",
135
nla->nla_type, nla->nla_len, attrs_len);
136
struct rtattr *rta = (struct rtattr *)nla;
137
if (rta->rta_len < sizeof(struct rtattr)) {
138
break;
139
}
140
switch (rta->rta_type) {
141
case NL_RTA_TABLE:
142
if (!valid_rta_u32(rta))
143
return (EBADMSG);
144
rtm->rtm_table = 0;
145
uint32_t fibnum = _rta_get_uint32(rta);
146
RT_LOG(LOG_DEBUG3, "GET RTABLE: %u", fibnum);
147
if (fibnum == 254) {
148
*((uint32_t *)NL_RTA_DATA(rta)) = 0;
149
}
150
break;
151
}
152
}
153
154
return (0);
155
}
156
157
static int
158
rtnl_from_linux(struct nlmsghdr *hdr, struct nl_pstate *npt)
159
{
160
161
switch (hdr->nlmsg_type) {
162
case NL_RTM_GETROUTE:
163
case NL_RTM_NEWROUTE:
164
case NL_RTM_DELROUTE:
165
return (rtnl_route_from_linux(hdr, npt));
166
case NL_RTM_GETNEIGH:
167
return (rtnl_neigh_from_linux(hdr, npt));
168
case NL_RTM_GETADDR:
169
return (rtnl_ifaddr_from_linux(hdr, npt));
170
/* Silence warning for the messages where no translation is required */
171
case NL_RTM_NEWLINK:
172
case NL_RTM_DELLINK:
173
case NL_RTM_GETLINK:
174
break;
175
default:
176
RT_LOG(LOG_DEBUG, "Passing message type %d untranslated",
177
hdr->nlmsg_type);
178
/* XXXGL: maybe return error? */
179
}
180
181
return (0);
182
}
183
184
static int
185
nlmsg_from_linux(int netlink_family, struct nlmsghdr **hdr,
186
struct nl_pstate *npt)
187
{
188
switch (netlink_family) {
189
case NETLINK_ROUTE:
190
return (rtnl_from_linux(*hdr, npt));
191
}
192
193
return (0);
194
}
195
196
197
/************************************************************
198
* Kernel -> Linux
199
************************************************************/
200
201
static bool
202
handle_default_out(struct nlmsghdr *hdr, struct nl_writer *nw)
203
{
204
char *out_hdr;
205
out_hdr = nlmsg_reserve_data(nw, NLMSG_ALIGN(hdr->nlmsg_len), char);
206
207
if (out_hdr != NULL) {
208
memcpy(out_hdr, hdr, hdr->nlmsg_len);
209
nw->num_messages++;
210
return (true);
211
}
212
return (false);
213
}
214
215
static bool
216
nlmsg_copy_header(struct nlmsghdr *hdr, struct nl_writer *nw)
217
{
218
return (nlmsg_add(nw, hdr->nlmsg_pid, hdr->nlmsg_seq, hdr->nlmsg_type,
219
hdr->nlmsg_flags, 0));
220
}
221
222
static void *
223
_nlmsg_copy_next_header(struct nlmsghdr *hdr, struct nl_writer *nw, int sz)
224
{
225
void *next_hdr = nlmsg_reserve_data(nw, sz, void);
226
memcpy(next_hdr, hdr + 1, NLMSG_ALIGN(sz));
227
228
return (next_hdr);
229
}
230
#define nlmsg_copy_next_header(_hdr, _ns, _t) \
231
((_t *)(_nlmsg_copy_next_header(_hdr, _ns, sizeof(_t))))
232
233
static bool
234
nlmsg_copy_nla(const struct nlattr *nla_orig, struct nl_writer *nw)
235
{
236
struct nlattr *nla = nlmsg_reserve_data(nw, nla_orig->nla_len, struct nlattr);
237
if (nla != NULL) {
238
memcpy(nla, nla_orig, nla_orig->nla_len);
239
return (true);
240
}
241
return (false);
242
}
243
244
/*
245
* Translate a FreeBSD interface name to a Linux interface name.
246
*/
247
static bool
248
nlmsg_translate_ifname_nla(struct nlattr *nla, struct nl_writer *nw)
249
{
250
char ifname[LINUX_IFNAMSIZ];
251
252
if (nw->ifp == NULL)
253
return (false);
254
(void)ifname_bsd_to_linux_ifp(nw->ifp, ifname, sizeof(ifname));
255
return (nlattr_add_string(nw, IFLA_IFNAME, ifname));
256
}
257
258
#define LINUX_NLA_UNHANDLED -1
259
/*
260
* Translate a FreeBSD attribute to a Linux attribute.
261
* Returns LINUX_NLA_UNHANDLED when the attribute is not processed
262
* and the caller must take care of it, otherwise the result is returned.
263
*/
264
static int
265
nlmsg_translate_all_nla(struct nlmsghdr *hdr, struct nlattr *nla,
266
struct nl_writer *nw)
267
{
268
269
switch (hdr->nlmsg_type) {
270
case NL_RTM_NEWLINK:
271
case NL_RTM_DELLINK:
272
case NL_RTM_GETLINK:
273
switch (nla->nla_type) {
274
case IFLA_IFNAME:
275
return (nlmsg_translate_ifname_nla(nla, nw));
276
default:
277
break;
278
}
279
default:
280
break;
281
}
282
return (LINUX_NLA_UNHANDLED);
283
}
284
285
static bool
286
nlmsg_copy_all_nla(struct nlmsghdr *hdr, int raw_hdrlen, struct nl_writer *nw)
287
{
288
struct nlattr *nla;
289
int ret;
290
291
int hdrlen = NETLINK_ALIGN(raw_hdrlen);
292
int attrs_len = hdr->nlmsg_len - sizeof(struct nlmsghdr) - hdrlen;
293
struct nlattr *nla_head = (struct nlattr *)((char *)(hdr + 1) + hdrlen);
294
295
NLA_FOREACH(nla, nla_head, attrs_len) {
296
RT_LOG(LOG_DEBUG3, "reading attr %d len %d", nla->nla_type, nla->nla_len);
297
if (nla->nla_len < sizeof(struct nlattr)) {
298
return (false);
299
}
300
ret = nlmsg_translate_all_nla(hdr, nla, nw);
301
if (ret == LINUX_NLA_UNHANDLED)
302
ret = nlmsg_copy_nla(nla, nw);
303
if (!ret)
304
return (false);
305
}
306
return (true);
307
}
308
#undef LINUX_NLA_UNHANDLED
309
310
static unsigned int
311
rtnl_if_flags_to_linux(unsigned int if_flags)
312
{
313
unsigned int result = 0;
314
315
for (int i = 0; i < 31; i++) {
316
unsigned int flag = 1 << i;
317
if (!(flag & if_flags))
318
continue;
319
switch (flag) {
320
case IFF_UP:
321
case IFF_BROADCAST:
322
case IFF_DEBUG:
323
case IFF_LOOPBACK:
324
case IFF_POINTOPOINT:
325
case IFF_DRV_RUNNING:
326
case IFF_NOARP:
327
case IFF_PROMISC:
328
case IFF_ALLMULTI:
329
result |= flag;
330
break;
331
case IFF_NEEDSEPOCH:
332
case IFF_DRV_OACTIVE:
333
case IFF_SIMPLEX:
334
case IFF_LINK0:
335
case IFF_LINK1:
336
case IFF_LINK2:
337
case IFF_CANTCONFIG:
338
case IFF_PPROMISC:
339
case IFF_MONITOR:
340
case IFF_STATICARP:
341
case IFF_STICKYARP:
342
case IFF_DYING:
343
/* No Linux analogue */
344
break;
345
case IFF_MULTICAST:
346
result |= 1 << 12;
347
}
348
}
349
return (result);
350
}
351
352
static bool
353
rtnl_newlink_to_linux(struct nlmsghdr *hdr, struct nlpcb *nlp,
354
struct nl_writer *nw)
355
{
356
if (!nlmsg_copy_header(hdr, nw))
357
return (false);
358
359
struct ifinfomsg *ifinfo;
360
ifinfo = nlmsg_copy_next_header(hdr, nw, struct ifinfomsg);
361
362
ifinfo->ifi_family = bsd_to_linux_domain(ifinfo->ifi_family);
363
/* Convert interface type */
364
switch (ifinfo->ifi_type) {
365
case IFT_ETHER:
366
ifinfo->ifi_type = LINUX_ARPHRD_ETHER;
367
break;
368
}
369
ifinfo->ifi_flags = rtnl_if_flags_to_linux(ifinfo->ifi_flags);
370
371
/* Copy attributes unchanged */
372
if (!nlmsg_copy_all_nla(hdr, sizeof(struct ifinfomsg), nw))
373
return (false);
374
375
/* make ip(8) happy */
376
if (!nlattr_add_string(nw, IFLA_QDISC, "noqueue"))
377
return (false);
378
379
if (!nlattr_add_u32(nw, IFLA_TXQLEN, 1000))
380
return (false);
381
382
nlmsg_end(nw);
383
RT_LOG(LOG_DEBUG2, "done processing nw %p", nw);
384
return (true);
385
}
386
387
static bool
388
rtnl_newaddr_to_linux(struct nlmsghdr *hdr, struct nlpcb *nlp,
389
struct nl_writer *nw)
390
{
391
if (!nlmsg_copy_header(hdr, nw))
392
return (false);
393
394
struct ifaddrmsg *ifamsg;
395
ifamsg = nlmsg_copy_next_header(hdr, nw, struct ifaddrmsg);
396
397
ifamsg->ifa_family = bsd_to_linux_domain(ifamsg->ifa_family);
398
/* XXX: fake ifa_flags? */
399
400
/* Copy attributes unchanged */
401
if (!nlmsg_copy_all_nla(hdr, sizeof(struct ifaddrmsg), nw))
402
return (false);
403
404
nlmsg_end(nw);
405
RT_LOG(LOG_DEBUG2, "done processing nw %p", nw);
406
return (true);
407
}
408
409
static bool
410
rtnl_newneigh_to_linux(struct nlmsghdr *hdr, struct nlpcb *nlp,
411
struct nl_writer *nw)
412
{
413
if (!nlmsg_copy_header(hdr, nw))
414
return (false);
415
416
struct ndmsg *ndm;
417
ndm = nlmsg_copy_next_header(hdr, nw, struct ndmsg);
418
419
ndm->ndm_family = bsd_to_linux_domain(ndm->ndm_family);
420
421
/* Copy attributes unchanged */
422
if (!nlmsg_copy_all_nla(hdr, sizeof(struct ndmsg), nw))
423
return (false);
424
425
nlmsg_end(nw);
426
RT_LOG(LOG_DEBUG2, "done processing nw %p", nw);
427
return (true);
428
}
429
430
static bool
431
rtnl_newroute_to_linux(struct nlmsghdr *hdr, struct nlpcb *nlp,
432
struct nl_writer *nw)
433
{
434
if (!nlmsg_copy_header(hdr, nw))
435
return (false);
436
437
struct rtmsg *rtm;
438
rtm = nlmsg_copy_next_header(hdr, nw, struct rtmsg);
439
rtm->rtm_family = bsd_to_linux_domain(rtm->rtm_family);
440
441
struct nlattr *nla;
442
443
int hdrlen = NETLINK_ALIGN(sizeof(struct rtmsg));
444
int attrs_len = hdr->nlmsg_len - sizeof(struct nlmsghdr) - hdrlen;
445
struct nlattr *nla_head = (struct nlattr *)((char *)(hdr + 1) + hdrlen);
446
447
NLA_FOREACH(nla, nla_head, attrs_len) {
448
struct rtattr *rta = (struct rtattr *)nla;
449
//RT_LOG(LOG_DEBUG, "READING attr %d len %d", nla->nla_type, nla->nla_len);
450
if (rta->rta_len < sizeof(struct rtattr)) {
451
break;
452
}
453
454
switch (rta->rta_type) {
455
case NL_RTA_TABLE:
456
{
457
uint32_t fibnum;
458
fibnum = _rta_get_uint32(rta);
459
if (fibnum == 0)
460
fibnum = 254;
461
RT_LOG(LOG_DEBUG3, "XFIBNUM %u", fibnum);
462
if (!nlattr_add_u32(nw, NL_RTA_TABLE, fibnum))
463
return (false);
464
}
465
break;
466
default:
467
if (!nlmsg_copy_nla(nla, nw))
468
return (false);
469
break;
470
}
471
}
472
473
nlmsg_end(nw);
474
RT_LOG(LOG_DEBUG2, "done processing nw %p", nw);
475
return (true);
476
}
477
478
static bool
479
rtnl_to_linux(struct nlmsghdr *hdr, struct nlpcb *nlp, struct nl_writer *nw)
480
{
481
RT_LOG(LOG_DEBUG2, "Got message type %d", hdr->nlmsg_type);
482
483
switch (hdr->nlmsg_type) {
484
case NL_RTM_NEWLINK:
485
case NL_RTM_DELLINK:
486
case NL_RTM_GETLINK:
487
return (rtnl_newlink_to_linux(hdr, nlp, nw));
488
case NL_RTM_NEWADDR:
489
case NL_RTM_DELADDR:
490
return (rtnl_newaddr_to_linux(hdr, nlp, nw));
491
case NL_RTM_NEWROUTE:
492
case NL_RTM_DELROUTE:
493
return (rtnl_newroute_to_linux(hdr, nlp, nw));
494
case NL_RTM_NEWNEIGH:
495
case NL_RTM_DELNEIGH:
496
case NL_RTM_GETNEIGH:
497
return (rtnl_newneigh_to_linux(hdr, nlp, nw));
498
default:
499
RT_LOG(LOG_DEBUG, "[WARN] Passing message type %d untranslated",
500
hdr->nlmsg_type);
501
return (handle_default_out(hdr, nw));
502
}
503
}
504
505
static bool
506
nlmsg_error_to_linux(struct nlmsghdr *hdr, struct nlpcb *nlp, struct nl_writer *nw)
507
{
508
if (!nlmsg_copy_header(hdr, nw))
509
return (false);
510
511
struct nlmsgerr *nlerr;
512
nlerr = nlmsg_copy_next_header(hdr, nw, struct nlmsgerr);
513
nlerr->error = bsd_to_linux_errno(nlerr->error);
514
515
int copied_len = sizeof(struct nlmsghdr) + sizeof(struct nlmsgerr);
516
if (hdr->nlmsg_len == copied_len) {
517
nlmsg_end(nw);
518
return (true);
519
}
520
521
/*
522
* CAP_ACK was not set. Original request needs to be translated.
523
* XXX: implement translation of the original message
524
*/
525
RT_LOG(LOG_DEBUG, "[WARN] Passing ack message type %d untranslated",
526
nlerr->msg.nlmsg_type);
527
char *dst_payload, *src_payload;
528
int copy_len = hdr->nlmsg_len - copied_len;
529
dst_payload = nlmsg_reserve_data(nw, NLMSG_ALIGN(copy_len), char);
530
531
src_payload = (char *)hdr + copied_len;
532
533
memcpy(dst_payload, src_payload, copy_len);
534
nlmsg_end(nw);
535
536
return (true);
537
}
538
539
static bool
540
nlmsg_to_linux(struct nlmsghdr *hdr, struct nlpcb *nlp, struct nl_writer *nw)
541
{
542
if (hdr->nlmsg_type < NLMSG_MIN_TYPE) {
543
switch (hdr->nlmsg_type) {
544
case NLMSG_ERROR:
545
return (nlmsg_error_to_linux(hdr, nlp, nw));
546
case NLMSG_NOOP:
547
case NLMSG_DONE:
548
case NLMSG_OVERRUN:
549
return (handle_default_out(hdr, nw));
550
default:
551
RT_LOG(LOG_DEBUG, "[WARN] Passing message type %d untranslated",
552
hdr->nlmsg_type);
553
return (handle_default_out(hdr, nw));
554
}
555
}
556
557
switch (nlp->nl_proto) {
558
case NETLINK_ROUTE:
559
return (rtnl_to_linux(hdr, nlp, nw));
560
default:
561
return (handle_default_out(hdr, nw));
562
}
563
}
564
565
static struct nl_buf *
566
nlmsgs_to_linux(struct nl_buf *orig, struct nlpcb *nlp, const struct ifnet *ifp)
567
{
568
struct nl_writer nw;
569
u_int offset, msglen;
570
571
if (__predict_false(!nl_writer_unicast(&nw,
572
orig->datalen + SCRATCH_BUFFER_SIZE, nlp, false)))
573
return (NULL);
574
575
nw.ifp = ifp;
576
/* Assume correct headers. Buffer IS mutable */
577
for (offset = 0;
578
offset + sizeof(struct nlmsghdr) <= orig->datalen;
579
offset += msglen) {
580
struct nlmsghdr *hdr = (struct nlmsghdr *)&orig->data[offset];
581
582
msglen = NLMSG_ALIGN(hdr->nlmsg_len);
583
if (!nlmsg_to_linux(hdr, nlp, &nw)) {
584
RT_LOG(LOG_DEBUG, "failed to process msg type %d",
585
hdr->nlmsg_type);
586
nl_buf_free(nw.buf);
587
return (NULL);
588
}
589
}
590
591
RT_LOG(LOG_DEBUG3, "%p: in %u bytes %u messages", __func__,
592
nw.buf->datalen, nw.num_messages);
593
594
return (nw.buf);
595
}
596
597
static struct linux_netlink_provider linux_netlink_v1 = {
598
.msgs_to_linux = nlmsgs_to_linux,
599
.msg_from_linux = nlmsg_from_linux,
600
};
601
602
void
603
linux_netlink_register(void)
604
{
605
linux_netlink_p = &linux_netlink_v1;
606
}
607
608
void
609
linux_netlink_deregister(void)
610
{
611
linux_netlink_p = NULL;
612
}
613
614