1*2ea71e24SRobert Mustacchi /*
2*2ea71e24SRobert Mustacchi * This file and its contents are supplied under the terms of the
3*2ea71e24SRobert Mustacchi * Common Development and Distribution License ("CDDL"), version 1.0.
4*2ea71e24SRobert Mustacchi * You may only use this file in accordance with the terms of version
5*2ea71e24SRobert Mustacchi * 1.0 of the CDDL.
6*2ea71e24SRobert Mustacchi *
7*2ea71e24SRobert Mustacchi * A full copy of the text of the CDDL should have accompanied this
8*2ea71e24SRobert Mustacchi * source. A copy of the CDDL is also available via the Internet at
9*2ea71e24SRobert Mustacchi * http://www.illumos.org/license/CDDL.
10*2ea71e24SRobert Mustacchi */
11*2ea71e24SRobert Mustacchi
12*2ea71e24SRobert Mustacchi /*
13*2ea71e24SRobert Mustacchi * Copyright 2026 Oxide Computer Company
14*2ea71e24SRobert Mustacchi */
15*2ea71e24SRobert Mustacchi
16*2ea71e24SRobert Mustacchi /*
17*2ea71e24SRobert Mustacchi * Verify that a few sources all agree on socket index information:
18*2ea71e24SRobert Mustacchi * getifaddrs(3SOCKET), if_nametoindex(3SOCKET), and manually walking
19*2ea71e24SRobert Mustacchi * interfaces. if_nametoindex() doesn't always handle the fact that we can have
20*2ea71e24SRobert Mustacchi * two interfaces (v4 and v6) with the same name. if_nametoindex() prefers v4
21*2ea71e24SRobert Mustacchi * addresses over v6. So when both are up, we'll need to be careful.
22*2ea71e24SRobert Mustacchi *
23*2ea71e24SRobert Mustacchi * This test assumes the set of interfaces isn't changing durings its lifetime.
24*2ea71e24SRobert Mustacchi */
25*2ea71e24SRobert Mustacchi
26*2ea71e24SRobert Mustacchi #include <stdlib.h>
27*2ea71e24SRobert Mustacchi #include <err.h>
28*2ea71e24SRobert Mustacchi #include <sys/types.h>
29*2ea71e24SRobert Mustacchi #include <sys/socket.h>
30*2ea71e24SRobert Mustacchi #include <ifaddrs.h>
31*2ea71e24SRobert Mustacchi #include <net/if.h>
32*2ea71e24SRobert Mustacchi #include <sys/sockio.h>
33*2ea71e24SRobert Mustacchi #include <string.h>
34*2ea71e24SRobert Mustacchi #include <unistd.h>
35*2ea71e24SRobert Mustacchi #include <errno.h>
36*2ea71e24SRobert Mustacchi #include <sys/sysmacros.h>
37*2ea71e24SRobert Mustacchi
38*2ea71e24SRobert Mustacchi int
main(void)39*2ea71e24SRobert Mustacchi main(void)
40*2ea71e24SRobert Mustacchi {
41*2ea71e24SRobert Mustacchi int ret = EXIT_SUCCESS;
42*2ea71e24SRobert Mustacchi int s4, s6;
43*2ea71e24SRobert Mustacchi struct ifaddrs *ifa;
44*2ea71e24SRobert Mustacchi
45*2ea71e24SRobert Mustacchi if ((s4 = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
46*2ea71e24SRobert Mustacchi err(EXIT_FAILURE, "failed to get IPv4 socket");
47*2ea71e24SRobert Mustacchi }
48*2ea71e24SRobert Mustacchi
49*2ea71e24SRobert Mustacchi if ((s6 = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
50*2ea71e24SRobert Mustacchi err(EXIT_FAILURE, "failed to get IPv6 socket");
51*2ea71e24SRobert Mustacchi }
52*2ea71e24SRobert Mustacchi
53*2ea71e24SRobert Mustacchi if (getifaddrs(&ifa) != 0) {
54*2ea71e24SRobert Mustacchi err(EXIT_FAILURE, "failed to get address information");
55*2ea71e24SRobert Mustacchi }
56*2ea71e24SRobert Mustacchi
57*2ea71e24SRobert Mustacchi for (struct ifaddrs *i = ifa; i != NULL; i = i->ifa_next) {
58*2ea71e24SRobert Mustacchi struct lifreq lif;
59*2ea71e24SRobert Mustacchi char name[IF_NAMESIZE];
60*2ea71e24SRobert Mustacchi sa_family_t fam = i->ifa_addr->sa_family;
61*2ea71e24SRobert Mustacchi
62*2ea71e24SRobert Mustacchi if (fam != AF_INET && fam != AF_INET6) {
63*2ea71e24SRobert Mustacchi continue;
64*2ea71e24SRobert Mustacchi }
65*2ea71e24SRobert Mustacchi
66*2ea71e24SRobert Mustacchi /*
67*2ea71e24SRobert Mustacchi * First ask the kernel our index.
68*2ea71e24SRobert Mustacchi */
69*2ea71e24SRobert Mustacchi (void) memset(&lif, 0, sizeof (lif));
70*2ea71e24SRobert Mustacchi if (strlcpy(lif.lifr_name, i->ifa_name,
71*2ea71e24SRobert Mustacchi sizeof (lif.lifr_name)) >= sizeof (lif.lifr_name)) {
72*2ea71e24SRobert Mustacchi errx(EXIT_FAILURE, "INTERNAL TEST FAILURE: encountered "
73*2ea71e24SRobert Mustacchi "interface name %s that would overflow struct "
74*2ea71e24SRobert Mustacchi "lifreq length!", i->ifa_name);
75*2ea71e24SRobert Mustacchi }
76*2ea71e24SRobert Mustacchi
77*2ea71e24SRobert Mustacchi if (ioctl(fam == AF_INET ? s4 : s6, SIOCGLIFINDEX, &lif) != 0) {
78*2ea71e24SRobert Mustacchi warn("TEST FAILED: failed to ask kernel for index of "
79*2ea71e24SRobert Mustacchi "interface %s (family 0x%x", i->ifa_name, fam);
80*2ea71e24SRobert Mustacchi ret = EXIT_FAILURE;
81*2ea71e24SRobert Mustacchi continue;
82*2ea71e24SRobert Mustacchi }
83*2ea71e24SRobert Mustacchi
84*2ea71e24SRobert Mustacchi if (if_indextoname(lif.lifr_index, name) == NULL) {
85*2ea71e24SRobert Mustacchi warn("TEST FAILED: if_indextoname() failed to convert "
86*2ea71e24SRobert Mustacchi "index 0x%x back to %s", lif.lifr_index,
87*2ea71e24SRobert Mustacchi i->ifa_name);
88*2ea71e24SRobert Mustacchi ret = EXIT_FAILURE;
89*2ea71e24SRobert Mustacchi continue;
90*2ea71e24SRobert Mustacchi }
91*2ea71e24SRobert Mustacchi
92*2ea71e24SRobert Mustacchi if (strcmp(name, i->ifa_name) != 0) {
93*2ea71e24SRobert Mustacchi warn("TEST FAILED: if_indextoname() returned name %s "
94*2ea71e24SRobert Mustacchi "for index 0x%x, but expected %s", name,
95*2ea71e24SRobert Mustacchi lif.lifr_index, i->ifa_name);
96*2ea71e24SRobert Mustacchi ret = EXIT_FAILURE;
97*2ea71e24SRobert Mustacchi } else {
98*2ea71e24SRobert Mustacchi (void) printf("TEST PASSED: Mapped %s (fam 0x%x) index "
99*2ea71e24SRobert Mustacchi "back to expected name\n", i->ifa_name, fam);
100*2ea71e24SRobert Mustacchi }
101*2ea71e24SRobert Mustacchi
102*2ea71e24SRobert Mustacchi /*
103*2ea71e24SRobert Mustacchi * Now go from the name to the index. In general we expect the
104*2ea71e24SRobert Mustacchi * IPv4 and IPv6 interfaces with the same name to share the same
105*2ea71e24SRobert Mustacchi * index.
106*2ea71e24SRobert Mustacchi */
107*2ea71e24SRobert Mustacchi uint_t idx = if_nametoindex(i->ifa_name);
108*2ea71e24SRobert Mustacchi if (idx == 0) {
109*2ea71e24SRobert Mustacchi warn("TEST FAILED: if_nametoindex() unexpected failed "
110*2ea71e24SRobert Mustacchi "on %s (fam 0x%x)", i->ifa_name, fam);
111*2ea71e24SRobert Mustacchi ret = EXIT_FAILURE;
112*2ea71e24SRobert Mustacchi } else if (idx != lif.lifr_index) {
113*2ea71e24SRobert Mustacchi warnx("TEST FAILED: if_nametoindex() on %s (fam 0x%x) "
114*2ea71e24SRobert Mustacchi "returned index 0x%x, but expected 0x%x from "
115*2ea71e24SRobert Mustacchi "SIOCGLIFINDEX\n", i->ifa_name, fam, idx,
116*2ea71e24SRobert Mustacchi lif.lifr_index);
117*2ea71e24SRobert Mustacchi ret = EXIT_FAILURE;
118*2ea71e24SRobert Mustacchi } else {
119*2ea71e24SRobert Mustacchi (void) printf("TEST PASSED: %s (fam 0x%x) name to "
120*2ea71e24SRobert Mustacchi "index round tripped successfully\n", i->ifa_name,
121*2ea71e24SRobert Mustacchi fam);
122*2ea71e24SRobert Mustacchi }
123*2ea71e24SRobert Mustacchi }
124*2ea71e24SRobert Mustacchi
125*2ea71e24SRobert Mustacchi const char *bad_names[] = { "", "nonumber", "thisiswaytoolongforanif0",
126*2ea71e24SRobert Mustacchi "bad/char23", "if1234567890123456" };
127*2ea71e24SRobert Mustacchi for (size_t i = 0; i < ARRAY_SIZE(bad_names); i++) {
128*2ea71e24SRobert Mustacchi uint_t idx = if_nametoindex(bad_names[i]);
129*2ea71e24SRobert Mustacchi if (idx != 0) {
130*2ea71e24SRobert Mustacchi warnx("TEST FAILED: if_nametoindex() returned idx 0x%x "
131*2ea71e24SRobert Mustacchi "on invalid name '%s': expected failure", idx,
132*2ea71e24SRobert Mustacchi bad_names[i]);
133*2ea71e24SRobert Mustacchi ret = EXIT_FAILURE;
134*2ea71e24SRobert Mustacchi } else if (errno != ENXIO) {
135*2ea71e24SRobert Mustacchi warnx("TEST FAILED: if_nametoindex() returned %s on "
136*2ea71e24SRobert Mustacchi "invalid name '%s': expected ENXIO",
137*2ea71e24SRobert Mustacchi strerrorname_np(errno), bad_names[i]);
138*2ea71e24SRobert Mustacchi ret = EXIT_FAILURE;
139*2ea71e24SRobert Mustacchi } else {
140*2ea71e24SRobert Mustacchi (void) printf("TEST PASSED: if_nametoindex() failed "
141*2ea71e24SRobert Mustacchi "bad name '%s' with ENXIO\n", bad_names[i]);
142*2ea71e24SRobert Mustacchi }
143*2ea71e24SRobert Mustacchi }
144*2ea71e24SRobert Mustacchi
145*2ea71e24SRobert Mustacchi freeifaddrs(ifa);
146*2ea71e24SRobert Mustacchi (void) close(s6);
147*2ea71e24SRobert Mustacchi (void) close(s4);
148*2ea71e24SRobert Mustacchi if (ret == EXIT_SUCCESS) {
149*2ea71e24SRobert Mustacchi (void) printf("All tests passed successfully\n");
150*2ea71e24SRobert Mustacchi }
151*2ea71e24SRobert Mustacchi return (ret);
152*2ea71e24SRobert Mustacchi }
153