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 int 126 ifconfig_wrapper_nl(struct ifconfig_args *args, int iscreate, 127 const struct afswtch *uafp) 128 { 129 struct snl_state ss = {}; 130 struct ifconfig_context ctx = { 131 .args = args, 132 .io_s = -1, 133 .io_ss = &ss, 134 }; 135 136 nl_init_socket(&ss); 137 138 int error = ifconfig(&ctx, iscreate, uafp); 139 140 snl_free(&ss); 141 142 return (error); 143 } 144 145 struct ifa { 146 struct ifa *next; 147 uint32_t idx; 148 struct snl_parsed_addr addr; 149 }; 150 151 struct iface { 152 struct snl_parsed_link link; 153 struct ifa *ifa; 154 uint32_t ifa_count; 155 uint32_t idx; 156 }; 157 158 struct ifmap { 159 uint32_t size; 160 uint32_t count; 161 struct iface **ifaces; 162 }; 163 164 /* 165 * Returns ifmap ifindex->snl_parsed_link. 166 * Memory is allocated using snl temporary buffers 167 */ 168 static struct ifmap * 169 prepare_ifmap(struct snl_state *ss) 170 { 171 struct snl_writer nw = {}; 172 173 snl_init_writer(ss, &nw); 174 struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETLINK); 175 hdr->nlmsg_flags |= NLM_F_DUMP; 176 snl_reserve_msg_object(&nw, struct ifinfomsg); 177 178 if (!snl_finalize_msg(&nw) || !snl_send_message(ss, hdr)) 179 return (NULL); 180 181 uint32_t nlmsg_seq = hdr->nlmsg_seq; 182 struct ifmap *ifmap = snl_allocz(ss, sizeof(*ifmap)); 183 struct snl_errmsg_data e = {}; 184 185 while ((hdr = snl_read_reply_multi(ss, nlmsg_seq, &e)) != NULL) { 186 struct iface *iface = snl_allocz(ss, sizeof(*iface)); 187 188 if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_link_parser, &iface->link)) 189 continue; 190 if (iface->link.ifi_index >= ifmap->size) { 191 size_t new_size = MAX(ifmap->size, 32); 192 193 while (new_size <= iface->link.ifi_index + 1) 194 new_size *= 2; 195 196 struct iface **ifaces= snl_allocz(ss, new_size * sizeof(void *)); 197 memcpy(ifaces, ifmap->ifaces, ifmap->size * sizeof(void *)); 198 ifmap->ifaces = ifaces; 199 ifmap->size = new_size; 200 } 201 ifmap->ifaces[iface->link.ifi_index] = iface; 202 ifmap->count++; 203 iface->idx = ifmap->count; 204 } 205 return (ifmap); 206 } 207 208 uint32_t 209 if_nametoindex_nl(struct snl_state *ss, const char *ifname) 210 { 211 struct snl_writer nw = {}; 212 struct snl_parsed_link_simple link = {}; 213 214 snl_init_writer(ss, &nw); 215 struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETLINK); 216 snl_reserve_msg_object(&nw, struct ifinfomsg); 217 snl_add_msg_attr_string(&nw, IFLA_IFNAME, ifname); 218 219 if (!snl_finalize_msg(&nw) || !snl_send_message(ss, hdr)) 220 return (0); 221 222 hdr = snl_read_reply(ss, hdr->nlmsg_seq); 223 if (hdr->nlmsg_type != NL_RTM_NEWLINK) 224 return (0); 225 if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_link_parser_simple, &link)) 226 return (0); 227 228 return (link.ifi_index); 229 } 230 231 static void 232 prepare_ifaddrs(struct snl_state *ss, struct ifmap *ifmap) 233 { 234 struct snl_writer nw = {}; 235 236 snl_init_writer(ss, &nw); 237 struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETADDR); 238 hdr->nlmsg_flags |= NLM_F_DUMP; 239 snl_reserve_msg_object(&nw, struct ifaddrmsg); 240 241 if (!snl_finalize_msg(&nw) || !snl_send_message(ss, hdr)) 242 return; 243 244 uint32_t nlmsg_seq = hdr->nlmsg_seq; 245 struct snl_errmsg_data e = {}; 246 uint32_t count = 0; 247 248 while ((hdr = snl_read_reply_multi(ss, nlmsg_seq, &e)) != NULL) { 249 struct ifa *ifa = snl_allocz(ss, sizeof(*ifa)); 250 251 if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_addr_parser, &ifa->addr)) 252 continue; 253 254 const uint32_t ifindex = ifa->addr.ifa_index; 255 if (ifindex >= ifmap->size || ifmap->ifaces[ifindex] == NULL) 256 continue; 257 struct iface *iface = ifmap->ifaces[ifindex]; 258 ifa->next = iface->ifa; 259 ifa->idx = ++count; 260 iface->ifa = ifa; 261 iface->ifa_count++; 262 } 263 } 264 265 static bool 266 match_iface(struct ifconfig_args *args, struct iface *iface) 267 { 268 if_link_t *link = &iface->link; 269 270 if (args->ifname != NULL && strcmp(args->ifname, link->ifla_ifname)) 271 return (false); 272 273 if (!match_if_flags(args, link->ifi_flags)) 274 return (false); 275 276 if (!group_member(link->ifla_ifname, args->matchgroup, args->nogroup)) 277 return (false); 278 279 if (args->afp == NULL) 280 return (true); 281 282 if (!strcmp(args->afp->af_name, "ether")) { 283 if (link->ifla_address == NULL) 284 return (false); 285 286 struct sockaddr_dl sdl = { 287 .sdl_len = sizeof(struct sockaddr_dl), 288 .sdl_family = AF_LINK, 289 .sdl_type = link->ifi_type, 290 .sdl_alen = NLA_DATA_LEN(link->ifla_address), 291 }; 292 return (match_ether(&sdl)); 293 } 294 295 for (struct ifa *ifa = iface->ifa; ifa != NULL; ifa = ifa->next) { 296 if (args->afp->af_af == ifa->addr.ifa_family) 297 return (true); 298 } 299 300 return (false); 301 } 302 303 /* Sort according to the kernel-provided order */ 304 static int 305 cmp_iface(const void *_a, const void *_b) 306 { 307 const struct iface *a = *((const void * const *)_a); 308 const struct iface *b = *((const void * const *)_b); 309 310 return ((a->idx > b->idx) * 2 - 1); 311 } 312 313 static int 314 cmp_ifaddr(const void *_a, const void *_b) 315 { 316 const struct ifa *a = *((const void * const *)_a); 317 const struct ifa *b = *((const void * const *)_b); 318 319 if (a->addr.ifa_family != b->addr.ifa_family) 320 return ((a->addr.ifa_family > b->addr.ifa_family) * 2 - 1); 321 return ((a->idx > b->idx) * 2 - 1); 322 } 323 324 static void 325 sort_iface_ifaddrs(struct snl_state *ss, struct iface *iface) 326 { 327 if (iface->ifa_count == 0) 328 return; 329 330 struct ifa **sorted_ifaddrs = snl_allocz(ss, iface->ifa_count * sizeof(void *)); 331 struct ifa *ifa = iface->ifa; 332 333 for (uint32_t i = 0; i < iface->ifa_count; i++) { 334 struct ifa *ifa_next = ifa->next; 335 336 sorted_ifaddrs[i] = ifa; 337 ifa->next = NULL; 338 ifa = ifa_next; 339 } 340 qsort(sorted_ifaddrs, iface->ifa_count, sizeof(void *), cmp_ifaddr); 341 ifa = sorted_ifaddrs[0]; 342 iface->ifa = ifa; 343 for (uint32_t i = 1; i < iface->ifa_count; i++) { 344 ifa->next = sorted_ifaddrs[i]; 345 ifa = sorted_ifaddrs[i]; 346 } 347 } 348 349 static void 350 status_nl(if_ctx *ctx, struct iface *iface) 351 { 352 if_link_t *link = &iface->link; 353 struct ifconfig_args *args = ctx->args; 354 355 printf("%s: ", link->ifla_ifname); 356 357 printf("flags=%x", link->ifi_flags); 358 print_bits("IFF", &link->ifi_flags, 1, IFFBITS, nitems(IFFBITS)); 359 360 print_metric(ctx->io_s); 361 printf(" mtu %d\n", link->ifla_mtu); 362 363 if (link->ifla_ifalias != NULL) 364 printf("\tdescription: %s\n", link->ifla_ifalias); 365 366 /* TODO: convert to netlink */ 367 strlcpy(ifr.ifr_name, link->ifla_ifname, sizeof(ifr.ifr_name)); 368 print_ifcap(args, ctx->io_s); 369 tunnel_status(ctx->io_s); 370 371 if (args->allfamilies | (args->afp != NULL && args->afp->af_af == AF_LINK)) { 372 /* Start with link-level */ 373 const struct afswtch *p = af_getbyfamily(AF_LINK); 374 if (p != NULL && link->ifla_address != NULL) 375 p->af_status(ctx, link, NULL); 376 } 377 378 sort_iface_ifaddrs(ctx->io_ss, iface); 379 380 for (struct ifa *ifa = iface->ifa; ifa != NULL; ifa = ifa->next) { 381 if (args->allfamilies) { 382 const struct afswtch *p = af_getbyfamily(ifa->addr.ifa_family); 383 384 if (p != NULL) 385 p->af_status(ctx, link, &ifa->addr); 386 } else if (args->afp->af_af == ifa->addr.ifa_family) { 387 const struct afswtch *p = args->afp; 388 389 p->af_status(ctx, link, &ifa->addr); 390 } 391 } 392 393 /* TODO: convert to netlink */ 394 if (args->allfamilies) 395 af_other_status(ctx); 396 else if (args->afp->af_other_status != NULL) 397 args->afp->af_other_status(ctx); 398 399 print_ifstatus(ctx->io_s); 400 if (args->verbose > 0) 401 sfp_status(ctx); 402 } 403 404 static int 405 get_local_socket(void) 406 { 407 int s = socket(AF_LOCAL, SOCK_DGRAM, 0); 408 409 if (s < 0) 410 err(1, "socket(family %u,SOCK_DGRAM)", AF_LOCAL); 411 return (s); 412 } 413 414 static void 415 set_global_ifname(if_link_t *link) 416 { 417 size_t iflen = strlcpy(name, link->ifla_ifname, sizeof(name)); 418 419 if (iflen >= sizeof(name)) 420 errx(1, "%s: cloning name too long", link->ifla_ifname); 421 strlcpy(ifr.ifr_name, link->ifla_ifname, sizeof(ifr.ifr_name)); 422 } 423 424 void 425 list_interfaces_nl(struct ifconfig_args *args) 426 { 427 struct snl_state ss = {}; 428 struct ifconfig_context _ctx = { 429 .args = args, 430 .io_s = get_local_socket(), 431 .io_ss = &ss, 432 }; 433 struct ifconfig_context *ctx = &_ctx; 434 435 nl_init_socket(&ss); 436 437 struct ifmap *ifmap = prepare_ifmap(&ss); 438 struct iface **sorted_ifaces = snl_allocz(&ss, ifmap->count * sizeof(void *)); 439 for (uint32_t i = 0, num = 0; i < ifmap->size; i++) { 440 if (ifmap->ifaces[i] != NULL) { 441 sorted_ifaces[num++] = ifmap->ifaces[i]; 442 if (num == ifmap->count) 443 break; 444 } 445 } 446 qsort(sorted_ifaces, ifmap->count, sizeof(void *), cmp_iface); 447 prepare_ifaddrs(&ss, ifmap); 448 449 for (uint32_t i = 0, num = 0; i < ifmap->count; i++) { 450 struct iface *iface = sorted_ifaces[i]; 451 452 if (!match_iface(args, iface)) 453 continue; 454 455 set_global_ifname(&iface->link); 456 457 if (args->namesonly) { 458 if (num++ != 0) 459 printf(" "); 460 fputs(iface->link.ifla_ifname, stdout); 461 } else if (args->argc == 0) 462 status_nl(ctx, iface); 463 else 464 ifconfig(ctx, 0, args->afp); 465 } 466 if (args->namesonly) 467 printf("\n"); 468 469 close(ctx->io_s); 470 snl_free(&ss); 471 } 472 473