diff --git a/libc/Makefile b/libc/Makefile index 427b372e..3af132d4 100644 --- a/libc/Makefile +++ b/libc/Makefile @@ -412,6 +412,10 @@ netdb/getservent.o \ netdb/setnetent.o \ netdb/setprotoent.o \ netdb/setservent.o \ +net/if/if_freenameindex.o \ +net/if/if_indextoname.o \ +net/if/if_nameindex.o \ +net/if/if_nametoindex.o \ poll/poll.o \ poll/ppoll.o \ psctl/psctl.o \ diff --git a/libc/include/net/if.h b/libc/include/net/if.h index c7f102e4..a467e690 100644 --- a/libc/include/net/if.h +++ b/libc/include/net/if.h @@ -74,4 +74,23 @@ struct if_config }; #endif +struct if_nameindex +{ + unsigned int if_index; + char* if_name; +}; + +#ifdef __cplusplus +extern "C" { +#endif + +void if_freenameindex(struct if_nameindex*); +char* if_indextoname(unsigned int, char*); +struct if_nameindex* if_nameindex(void); +unsigned int if_nametoindex(const char*); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + #endif diff --git a/libc/net/if/if_freenameindex.c b/libc/net/if/if_freenameindex.c new file mode 100644 index 00000000..fa81b680 --- /dev/null +++ b/libc/net/if/if_freenameindex.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2016 Jonas 'Sortie' Termansen. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * net/if/if_freenameindex.c + * Free list of network interfaces. + */ + +#include +#include + +void if_freenameindex(struct if_nameindex* ifs) +{ + for ( size_t i = 0; ifs[i].if_name; i++ ) + free(ifs[i].if_name); + free(ifs); +} diff --git a/libc/net/if/if_indextoname.c b/libc/net/if/if_indextoname.c new file mode 100644 index 00000000..24ebe5b8 --- /dev/null +++ b/libc/net/if/if_indextoname.c @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2016 Jonas 'Sortie' Termansen. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * net/if/if_indextoname.c + * Get name of network interface by index. + */ + +#include +#include +#include +#include + +// TODO: This is better done with a dedicated system call. +char* if_indextoname(unsigned int ifindex, char* str) +{ + struct if_nameindex* ifs = if_nameindex(); + if ( !ifs ) + return NULL; + for ( size_t i = 0; ifs[i].if_index; i++ ) + { + if ( ifs[i].if_index == ifindex ) + { + strlcpy(str, ifs[i].if_name, IF_NAMESIZE); + if_freenameindex(ifs); + return str; + } + } + if_freenameindex(ifs); + return errno = ENXIO, (char*) NULL; +} diff --git a/libc/net/if/if_nameindex.c b/libc/net/if/if_nameindex.c new file mode 100644 index 00000000..c329a4ac --- /dev/null +++ b/libc/net/if/if_nameindex.c @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2016, 2017 Jonas 'Sortie' Termansen. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * net/if/if_nameindex.c + * Build list of network interfaces. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +// TODO: Consider turning this into a system call to avoid a number of error +// cases and work properly in chroots. +struct if_nameindex* if_nameindex(void) +{ + DIR* dir = opendir("/dev"); + if ( !dir ) + return NULL; + struct if_nameindex* ifs = (struct if_nameindex*) + reallocarray(NULL, sizeof(struct if_nameindex), 2); + if ( !ifs ) + return closedir(dir), (struct if_nameindex*) NULL; + size_t ifs_count = 0; + size_t ifs_allocated = 2; + ifs[ifs_count].if_index = 0; + ifs[ifs_count].if_name = NULL; + struct dirent* entry; + while ( (errno = 0, entry = readdir(dir)) ) + { + if ( entry->d_type != DT_UNKNOWN && entry->d_type != DT_CHR ) + continue; + int fd = openat(dirfd(dir), entry->d_name, O_RDWR | O_NOFOLLOW); + if ( fd < 0 ) + { + int errnum = errno; + if ( entry->d_type == DT_UNKNOWN ) + { + struct stat st; + if ( fstatat(dirfd(dir), entry->d_name, &st, + AT_SYMLINK_NOFOLLOW) < 0 ) + { + closedir(dir); + if_freenameindex(ifs); + return NULL; + } + if ( !S_ISCHR(st.st_mode) ) + continue; + } + if ( errnum == ELOOP || errnum == EACCES || errnum == EPERM ) + continue; + closedir(dir); + if_freenameindex(ifs); + return NULL; + } + int type; + struct if_info info; + if ( (type = ioctl(fd, IOCGETTYPE)) < 0 || + IOC_TYPE(type) != IOC_TYPE_NETWORK_INTERFACE || + ioctl(fd, NIOC_GETINFO, &info) < 0 ) + { + close(fd); + continue; + } + close(fd); + if ( ifs_count + 1 == ifs_allocated ) + { + size_t new_allocated = 2 * ifs_allocated; + struct if_nameindex* new_ifs = (struct if_nameindex*) + reallocarray(ifs, sizeof(struct if_nameindex), new_allocated); + if ( !new_ifs ) + { + closedir(dir); + if_freenameindex(ifs); + return NULL; + } + ifs = new_ifs; + ifs_allocated = new_allocated; + } + char* name = strdup(entry->d_name); + if ( !name ) + { + closedir(dir); + if_freenameindex(ifs); + return NULL; + } + ifs[ifs_count].if_index = info.linkid; + ifs[ifs_count].if_name = name; + ifs_count++; + ifs[ifs_count].if_index = 0; + ifs[ifs_count].if_name = NULL; + } + if ( errno ) + { + closedir(dir); + if_freenameindex(ifs); + return NULL; + } + closedir(dir); + return ifs; +} diff --git a/libc/net/if/if_nametoindex.c b/libc/net/if/if_nametoindex.c new file mode 100644 index 00000000..2aa45c15 --- /dev/null +++ b/libc/net/if/if_nametoindex.c @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2016 Jonas 'Sortie' Termansen. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * net/if/if_nametoindex.c + * Get index of network interface by name. + */ + +#include +#include +#include +#include + +// TODO: This is better done with a dedicated system call. +// TODO: Or at least the name can be looked up directly in /dev. +unsigned int if_nametoindex(const char* name) +{ + struct if_nameindex* ifs = if_nameindex(); + if ( !ifs ) + return NULL; + for ( size_t i = 0; ifs[i].if_index; i++ ) + { + if ( !strcmp(ifs[i].if_name, name) ) + { + unsigned int result = ifs[i].if_index; + if_freenameindex(ifs); + return result; + } + } + if_freenameindex(ifs); + return 0; +} diff --git a/share/man/man4/if.4 b/share/man/man4/if.4 index a99f4c3a..9ccc1d6f 100644 --- a/share/man/man4/if.4 +++ b/share/man/man4/if.4 @@ -330,6 +330,7 @@ temporarily fail with .Xr getsockopt 2 , .Xr ioctl 2 , .Xr setsockopt 2 , +.Xr if_nameindex 3 , .Xr arp 4 , .Xr ether 4 , .Xr inet 4 ,