1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. 24 * Copyright 2026 Oxide Computer Company 25 */ 26 27 #include <stdio.h> 28 #include <ctype.h> 29 #include <string.h> 30 #include <strings.h> 31 #include <stdlib.h> 32 #include <sys/types.h> 33 #include <sys/socket.h> 34 #include <inet/common.h> 35 #include <net/if.h> 36 #include <netinet/in.h> 37 #include <sys/sockio.h> 38 #include <sys/ioctl.h> 39 #include <unistd.h> 40 #include <errno.h> 41 42 #define IPIF_SEPARATOR_CHAR ":" 43 44 /* 45 * Given an interface name, this function retrives the associated 46 * index value. Returns index value if successful, zero otherwise. 47 * The length of the supplied interface name must be at most 48 * IF_NAMESIZE-1 bytes 49 */ 50 uint32_t 51 if_nametoindex(const char *ifname) 52 { 53 int s; 54 struct lifreq lifr; 55 int save_err; 56 size_t size; 57 58 59 /* Make sure the given name is not NULL */ 60 if (ifname == NULL || *ifname == '\0') { 61 errno = ENXIO; 62 return (0); 63 } 64 65 /* 66 * Fill up the interface name in the ioctl 67 * request message. Make sure that the length of 68 * the given interface name <= (IF_NAMESIZE-1) 69 */ 70 size = strlen(ifname); 71 if (size > (IF_NAMESIZE - 1)) { 72 errno = EINVAL; 73 return (0); 74 } 75 76 (void) memcpy(lifr.lifr_name, ifname, size + 1); 77 78 /* Check the v4 interfaces first */ 79 s = socket(AF_INET, SOCK_DGRAM, 0); 80 if (s >= 0) { 81 if (ioctl(s, SIOCGLIFINDEX, (caddr_t)&lifr) >= 0) { 82 (void) close(s); 83 return (lifr.lifr_index); 84 } 85 (void) close(s); 86 } 87 88 /* Check the v6 interface list */ 89 s = socket(AF_INET6, SOCK_DGRAM, 0); 90 if (s < 0) 91 return (0); 92 93 if (ioctl(s, SIOCGLIFINDEX, (caddr_t)&lifr) < 0) 94 lifr.lifr_index = 0; 95 96 save_err = errno; 97 (void) close(s); 98 errno = save_err; 99 return (lifr.lifr_index); 100 } 101 102 /* 103 * Given an index, this function returns the associated interface 104 * name in the supplied buffer ifname. 105 * Returns physical interface name if successful, NULL otherwise. 106 * The interface name returned will be at most IF_NAMESIZE-1 bytes. 107 */ 108 char * 109 if_indextoname(uint32_t ifindex, char *ifname) 110 { 111 int n; 112 int s; 113 char *buf; 114 uint32_t index; 115 struct lifnum lifn; 116 struct lifconf lifc; 117 struct lifreq *lifrp; 118 int numifs; 119 size_t bufsize; 120 boolean_t found; 121 uint_t flags; 122 123 flags = LIFC_NOXMIT | LIFC_TEMPORARY | LIFC_ALLZONES | LIFC_UNDER_IPMP; 124 125 /* A interface index of 0 is invalid */ 126 if (ifindex == 0) { 127 errno = ENXIO; 128 return (NULL); 129 } 130 131 s = socket(AF_INET6, SOCK_DGRAM, 0); 132 if (s < 0) { 133 s = socket(AF_INET, SOCK_DGRAM, 0); 134 if (s < 0) { 135 return (NULL); 136 } 137 } 138 139 /* Prepare to send a SIOCGLIFNUM request message */ 140 lifn.lifn_family = AF_UNSPEC; 141 lifn.lifn_flags = flags; 142 if (ioctl(s, SIOCGLIFNUM, (char *)&lifn) < 0) { 143 int save_err = errno; 144 (void) close(s); 145 errno = save_err; 146 return (NULL); 147 } 148 149 /* 150 * NOTE: "+ 10" sleaze mitigates new IP interfaces showing up between 151 * the SIOCGLIFNUM and the SIOCGLIFCONF. 152 */ 153 numifs = lifn.lifn_count + 10; 154 155 /* 156 * Provide enough buffer to obtain the interface 157 * list from the kernel as response to a SIOCGLIFCONF 158 * request 159 */ 160 161 bufsize = numifs * sizeof (struct lifreq); 162 buf = malloc(bufsize); 163 if (buf == NULL) { 164 int save_err = errno; 165 (void) close(s); 166 errno = save_err; 167 return (NULL); 168 } 169 lifc.lifc_family = AF_UNSPEC; 170 lifc.lifc_flags = flags; 171 lifc.lifc_len = bufsize; 172 lifc.lifc_buf = buf; 173 if (ioctl(s, SIOCGLIFCONF, (char *)&lifc) < 0) { 174 int save_err = errno; 175 (void) close(s); 176 errno = save_err; 177 free(buf); 178 return (NULL); 179 } 180 181 lifrp = lifc.lifc_req; 182 found = B_FALSE; 183 for (n = lifc.lifc_len / sizeof (struct lifreq); n > 0; n--, lifrp++) { 184 /* 185 * Obtain the index value of each interface, and 186 * match to see if the retrived index value matches 187 * the given one. If so we return the corresponding 188 * device name of that interface. 189 */ 190 size_t size; 191 192 index = if_nametoindex(lifrp->lifr_name); 193 if (index == 0) 194 /* Oops the interface just disappeared */ 195 continue; 196 if (index == ifindex) { 197 size = strcspn(lifrp->lifr_name, 198 (char *)IPIF_SEPARATOR_CHAR); 199 lifrp->lifr_name[size] = '\0'; 200 found = B_TRUE; 201 (void) strncpy(ifname, lifrp->lifr_name, size + 1); 202 break; 203 } 204 } 205 (void) close(s); 206 free(buf); 207 if (!found) { 208 errno = ENXIO; 209 return (NULL); 210 } 211 return (ifname); 212 } 213 214 /* 215 * This function returns all the interface names and indexes 216 */ 217 struct if_nameindex * 218 if_nameindex(void) 219 { 220 int n; 221 int s; 222 boolean_t found; 223 char *buf; 224 struct lifnum lifn; 225 struct lifconf lifc; 226 struct lifreq *lifrp; 227 int numifs; 228 int i; 229 int physinterf_num; 230 size_t bufsize; 231 struct if_nameindex *interface_list; 232 struct if_nameindex *interface_entry; 233 234 s = socket(AF_INET6, SOCK_DGRAM, 0); 235 if (s < 0) { 236 s = socket(AF_INET, SOCK_DGRAM, 0); 237 if (s < 0) 238 return (NULL); 239 } 240 241 lifn.lifn_family = AF_UNSPEC; 242 lifn.lifn_flags = LIFC_NOXMIT | LIFC_TEMPORARY | LIFC_ALLZONES; 243 if (ioctl(s, SIOCGLIFNUM, (char *)&lifn) < 0) 244 return (NULL); 245 numifs = lifn.lifn_count; 246 247 bufsize = numifs * sizeof (struct lifreq); 248 buf = malloc(bufsize); 249 if (buf == NULL) { 250 int save_err = errno; 251 (void) close(s); 252 errno = save_err; 253 return (NULL); 254 } 255 lifc.lifc_family = AF_UNSPEC; 256 lifc.lifc_flags = LIFC_NOXMIT | LIFC_TEMPORARY | LIFC_ALLZONES; 257 lifc.lifc_len = bufsize; 258 lifc.lifc_buf = buf; 259 if (ioctl(s, SIOCGLIFCONF, (char *)&lifc) < 0) { 260 int save_err = errno; 261 (void) close(s); 262 errno = save_err; 263 free(buf); 264 return (NULL); 265 } 266 267 lifrp = lifc.lifc_req; 268 (void) close(s); 269 270 /* Allocate the array of if_nameindex structure */ 271 interface_list = malloc((numifs + 1) * sizeof (struct if_nameindex)); 272 if (!interface_list) { 273 int save_err = errno; 274 free(buf); 275 errno = save_err; 276 return (NULL); 277 } 278 /* 279 * Make sure that terminator structure automatically 280 * happens to be all zeroes. 281 */ 282 bzero(interface_list, ((numifs + 1) * sizeof (struct if_nameindex))); 283 interface_entry = interface_list; 284 physinterf_num = 0; 285 for (n = numifs; n > 0; n--, lifrp++) { 286 size_t size; 287 288 size = strcspn(lifrp->lifr_name, (char *)IPIF_SEPARATOR_CHAR); 289 found = B_FALSE; 290 /* 291 * Search the current array to see if this interface 292 * already exists. Only compare the physical name. 293 */ 294 for (i = 0; i < physinterf_num; i++) { 295 if (strncmp(interface_entry[i].if_name, 296 lifrp->lifr_name, size) == 0) { 297 found = B_TRUE; 298 break; 299 } 300 } 301 302 /* New one. Allocate an array element and fill it */ 303 if (!found) { 304 /* 305 * Obtain the index value for the interface 306 */ 307 interface_entry[physinterf_num].if_index = 308 if_nametoindex(lifrp->lifr_name); 309 310 if (interface_entry[physinterf_num].if_index == 0) { 311 /* The interface went away. Skip this entry. */ 312 continue; 313 } 314 315 /* 316 * Truncate the name to ensure that it represents 317 * a physical interface. 318 */ 319 lifrp->lifr_name[size] = '\0'; 320 if ((interface_entry[physinterf_num].if_name = 321 strdup(lifrp->lifr_name)) == NULL) { 322 int save_err; 323 324 if_freenameindex(interface_list); 325 save_err = errno; 326 free(buf); 327 errno = save_err; 328 return (NULL); 329 } 330 331 physinterf_num++; 332 } 333 } 334 335 /* Create the last one of the array */ 336 interface_entry[physinterf_num].if_name = NULL; 337 interface_entry[physinterf_num].if_index = 0; 338 339 /* Free up the excess array space */ 340 free(buf); 341 interface_list = realloc(interface_list, ((physinterf_num + 1) * 342 sizeof (struct if_nameindex))); 343 344 return (interface_list); 345 } 346 347 /* 348 * This function frees the the array that is created while 349 * the if_nameindex function. 350 */ 351 void 352 if_freenameindex(struct if_nameindex *ptr) 353 { 354 struct if_nameindex *p; 355 356 if (ptr == NULL) 357 return; 358 359 /* First free the if_name member in each array element */ 360 for (p = ptr; p->if_name != NULL; p++) 361 free(p->if_name); 362 363 /* Now free up the array space */ 364 free(ptr); 365 } 366