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