]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sbin/ifconfig/ifconfig_netlink.c
Revert "ifconfig: switch IPv4/IPv6 address manipulations to Netlink"
[FreeBSD/FreeBSD.git] / sbin / ifconfig / ifconfig_netlink.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2022 Alexander V. Chernikov <melifaro@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 <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <stdbool.h>
32 #include <err.h>
33 #include <errno.h>
34 #include <netdb.h>
35
36 #include <sys/bitcount.h>
37 #include <sys/param.h>
38 #include <sys/linker.h>
39 #include <sys/module.h>
40 #include <sys/socket.h>
41 #include <sys/sysctl.h>
42 #include <sys/time.h>
43 #include <sys/types.h>
44
45 #include <netinet/in.h>
46 #include <arpa/inet.h>
47
48 #include <net/ethernet.h>
49 #include <net/if.h>
50 #include <net/if_dl.h>
51 #include <net/if_types.h>
52 #include "ifconfig.h"
53 #include "ifconfig_netlink.h"
54
55 static const char       *IFFBITS[] = {
56         "UP",                   /* 00:0x1 IFF_UP*/
57         "BROADCAST",            /* 01:0x2 IFF_BROADCAST*/
58         "DEBUG",                /* 02:0x4 IFF_DEBUG*/
59         "LOOPBACK",             /* 03:0x8 IFF_LOOPBACK*/
60         "POINTOPOINT",          /* 04:0x10 IFF_POINTOPOINT*/
61         "NEEDSEPOCH",           /* 05:0x20 IFF_NEEDSEPOCH*/
62         "RUNNING",              /* 06:0x40 IFF_DRV_RUNNING*/
63         "NOARP",                /* 07:0x80 IFF_NOARP*/
64         "PROMISC",              /* 08:0x100 IFF_PROMISC*/
65         "ALLMULTI",             /* 09:0x200 IFF_ALLMULTI*/
66         "DRV_OACTIVE",          /* 10:0x400 IFF_DRV_OACTIVE*/
67         "SIMPLEX",              /* 11:0x800 IFF_SIMPLEX*/
68         "LINK0",                /* 12:0x1000 IFF_LINK0*/
69         "LINK1",                /* 13:0x2000 IFF_LINK1*/
70         "LINK2",                /* 14:0x4000 IFF_LINK2*/
71         "MULTICAST",            /* 15:0x8000 IFF_MULTICAST*/
72         "CANTCONFIG",           /* 16:0x10000 IFF_CANTCONFIG*/
73         "PPROMISC",             /* 17:0x20000 IFF_PPROMISC*/
74         "MONITOR",              /* 18:0x40000 IFF_MONITOR*/
75         "STATICARP",            /* 19:0x80000 IFF_STATICARP*/
76         "STICKYARP",            /* 20:0x100000 IFF_STICKYARP*/
77         "DYING",                /* 21:0x200000 IFF_DYING*/
78         "RENAMING",             /* 22:0x400000 IFF_RENAMING*/
79         "NOGROUP",              /* 23:0x800000 IFF_NOGROUP*/
80         "LOWER_UP",             /* 24:0x1000000 IFF_NETLINK_1*/
81 };
82
83 static void
84 print_bits(const char *btype, uint32_t *v, const int v_count,
85     const char **names, const int n_count)
86 {
87         int num = 0;
88
89         for (int i = 0; i < v_count * 32; i++) {
90                 bool is_set = v[i / 32] & (1 << (i % 32));
91                 if (i == 31)
92                         v++;
93                 if (is_set) {
94                         if (num++ == 0)
95                                 printf("<");
96                         if (num != 1)
97                                 printf(",");
98                         if (i < n_count)
99                                 printf("%s", names[i]);
100                         else
101                                 printf("%s_%d", btype, i);
102                 }
103         }
104         if (num > 0)
105                 printf(">");
106 }       
107
108 static void
109 nl_init_socket(struct snl_state *ss)
110 {
111         if (snl_init(ss, NETLINK_ROUTE))
112                 return;
113
114         if (modfind("netlink") == -1 && errno == ENOENT) {
115                 /* Try to load */
116                 if (kldload("netlink") == -1)
117                         err(1, "netlink is not loaded and load attempt failed");
118                 if (snl_init(ss, NETLINK_ROUTE))
119                         return;
120         }
121
122         err(1, "unable to open netlink socket");
123 }
124
125 struct ifa {
126         struct ifa              *next;
127         uint32_t                count;
128         uint32_t                idx;
129         struct snl_parsed_addr  addr;
130 };
131
132 struct iface {
133         struct snl_parsed_link  link;
134         struct ifa              *ifa;
135         uint32_t                ifa_count;
136         uint32_t                idx;
137 };
138
139 struct ifmap {
140         uint32_t                size;
141         uint32_t                count;
142         struct iface            **ifaces;
143 };
144
145 /*
146  * Returns ifmap ifindex->snl_parsed_link.
147  * Memory is allocated using snl temporary buffers
148  */
149 static struct ifmap *
150 prepare_ifmap(struct snl_state *ss)
151 {
152         struct snl_writer nw = {};
153
154         snl_init_writer(ss, &nw);
155         struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETLINK);
156         hdr->nlmsg_flags |= NLM_F_DUMP;
157         snl_reserve_msg_object(&nw, struct ifinfomsg);
158
159         if (!snl_finalize_msg(&nw) || !snl_send_message(ss, hdr))
160                 return (NULL);
161
162         uint32_t nlmsg_seq = hdr->nlmsg_seq;
163         struct ifmap *ifmap = snl_allocz(ss, sizeof(*ifmap));
164         struct snl_errmsg_data e = {};
165
166         while ((hdr = snl_read_reply_multi(ss, nlmsg_seq, &e)) != NULL) {
167                 struct iface *iface = snl_allocz(ss, sizeof(*iface));
168
169                 if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_link_parser, &iface->link))
170                         continue;
171                 if (iface->link.ifi_index >= ifmap->size) {
172                         size_t new_size = MAX(ifmap->size, 32);
173
174                         while (new_size <= iface->link.ifi_index + 1)
175                                 new_size *= 2;
176
177                         struct iface **ifaces= snl_allocz(ss, new_size * sizeof(void *));
178                         memcpy(ifaces, ifmap->ifaces, ifmap->size * sizeof(void *));
179                         ifmap->ifaces = ifaces;
180                         ifmap->size = new_size;
181                 }
182                 ifmap->ifaces[iface->link.ifi_index] = iface;
183                 ifmap->count++;
184                 iface->idx = ifmap->count;
185         }
186         return (ifmap);
187 }
188
189 static void
190 prepare_ifaddrs(struct snl_state *ss, struct ifmap *ifmap)
191 {
192         struct snl_writer nw = {};
193
194         snl_init_writer(ss, &nw);
195         struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETADDR);
196         hdr->nlmsg_flags |= NLM_F_DUMP;
197         snl_reserve_msg_object(&nw, struct ifaddrmsg);
198
199         if (!snl_finalize_msg(&nw) || !snl_send_message(ss, hdr))
200                 return;
201
202         uint32_t nlmsg_seq = hdr->nlmsg_seq;
203         struct snl_errmsg_data e = {};
204         uint32_t count = 0;
205
206         while ((hdr = snl_read_reply_multi(ss, nlmsg_seq, &e)) != NULL) {
207                 struct ifa *ifa = snl_allocz(ss, sizeof(*ifa));
208
209                 if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_addr_parser, &ifa->addr))
210                         continue;
211
212                 const uint32_t ifindex = ifa->addr.ifa_index;
213                 if (ifindex >= ifmap->size || ifmap->ifaces[ifindex] == NULL)
214                         continue;
215                 struct iface *iface = ifmap->ifaces[ifindex];
216                 ifa->next = iface->ifa;
217                 ifa->count = ++count;
218                 iface->ifa = ifa;
219                 iface->ifa_count++;
220         }
221 }
222
223 static bool
224 match_iface(struct ifconfig_args *args, struct iface *iface)
225 {
226         if_link_t *link = &iface->link;
227
228         if (args->ifname != NULL && strcmp(args->ifname, link->ifla_ifname))
229                 return (false);
230
231         if (!match_if_flags(args, link->ifi_flags))
232                 return (false);
233
234         if (!group_member(link->ifla_ifname, args->matchgroup, args->nogroup))
235                 return (false);
236
237         if (args->afp == NULL)
238                 return (true);
239
240         if (!strcmp(args->afp->af_name, "ether")) {
241                 if (link->ifla_address == NULL)
242                         return (false);
243
244                 struct sockaddr_dl sdl = {
245                         .sdl_len = sizeof(struct sockaddr_dl),
246                         .sdl_family = AF_LINK,
247                         .sdl_type = link->ifi_type,
248                         .sdl_alen = NLA_DATA_LEN(link->ifla_address),
249                 };
250                 return (match_ether(&sdl));
251         }
252         
253         for (struct ifa *ifa = iface->ifa; ifa != NULL; ifa = ifa->next) {
254                 if (args->afp->af_af == ifa->addr.ifa_family)
255                         return (true);
256         }
257
258         return (false);
259 }
260
261 /* Sort according to the kernel-provided order */
262 static int
263 cmp_iface(const void *_a, const void *_b)
264 {
265         const struct iface *a = *((const void * const *)_a);
266         const struct iface *b = *((const void * const *)_b);
267
268         return ((a->idx > b->idx) * 2 - 1);
269 }
270
271 static int
272 cmp_ifaddr(const void *_a, const void *_b)
273 {
274         const struct ifa *a = *((const void * const *)_a);
275         const struct ifa *b = *((const void * const *)_b);
276
277         if (a->addr.ifa_family != b->addr.ifa_family)
278                 return ((a->addr.ifa_family > b->addr.ifa_family) * 2 - 1);
279         return ((a->idx > b->idx) * 2 - 1);
280 }
281
282 static void
283 sort_iface_ifaddrs(struct snl_state *ss, struct iface *iface)
284 {
285         if (iface->ifa_count == 0)
286                 return;
287
288         struct ifa **sorted_ifaddrs = snl_allocz(ss, iface->ifa_count * sizeof(void *));
289         struct ifa *ifa = iface->ifa;
290
291         for (int i = 0; i < iface->ifa_count; i++) {
292                 struct ifa *ifa_next = ifa->next;
293
294                 sorted_ifaddrs[i] = ifa;
295                 ifa->next = NULL;
296                 ifa = ifa_next;
297         }
298         qsort(sorted_ifaddrs, iface->ifa_count, sizeof(void *), cmp_ifaddr);
299         ifa = sorted_ifaddrs[0];
300         iface->ifa = ifa;
301         for (int i = 1; i < iface->ifa_count; i++) {
302                 ifa->next = sorted_ifaddrs[i];
303                 ifa = sorted_ifaddrs[i];
304         }
305 }
306
307 static void
308 status_nl(struct ifconfig_args *args, struct io_handler *h, struct iface *iface)
309 {
310         if_link_t *link = &iface->link;
311
312         printf("%s: ", link->ifla_ifname);
313
314         printf("flags=%x", link->ifi_flags);
315         print_bits("IFF", &link->ifi_flags, 1, IFFBITS, nitems(IFFBITS));
316
317         print_metric(h->s);
318         printf(" mtu %d\n", link->ifla_mtu);
319
320         if (link->ifla_ifalias != NULL)
321                 printf("\tdescription: %s\n", link->ifla_ifalias);
322
323         /* TODO: convert to netlink */
324         strlcpy(ifr.ifr_name, link->ifla_ifname, sizeof(ifr.ifr_name));
325         print_ifcap(args, h->s);
326         tunnel_status(h->s);
327
328         if (args->allfamilies | (args->afp != NULL && args->afp->af_af == AF_LINK)) {
329                 /* Start with link-level */
330                 const struct afswtch *p = af_getbyfamily(AF_LINK);
331                 if (p != NULL && link->ifla_address != NULL)
332                         p->af_status_nl(args, h, link, NULL);
333         }
334
335         sort_iface_ifaddrs(h->ss, iface);
336
337         for (struct ifa *ifa = iface->ifa; ifa != NULL; ifa = ifa->next) {
338                 if (args->allfamilies) {
339                         const struct afswtch *p = af_getbyfamily(ifa->addr.ifa_family);
340
341                         if (p != NULL)
342                                 p->af_status_nl(args, h, link, &ifa->addr);
343                 } else if (args->afp->af_af == ifa->addr.ifa_family) {
344                         const struct afswtch *p = args->afp;
345
346                         p->af_status_nl(args, h, link, &ifa->addr);
347                 }
348         }
349
350         /* TODO: convert to netlink */
351         if (args->allfamilies)
352                 af_other_status(h->s);
353         else if (args->afp->af_other_status != NULL)
354                 args->afp->af_other_status(h->s);
355
356         print_ifstatus(h->s);
357         if (args->verbose > 0)
358                 sfp_status(h->s, &ifr, args->verbose);
359 }
360
361 static int
362 get_local_socket(void)
363 {
364         int s = socket(AF_LOCAL, SOCK_DGRAM, 0);
365         
366         if (s < 0)
367                 err(1, "socket(family %u,SOCK_DGRAM)", AF_LOCAL);
368         return (s);
369 }
370
371 static void
372 set_global_ifname(if_link_t *link)
373 {
374         int iflen = strlcpy(name, link->ifla_ifname, sizeof(name));
375         if (iflen >= sizeof(name))
376                 errx(1, "%s: cloning name too long", link->ifla_ifname);
377         strlcpy(ifr.ifr_name, link->ifla_ifname, sizeof(ifr.ifr_name));
378 }
379
380 void
381 list_interfaces_nl(struct ifconfig_args *args)
382 {
383         struct snl_state ss = {};
384
385         nl_init_socket(&ss);
386
387         struct ifmap *ifmap = prepare_ifmap(&ss);
388         struct iface **sorted_ifaces = snl_allocz(&ss, ifmap->count * sizeof(void *));
389         for (int i = 0, num = 0; i < ifmap->size; i++) {
390                 if (ifmap->ifaces[i] != NULL) {
391                         sorted_ifaces[num++] = ifmap->ifaces[i];
392                         if (num == ifmap->count)
393                                 break;
394                 }
395         }
396         qsort(sorted_ifaces, ifmap->count, sizeof(void *), cmp_iface);
397         prepare_ifaddrs(&ss, ifmap);
398
399         struct io_handler h = {
400                 .s = get_local_socket(),
401                 .ss = &ss,
402         };
403
404         for (int i = 0, num = 0; i < ifmap->count; i++) {
405                 struct iface *iface = sorted_ifaces[i];
406
407                 if (!match_iface(args, iface))
408                         continue;
409
410                 set_global_ifname(&iface->link);
411
412                 if (args->namesonly) {
413                         if (num++ != 0)
414                                 printf(" ");
415                         fputs(iface->link.ifla_ifname, stdout);
416                 } else if (args->argc == 0)
417                         status_nl(args, &h, iface);
418                 else
419                         ifconfig(args->argc, args->argv, 0, args->afp);
420         }
421         if (args->namesonly)
422                 printf("\n");
423
424         close(h.s);
425         snl_free(&ss);
426 }
427