Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sbin/ifconfig/ifconfig_netlink.c
39475 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3
*
4
* Copyright (c) 2022 Alexander V. Chernikov <[email protected]>
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
#define _WANT_IFCAP_BIT_NAMES
29
30
#include <stdio.h>
31
#include <stdlib.h>
32
#include <string.h>
33
#include <stdbool.h>
34
#include <err.h>
35
#include <errno.h>
36
#include <netdb.h>
37
38
#include <sys/bitcount.h>
39
#include <sys/param.h>
40
#include <sys/linker.h>
41
#include <sys/module.h>
42
#include <sys/socket.h>
43
#include <sys/sysctl.h>
44
#include <sys/time.h>
45
#include <sys/types.h>
46
47
#include <netinet/in.h>
48
#include <arpa/inet.h>
49
50
#include <net/ethernet.h>
51
#include <net/if.h>
52
#include <net/if_dl.h>
53
#include <net/if_strings.h>
54
#include <net/if_types.h>
55
#include "ifconfig.h"
56
#include "ifconfig_netlink.h"
57
58
static const char *IFFBITS[] = {
59
"UP", /* 00:0x1 IFF_UP*/
60
"BROADCAST", /* 01:0x2 IFF_BROADCAST*/
61
"DEBUG", /* 02:0x4 IFF_DEBUG*/
62
"LOOPBACK", /* 03:0x8 IFF_LOOPBACK*/
63
"POINTOPOINT", /* 04:0x10 IFF_POINTOPOINT*/
64
"NEEDSEPOCH", /* 05:0x20 IFF_NEEDSEPOCH*/
65
"RUNNING", /* 06:0x40 IFF_DRV_RUNNING*/
66
"NOARP", /* 07:0x80 IFF_NOARP*/
67
"PROMISC", /* 08:0x100 IFF_PROMISC*/
68
"ALLMULTI", /* 09:0x200 IFF_ALLMULTI*/
69
"DRV_OACTIVE", /* 10:0x400 IFF_DRV_OACTIVE*/
70
"SIMPLEX", /* 11:0x800 IFF_SIMPLEX*/
71
"LINK0", /* 12:0x1000 IFF_LINK0*/
72
"LINK1", /* 13:0x2000 IFF_LINK1*/
73
"LINK2", /* 14:0x4000 IFF_LINK2*/
74
"MULTICAST", /* 15:0x8000 IFF_MULTICAST*/
75
"CANTCONFIG", /* 16:0x10000 IFF_CANTCONFIG*/
76
"PPROMISC", /* 17:0x20000 IFF_PPROMISC*/
77
"MONITOR", /* 18:0x40000 IFF_MONITOR*/
78
"STATICARP", /* 19:0x80000 IFF_STATICARP*/
79
"STICKYARP", /* 20:0x100000 IFF_STICKYARP*/
80
"DYING", /* 21:0x200000 IFF_DYING*/
81
"RENAMING", /* 22:0x400000 IFF_RENAMING*/
82
"PALLMULTI", /* 23:0x800000 IFF_PALLMULTI*/
83
"LOWER_UP", /* 24:0x1000000 IFF_NETLINK_1*/
84
};
85
86
static void
87
nl_init_socket(struct snl_state *ss)
88
{
89
if (snl_init(ss, NETLINK_ROUTE))
90
return;
91
92
if (modfind("netlink") == -1 && errno == ENOENT) {
93
/* Try to load */
94
if (kldload("netlink") == -1)
95
err(1, "netlink is not loaded and load attempt failed");
96
if (snl_init(ss, NETLINK_ROUTE))
97
return;
98
}
99
100
err(1, "unable to open netlink socket");
101
}
102
103
int
104
ifconfig_nl(if_ctx *ctx, int iscreate,
105
const struct afswtch *uafp)
106
{
107
struct snl_state ss = {};
108
109
nl_init_socket(&ss);
110
ctx->io_ss = &ss;
111
112
int error = ifconfig_ioctl(ctx, iscreate, uafp);
113
114
snl_free(&ss);
115
ctx->io_ss = NULL;
116
117
return (error);
118
}
119
120
struct ifa {
121
struct ifa *next;
122
uint32_t idx;
123
struct snl_parsed_addr addr;
124
};
125
126
struct iface {
127
struct snl_parsed_link link;
128
struct ifa *ifa;
129
uint32_t ifa_count;
130
uint32_t idx;
131
};
132
133
struct ifmap {
134
uint32_t size;
135
uint32_t count;
136
struct iface **ifaces;
137
};
138
139
/*
140
* Returns ifmap ifindex->snl_parsed_link.
141
* Memory is allocated using snl temporary buffers
142
*/
143
static struct ifmap *
144
prepare_ifmap(struct snl_state *ss, const char *ifname)
145
{
146
struct snl_writer nw = {};
147
148
snl_init_writer(ss, &nw);
149
struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETLINK);
150
hdr->nlmsg_flags |= NLM_F_DUMP;
151
snl_reserve_msg_object(&nw, struct ifinfomsg);
152
if (ifname != NULL)
153
snl_add_msg_attr_string(&nw, IFLA_IFNAME, ifname);
154
155
if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ss, hdr))
156
return (NULL);
157
158
uint32_t nlmsg_seq = hdr->nlmsg_seq;
159
struct ifmap *ifmap = snl_allocz(ss, sizeof(*ifmap));
160
struct snl_errmsg_data e = {};
161
162
while ((hdr = snl_read_reply_multi(ss, nlmsg_seq, &e)) != NULL) {
163
struct iface *iface = snl_allocz(ss, sizeof(*iface));
164
165
if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_link_parser, &iface->link))
166
continue;
167
if (iface->link.ifi_index >= ifmap->size) {
168
size_t new_size = MAX(ifmap->size, 32);
169
170
while (new_size <= iface->link.ifi_index + 1)
171
new_size *= 2;
172
173
struct iface **ifaces= snl_allocz(ss, new_size * sizeof(void *));
174
memcpy(ifaces, ifmap->ifaces, ifmap->size * sizeof(void *));
175
ifmap->ifaces = ifaces;
176
ifmap->size = new_size;
177
}
178
ifmap->ifaces[iface->link.ifi_index] = iface;
179
ifmap->count++;
180
iface->idx = ifmap->count;
181
}
182
return (ifmap);
183
}
184
185
uint32_t
186
if_nametoindex_nl(struct snl_state *ss, const char *ifname)
187
{
188
struct snl_writer nw = {};
189
struct snl_parsed_link_simple link = {};
190
191
snl_init_writer(ss, &nw);
192
struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETLINK);
193
snl_reserve_msg_object(&nw, struct ifinfomsg);
194
snl_add_msg_attr_string(&nw, IFLA_IFNAME, ifname);
195
196
if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ss, hdr))
197
return (0);
198
199
hdr = snl_read_reply(ss, hdr->nlmsg_seq);
200
if (hdr->nlmsg_type != NL_RTM_NEWLINK)
201
return (0);
202
if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_link_parser_simple, &link))
203
return (0);
204
205
return (link.ifi_index);
206
}
207
208
ifType
209
convert_iftype(ifType iftype)
210
{
211
switch (iftype) {
212
case IFT_IEEE8023ADLAG:
213
return (IFT_ETHER);
214
case IFT_INFINIBANDLAG:
215
return (IFT_INFINIBAND);
216
default:
217
return (iftype);
218
}
219
}
220
221
static void
222
prepare_ifaddrs(struct snl_state *ss, struct ifmap *ifmap)
223
{
224
struct snl_writer nw = {};
225
226
snl_init_writer(ss, &nw);
227
struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETADDR);
228
hdr->nlmsg_flags |= NLM_F_DUMP;
229
snl_reserve_msg_object(&nw, struct ifaddrmsg);
230
231
if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ss, hdr))
232
return;
233
234
uint32_t nlmsg_seq = hdr->nlmsg_seq;
235
struct snl_errmsg_data e = {};
236
uint32_t count = 0;
237
238
while ((hdr = snl_read_reply_multi(ss, nlmsg_seq, &e)) != NULL) {
239
struct ifa *ifa = snl_allocz(ss, sizeof(*ifa));
240
241
if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_addr_parser, &ifa->addr))
242
continue;
243
244
const uint32_t ifindex = ifa->addr.ifa_index;
245
if (ifindex >= ifmap->size || ifmap->ifaces[ifindex] == NULL)
246
continue;
247
struct iface *iface = ifmap->ifaces[ifindex];
248
ifa->next = iface->ifa;
249
ifa->idx = ++count;
250
iface->ifa = ifa;
251
iface->ifa_count++;
252
}
253
}
254
255
static bool
256
match_iface(struct ifconfig_args *args, struct iface *iface)
257
{
258
if_link_t *link = &iface->link;
259
260
if (args->ifname != NULL && strcmp(args->ifname, link->ifla_ifname))
261
return (false);
262
263
if (!match_if_flags(args, link->ifi_flags))
264
return (false);
265
266
if (!group_member(link->ifla_ifname, args->matchgroup, args->nogroup))
267
return (false);
268
269
if (args->afp == NULL)
270
return (true);
271
272
if (!strcmp(args->afp->af_name, "ether")) {
273
if (link->ifla_address == NULL)
274
return (false);
275
276
struct sockaddr_dl sdl = {
277
.sdl_len = sizeof(struct sockaddr_dl),
278
.sdl_family = AF_LINK,
279
.sdl_type = convert_iftype(link->ifi_type),
280
.sdl_alen = NLA_DATA_LEN(link->ifla_address),
281
};
282
return (match_ether(&sdl));
283
} else if (args->afp->af_af == AF_LINK)
284
/*
285
* The rtnetlink(4) RTM_GETADDR does not list link level
286
* addresses, so latter cycle won't match anything. Short
287
* circuit on RTM_GETLINK has provided us an address.
288
*/
289
return (link->ifla_address != NULL);
290
291
for (struct ifa *ifa = iface->ifa; ifa != NULL; ifa = ifa->next) {
292
if (args->afp->af_af == ifa->addr.ifa_family)
293
return (true);
294
}
295
296
return (false);
297
}
298
299
/* Sort according to the kernel-provided order */
300
static int
301
cmp_iface(const void *_a, const void *_b)
302
{
303
const struct iface *a = *((const void * const *)_a);
304
const struct iface *b = *((const void * const *)_b);
305
306
return ((a->idx > b->idx) * 2 - 1);
307
}
308
309
static int
310
cmp_ifaddr(const void *_a, const void *_b)
311
{
312
const struct ifa *a = *((const void * const *)_a);
313
const struct ifa *b = *((const void * const *)_b);
314
315
if (a->addr.ifa_family != b->addr.ifa_family)
316
return ((a->addr.ifa_family > b->addr.ifa_family) * 2 - 1);
317
return ((a->idx > b->idx) * 2 - 1);
318
}
319
320
static void
321
sort_iface_ifaddrs(struct snl_state *ss, struct iface *iface)
322
{
323
if (iface->ifa_count == 0)
324
return;
325
326
struct ifa **sorted_ifaddrs = snl_allocz(ss, iface->ifa_count * sizeof(void *));
327
struct ifa *ifa = iface->ifa;
328
329
for (uint32_t i = 0; i < iface->ifa_count; i++) {
330
struct ifa *ifa_next = ifa->next;
331
332
sorted_ifaddrs[i] = ifa;
333
ifa->next = NULL;
334
ifa = ifa_next;
335
}
336
qsort(sorted_ifaddrs, iface->ifa_count, sizeof(void *), cmp_ifaddr);
337
ifa = sorted_ifaddrs[0];
338
iface->ifa = ifa;
339
for (uint32_t i = 1; i < iface->ifa_count; i++) {
340
ifa->next = sorted_ifaddrs[i];
341
ifa = sorted_ifaddrs[i];
342
}
343
}
344
345
static void
346
print_ifcaps(if_ctx *ctx, if_link_t *link)
347
{
348
uint32_t sz_u32 = roundup2(link->iflaf_caps.nla_bitset_size, 32) / 32;
349
350
if (sz_u32 > 0) {
351
uint32_t *caps = link->iflaf_caps.nla_bitset_value;
352
353
printf("\toptions=%x", caps[0]);
354
print_bits("IFCAPS", caps, sz_u32, ifcap_bit_names, nitems(ifcap_bit_names));
355
putchar('\n');
356
}
357
358
if (ctx->args->supmedia && sz_u32 > 0) {
359
uint32_t *caps = link->iflaf_caps.nla_bitset_mask;
360
361
printf("\tcapabilities=%x", caps[0]);
362
print_bits("IFCAPS", caps, sz_u32, ifcap_bit_names, nitems(ifcap_bit_names));
363
putchar('\n');
364
}
365
}
366
367
static void
368
status_nl(if_ctx *ctx, struct iface *iface)
369
{
370
if_link_t *link = &iface->link;
371
struct ifconfig_args *args = ctx->args;
372
char *drivername = NULL;
373
374
printf("%s: ", link->ifla_ifname);
375
376
printf("flags=%x", link->ifi_flags);
377
print_bits("IFF", &link->ifi_flags, 1, IFFBITS, nitems(IFFBITS));
378
379
print_metric(ctx);
380
printf(" mtu %d\n", link->ifla_mtu);
381
382
if (link->ifla_ifalias != NULL)
383
printf("\tdescription: %s\n", link->ifla_ifalias);
384
385
print_ifcaps(ctx, link);
386
tunnel_status(ctx);
387
388
if (args->allfamilies | (args->afp != NULL && args->afp->af_af == AF_LINK)) {
389
/* Start with link-level */
390
const struct afswtch *p = af_getbyfamily(AF_LINK);
391
if (p != NULL && link->ifla_address != NULL)
392
p->af_status(ctx, link, NULL);
393
}
394
395
sort_iface_ifaddrs(ctx->io_ss, iface);
396
397
for (struct ifa *ifa = iface->ifa; ifa != NULL; ifa = ifa->next) {
398
if (args->allfamilies) {
399
const struct afswtch *p = af_getbyfamily(ifa->addr.ifa_family);
400
401
if (p != NULL)
402
p->af_status(ctx, link, &ifa->addr);
403
} else if (args->afp->af_af == ifa->addr.ifa_family) {
404
const struct afswtch *p = args->afp;
405
406
p->af_status(ctx, link, &ifa->addr);
407
}
408
}
409
410
/* TODO: convert to netlink */
411
if (args->allfamilies)
412
af_other_status(ctx);
413
else if (args->afp->af_other_status != NULL)
414
args->afp->af_other_status(ctx);
415
416
print_ifstatus(ctx);
417
if (args->drivername || args->verbose) {
418
if (ifconfig_get_orig_name(lifh, link->ifla_ifname,
419
&drivername) != 0) {
420
if (ifconfig_err_errtype(lifh) == OTHER)
421
fprintf(stderr, "get original name: %s\n",
422
strerror(ifconfig_err_errno(lifh)));
423
else
424
fprintf(stderr,
425
"get original name: error type %d\n",
426
ifconfig_err_errtype(lifh));
427
exit_code = 1;
428
}
429
if (drivername != NULL)
430
printf("\tdrivername: %s\n", drivername);
431
free(drivername);
432
}
433
if (args->verbose > 0)
434
sfp_status(ctx);
435
}
436
437
static int
438
get_local_socket(void)
439
{
440
int s = socket(AF_LOCAL, SOCK_DGRAM, 0);
441
442
if (s < 0)
443
err(1, "socket(family %u,SOCK_DGRAM)", AF_LOCAL);
444
return (s);
445
}
446
447
void
448
list_interfaces_nl(struct ifconfig_args *args)
449
{
450
struct snl_state ss = {};
451
struct ifconfig_context _ctx = {
452
.args = args,
453
.io_s = get_local_socket(),
454
.io_ss = &ss,
455
};
456
struct ifconfig_context *ctx = &_ctx;
457
458
nl_init_socket(&ss);
459
460
struct ifmap *ifmap = prepare_ifmap(&ss, args->ifname);
461
struct iface **sorted_ifaces = snl_allocz(&ss, ifmap->count * sizeof(void *));
462
for (uint32_t i = 0, num = 0; i < ifmap->size; i++) {
463
if (ifmap->ifaces[i] != NULL) {
464
sorted_ifaces[num++] = ifmap->ifaces[i];
465
if (num == ifmap->count)
466
break;
467
}
468
}
469
qsort(sorted_ifaces, ifmap->count, sizeof(void *), cmp_iface);
470
prepare_ifaddrs(&ss, ifmap);
471
472
for (uint32_t i = 0, num = 0; i < ifmap->count; i++) {
473
struct iface *iface = sorted_ifaces[i];
474
475
if (!match_iface(args, iface))
476
continue;
477
478
ctx->ifname = iface->link.ifla_ifname;
479
480
if (args->namesonly) {
481
if (num++ != 0)
482
printf(" ");
483
fputs(iface->link.ifla_ifname, stdout);
484
} else if (args->argc == 0)
485
status_nl(ctx, iface);
486
else
487
ifconfig_ioctl(ctx, 0, args->afp);
488
}
489
if (args->namesonly)
490
printf("\n");
491
492
close(ctx->io_s);
493
snl_free(&ss);
494
}
495
496
497