From 2edaf130fab557d63cb8b2f8d22360afe8afe93d Mon Sep 17 00:00:00 2001 From: Jonas 'Sortie' Termansen Date: Fri, 30 Jun 2017 23:11:16 +0200 Subject: [PATCH] Add ifconfig(8). MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Juhani Krekelä --- Makefile | 1 + dnsconfig/dnsconfig.8 | 3 +- ifconfig/.gitignore | 1 + ifconfig/Makefile | 29 +++ ifconfig/ifconfig.8 | 218 ++++++++++++++++ ifconfig/ifconfig.c | 593 ++++++++++++++++++++++++++++++++++++++++++ share/man/man4/if.4 | 3 +- share/man/man4/lo.4 | 3 +- 8 files changed, 848 insertions(+), 3 deletions(-) create mode 100644 ifconfig/.gitignore create mode 100644 ifconfig/Makefile create mode 100644 ifconfig/ifconfig.8 create mode 100644 ifconfig/ifconfig.c diff --git a/Makefile b/Makefile index 2ce51208..9056f0db 100644 --- a/Makefile +++ b/Makefile @@ -17,6 +17,7 @@ editor \ ext \ games \ hostname \ +ifconfig \ init \ kblayout \ kblayout-compiler \ diff --git a/dnsconfig/dnsconfig.8 b/dnsconfig/dnsconfig.8 index 935f6219..f6557dd4 100644 --- a/dnsconfig/dnsconfig.8 +++ b/dnsconfig/dnsconfig.8 @@ -76,7 +76,8 @@ Delete a resolver: .Sh SEE ALSO .Xr getdnsconfig 2 , .Xr setdnsconfig 2 , -.Xr inet 4 +.Xr inet 4 , +.Xr ifconfig 8 .Sh HISTORY .Nm originally appeared in Sortix 1.1. diff --git a/ifconfig/.gitignore b/ifconfig/.gitignore new file mode 100644 index 00000000..f590c4c4 --- /dev/null +++ b/ifconfig/.gitignore @@ -0,0 +1 @@ +ifconfig diff --git a/ifconfig/Makefile b/ifconfig/Makefile new file mode 100644 index 00000000..13f8aa69 --- /dev/null +++ b/ifconfig/Makefile @@ -0,0 +1,29 @@ +SOFTWARE_MEANT_FOR_SORTIX=1 +include ../build-aux/platform.mak +include ../build-aux/compiler.mak +include ../build-aux/version.mak +include ../build-aux/dirs.mak + +OPTLEVEL?=$(DEFAULT_OPTLEVEL) +CFLAGS?=$(OPTLEVEL) + +CFLAGS += -Wall -Wextra + +BINARIES = ifconfig +MANPAGES8 = ifconfig.8 + +all: $(BINARIES) + +.PHONY: all install clean + +install: all + mkdir -p $(DESTDIR)$(SBINDIR) + install $(BINARIES) $(DESTDIR)$(SBINDIR) + mkdir -p $(DESTDIR)$(MANDIR)/man8 + cp $(MANPAGES8) $(DESTDIR)$(MANDIR)/man8 + +%: %.c + $(CC) -std=gnu11 $(CFLAGS) $(CPPFLAGS) $< -o $@ + +clean: + rm -f $(BINARIES) diff --git a/ifconfig/ifconfig.8 b/ifconfig/ifconfig.8 new file mode 100644 index 00000000..dae58e0e --- /dev/null +++ b/ifconfig/ifconfig.8 @@ -0,0 +1,218 @@ +.Dd July 4, 2021 +.Dt IFCONFIG 8 +.Os +.Sh NAME +.Nm ifconfig +.Nd configure network interface +.Sh SYNOPSIS +.Nm +.Op Ar interface +.Nm +.Ar interface +.Oo +.Ar protocol +.Op Ar configuration Ar new-value ... +.Ar ... +.Oc +.Nm +.Fl l +.Oo Ar interface Oo Ar protocol Oo Ar configuration Oc Oc Oc +.Nm +.Fl l +.Ar interface +.Ar protocol +.Op Ar configuration ... +.Ar ... +.Sh DESCRIPTION +.Nm +can both write the current configuration of +.Xr if 4 +network interface devices as well as update the configuration of a network +interface. +By default the configuration of every network interface is written. +If the +.Ar interface +argument is given, only the configuration of that network interface is +written. +.Pp +If an +.Ar interface +is specified along with further operands, the configuration of the network +interface is updated by iterating over the remaining operands: +Naming a +.Ar protocol +makes it the current protocol, naming a +.Ar configuration +sets it within the current protocol to the subsequent +.Ar new-value +operand. +.Pp +The options are as follows: +.Bl -tag -width "12345678" +.It Fl l +Write the current value of each specified +.Ar configuration +rather than setting a new value. +The +.Ar new-value +argument is no longer passed. +.Pp +If only an +.Ar interface +and a +.Ar protocol +is specified, list the names of each configuration of that protocol on the +network interface. +.Pp +If only an +.Ar interface +is specified, list the names of each protocol on the network interface. +.Pp +If no +.Ar interface +is specified, list the names of each network interface. +.El +.Pp +The +.Ar interface +argument can be the name or the path of an interface, or a specifier uniquely +matching an interface: +.Pp +.Bl -tag -width "12345678901" -compact +.It Sy ether : Ns Ar mac +Local +.Xr ether 4 +address. +.It Sy etherhw : Ns Ar mac +Hardware +.Xr ether 4 +address. +.It Sy inet : Ns Ar ip +Local +.Xr inet 4 +address. +.It Sy id : Ns Ar num +Network interface integer index. +.El +.Pp +The +.Cm link +.Ar protocol +provides information about the network interface. +The following configurations are supported: +.Pp +.Bl -tag -width "12345678901" -compact +.It Cm up +.Sy yes +if the link is up and +.Sy no +otherwise. +(read-only) +.It Cm type +The type of the network interface, either +.Sy ether +or +.Sy loopback . +(read-only) +.It Cm id +The network interface integer index. +(read-only) +.It Cm name +The name of the network interface. +(read-only) +.El +.Pp +The +.Cm loopback +.Ar protocol +.Pq Xr lo 4 +has no configuration. +.Pp +The +.Cm ether +.Ar protocol +.Pq Ethernet , Xr ether 4 +has the following configurations: +.Pp +.Bl -tag -width "12345678901" -compact +.It Cm address +The local address in +.Xr ether 4 +address notation, or +.Sy default +to use the hardware address. +.It Cm hwaddress +The hardware address in +.Xr ether 4 +address notation. +(read-only) +.El +.Pp +The +.Cm inet +.Ar protocol +.Pq Internet Protocol version 4 , Xr inet 4 +has the following configurations: +.Pp +.Bl -tag -width "12345678901" -compact +.It Cm address +The local address in +.Xr inet 4 +address notation. +.It Cm router +The default route in +.Xr inet 4 +address notation. +.It Cm subnet +The subnet mask in +.Xr inet 4 +address notation. +.El +.Sh EXIT STATUS +.Nm +will exit 0 on success and non-zero otherwise. +.Sh EXAMPLES +.Bd -literal +$ ifconfig +if0: + link up yes type ether id 2 + ether address 00:00:5e:00:53:ff hwaddress 00:00:5e:00:53:ff + inet address 192.0.2.2 router 192.0.2.1 subnet 255.255.255.0 +lo0: + link up yes type loopback id 1 + loopback + inet address 127.0.0.1 router 0.0.0.0 subnet 255.0.0.0 +$ ifconfig if0 +if0: + link up yes type ether id 2 + ether address 00:00:5e:00:53:ff hwaddress 00:00:5e:00:53:ff + inet address 192.0.2.2 router 192.0.2.1 subnet 255.255.255.0 +$ ifconfig if0 inet address 198.51.100.2 router 198.51.100.1 +$ ifconfig if0 ether address 00:00:5e:00:53:42 inet address 198.51.100.3 +$ ifconfig -l +if0 +lo0 +$ ifconfig -l if0 +link +ether +inet +$ ifconfig -l if0 inet +address +router +subnet +$ ifconfig -l if0 inet address +198.51.100.3 +$ ifconfig -l if0 inet address subnet ether address link id +198.51.100.3 +255.255.255.0 +00:00:5e:00:53:42 +42 +$ ifconfig -l etherhw:00:00:5e:00:53:ff link name +if0 +.Ed +.Sh SEE ALSO +.Xr ether 4 , +.Xr if 4 , +.Xr inet 4 , +.Xr lo 4 , +.Xr dnsconfig 8 diff --git a/ifconfig/ifconfig.c b/ifconfig/ifconfig.c new file mode 100644 index 00000000..59f43c89 --- /dev/null +++ b/ifconfig/ifconfig.c @@ -0,0 +1,593 @@ +/* + * Copyright (c) 2016, 2017, 2018 Jonas 'Sortie' Termansen. + * Copyright (c) 2021 Juhani 'nortti' Krekelä. + * + * 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. + * + * ifconfig.c + * Configure network interface. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct if_all +{ + struct if_info info; + struct if_status status; + struct if_config config; +}; + +static void link_id_print(const struct if_all* all, const void* ptr) +{ + (void) all; + unsigned int id = *(const unsigned int*) ptr; + printf("%u", id); +} + +static void link_type_print(const struct if_all* all, const void* ptr) +{ + (void) all; + int type = *(const int*) ptr; + const char* type_string; + switch ( type ) + { + case IF_TYPE_ETHERNET: type_string = "ether"; break; + case IF_TYPE_LOOPBACK: type_string = "loopback"; break; + default: type_string = "unknown"; break; + } + fputs(type_string, stdout); +} + +static void link_up_print(const struct if_all* all, const void* ptr) +{ + (void) all; + int flags = *(const int*) ptr; + fputs(flags & IF_STATUS_FLAGS_UP ? "yes" : "no", stdout); +} + +static void link_name_print(const struct if_all* all, const void* ptr) +{ + (void) all; + const char* name = (const char*) ptr; + fputs(name, stdout); +} + +static void ether_address_print(const struct if_all* all, const void* ptr) +{ + (void) all; + struct ether_addr* addr = (struct ether_addr*) ptr; + printf("%02x:%02x:%02x:%02x:%02x:%02x", + addr->ether_addr_octet[0], addr->ether_addr_octet[1], + addr->ether_addr_octet[2], addr->ether_addr_octet[3], + addr->ether_addr_octet[4], addr->ether_addr_octet[5]); +} + +static void ether_hwaddress_print(const struct if_all* all, const void* ptr) +{ + struct ether_addr hwaddr; + memcpy(&hwaddr, ptr, sizeof(hwaddr)); + ether_address_print(all, &hwaddr); +} + +static bool mac_parse(struct ether_addr* addr, const char* string) +{ + for ( size_t i = 0; i < 6; i++ ) + { + int upper; + if ( '0' <= string[i*3 + 0] && string[i*3 + 0] <= '9' ) + upper = string[i*3 + 0] - '0'; + else if ( 'a' <= string[i*3 + 0] && string[i*3 + 0] <= 'f' ) + upper = string[i*3 + 0] - 'a' + 10; + else if ( 'A' <= string[i*3 + 0] && string[i*3 + 0] <= 'F' ) + upper = string[i*3 + 0] - 'A' + 10; + else + return false; + int lower; + if ( '0' <= string[i*3 + 1] && string[i*3 + 1] <= '9' ) + lower = string[i*3 + 1] - '0'; + else if ( 'a' <= string[i*3 + 1] && string[i*3 + 1] <= 'f' ) + lower = string[i*3 + 1] - 'a' + 10; + else if ( 'A' <= string[i*3 + 1] && string[i*3 + 1] <= 'F' ) + lower = string[i*3 + 1] - 'A' + 10; + else + return false; + if ( string[i*3 + 2] != (i + 1 != 6 ? ':' : '\0') ) + return false; + addr->ether_addr_octet[i] = upper << 4 | lower; + } + return true; +} + +static bool ether_address_parse(const struct if_all* all, + void* ptr, + const char* string) +{ + struct ether_addr* addr = (struct ether_addr*) ptr; + if ( !strcmp(string, "default") ) + { + memcpy(addr, all->info.addr, sizeof(*addr)); + return true; + } + return mac_parse(addr, string); +} + +static void inet_address_print(const struct if_all* all, const void* ptr) +{ + (void) all; + char addr[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, ptr, addr, sizeof(addr)); + fputs(addr, stdout); +} + +static bool inet_address_parse(const struct if_all* all, + void* ptr, + const char* string) +{ + (void) all; + return inet_pton(AF_INET, string, ptr) == 1; +} + +#define ARRAY_LENGTH(array) (sizeof(array) / sizeof((array)[0])) + +struct configuration +{ + const char* name; + size_t offset; + void (*print)(const struct if_all*, const void*); + bool (*parse)(const struct if_all*, void*, const char*); + bool hidden; +}; + +#define INFOOFFSET(parameter) \ + (offsetof(struct if_all, info) + \ + offsetof(struct if_info, parameter)) + +#define STATUSOFFSET(parameter) \ + (offsetof(struct if_all, status) + \ + offsetof(struct if_status, parameter)) + +#define CONFIGOFFSET(protocol, parameter) \ + (offsetof(struct if_all, config) + \ + offsetof(struct if_config, protocol) + \ + offsetof(struct if_config_##protocol, parameter)) + +struct configuration link_configurations[] = +{ + { "up", STATUSOFFSET(flags), link_up_print, NULL, false }, + { "type", INFOOFFSET(type), link_type_print, NULL, false }, + { "id", INFOOFFSET(linkid), link_id_print, NULL, false }, + { "name", INFOOFFSET(name), link_name_print, NULL, true }, +}; + +struct configuration ether_configurations[] = +{ + { "address", CONFIGOFFSET(ether, address), + ether_address_print, ether_address_parse, false }, + { "hwaddress", INFOOFFSET(addr), ether_hwaddress_print, NULL, false }, +}; + +struct configuration loopback_configurations[] = +{ +}; + +struct configuration inet_configurations[] = +{ + { "address", CONFIGOFFSET(inet, address), + inet_address_print, inet_address_parse, false }, + { "router", CONFIGOFFSET(inet, router), + inet_address_print, inet_address_parse, false }, + { "subnet", CONFIGOFFSET(inet, subnet), + inet_address_print, inet_address_parse, false }, +}; + +struct protocol +{ + const char* name; + int link_type_value; + struct configuration* configurations; + size_t configurations_count; +}; + +struct protocol protocols[] = +{ + { "link", 0, + link_configurations, ARRAY_LENGTH(link_configurations) }, + { "ether", IF_TYPE_ETHERNET, + ether_configurations, ARRAY_LENGTH(ether_configurations) }, + { "loopback", IF_TYPE_LOOPBACK, + loopback_configurations, ARRAY_LENGTH(loopback_configurations) }, + { "inet", 0, + inet_configurations, ARRAY_LENGTH(inet_configurations) }, +}; + +static int filter_dev_netif(const struct dirent* entry) +{ + char* path; + if ( asprintf(&path, "/dev/%s", entry->d_name) < 0 ) + err(1, "malloc"); + // TODO: Open with O_STAT or some future extension that lets us properly + // test whether this is a network interface before complaining we + // couldn't open it. Otherwise it's annoying for non-root users to get + // warnings about non-network-interfaces in /dev they aren't supposed + // to be able to open. + int fd = open(path, O_RDONLY | O_NOFOLLOW); + if ( fd < 0 ) + { + struct stat st; + if ( lstat(path, &st) < 0 ) + { + warn("stat: %s", path); + free(path); + return 0; + } + // TODO: Determine whether this is a network interface without having + // access to the device. Otherwise non-root users will be warned + // about non-network interfaces in /dev they're not supposed to be + // able to access. + if ( S_ISCHR(st.st_mode) ) + warn("%s", path); + free(path); + return 0; + } + free(path); + int type = ioctl(fd, IOCGETTYPE); + close(fd); + return IOC_TYPE(type) == IOC_TYPE_NETWORK_INTERFACE; +} + +enum specifier_type { ETHER, ETHERHW, INET, ID }; + +struct if_specifier +{ + enum specifier_type type; + union + { + struct ether_addr mac_addr; + struct in_addr ipv4_addr; + unsigned int id; + }; +}; + +static bool parse_specifier(struct if_specifier* specifier, const char* string) +{ + if ( !strncmp(string, "ether:", strlen("ether:")) ) + { + specifier->type = ETHER; + return mac_parse(&specifier->mac_addr, string + strlen("ether:")); + } + else if ( !strncmp(string, "etherhw:", strlen("etherhw:")) ) + { + specifier->type = ETHERHW; + return mac_parse(&specifier->mac_addr, string + strlen("etherhw:")); + } + else if ( !strncmp(string, "inet:", strlen("inet:")) ) + { + specifier->type = INET; + return inet_pton(AF_INET, string + strlen("inet:"), + &specifier->ipv4_addr) == 1; + } + else if ( !strncmp(string, "id:", strlen("id:")) ) + { + specifier->type = ID; + const char* idstr = string + strlen("id:"); + char* end; + errno = 0; + unsigned long ulong = strtoul(idstr, &end, 10); + if ( errno || !*idstr || *end || ulong > UINT_MAX ) + return false; + specifier->id = ulong; + return true; + } + + return false; +} + +static int find_interface(const char* specifier_string, + char if_name[IF_NAMESIZE]) +{ + struct if_specifier specifier; + if ( !parse_specifier(&specifier, specifier_string) ) + errx(1, "Invalid interface specifier: %s", specifier_string); + + struct if_nameindex* ifs = if_nameindex(); + if ( !ifs ) + err(1, "if_nameindex"); + + int if_fd = -1; + for ( size_t i = 0; ifs[i].if_index || ifs[i].if_name; i++ ) + { + const char* name = ifs[i].if_name; + + char* path; + if ( asprintf(&path, "/dev/%s", name) < 0 ) + err(1, "malloc"); + int fd = open(path, O_RDONLY); + if ( fd < 0 ) + err(1, "%s", path); + free(path); + + struct if_info ifinfo; + struct if_config ifconfig; + if ( ioctl(fd, NIOC_GETINFO, &ifinfo) < 0 ) + err(1, "%s: ioctl: NIOC_GETINFO", name); + if ( ioctl(fd, NIOC_GETCONFIG, &ifconfig) < 0 ) + err(1, "%s: ioctl: NIOC_GETCONFIG", name); + + bool match = false; + switch ( specifier.type ) + { + case ETHER: + match = ifinfo.type == IF_TYPE_ETHERNET && + !memcmp(&specifier.mac_addr, &ifconfig.ether.address, + sizeof(struct ether_addr)); + break; + case ETHERHW: + match = ifinfo.type == IF_TYPE_ETHERNET && + !memcmp(&specifier.mac_addr, &ifinfo.addr, + sizeof(struct ether_addr)); + break; + case INET: + match = !memcmp(&specifier.ipv4_addr, &ifconfig.inet.address, + sizeof(struct in_addr)); + break; + case ID: + match = ifinfo.linkid == specifier.id; + break; + } + + // Ensure the specifier unambiguously matches an interface. + if ( match && if_fd != -1 ) + errx(1, "Ambiguous specifier; matches at least %s and %s: %s", + if_name, name, specifier_string); + + if ( match ) + { + if_fd = fd; + strlcpy(if_name, name, IF_NAMESIZE); + } + else + close(fd); + + } + + if_freenameindex(ifs); + + if ( if_fd == -1 ) + errx(1, "Specifier does not match any interfaces: %s", + specifier_string); + + return if_fd; +} + +static void compact_arguments(int* argc, char*** argv) +{ + for ( int i = 0; i < *argc; i++ ) + { + while ( i < *argc && !(*argv)[i] ) + { + for ( int n = i; n < *argc; n++ ) + (*argv)[n] = (*argv)[n+1]; + (*argc)--; + } + } +} + +int main(int argc, char* argv[]) +{ + bool list = false; + + for ( int i = 1; i < argc; i++ ) + { + const char* arg = argv[i]; + if ( arg[0] != '-' || !arg[1] ) + continue; + argv[i] = NULL; + if ( !strcmp(arg, "--") ) + break; + if ( arg[1] != '-' ) + { + char c; + while ( (c = *++arg) ) switch ( c ) + { + case 'l': list = true; break; + default: + errx(1, "unknown option -- '%c'", c); + } + } + else + errx(1, "unknown option: %s", arg); + } + + compact_arguments(&argc, &argv); + + int devices_count = 1; + struct dirent** devices = NULL; + if ( argc <= 1 ) + { + devices_count = scandir("/dev", &devices, filter_dev_netif, alphasort); + if ( devices_count < 0 ) + err(1, "scandir: /dev"); + } + + int result = 0; + for ( int d = 0; d < devices_count; d++ ) + { + char if_name[IF_NAMESIZE]; + const char* name = devices ? devices[d]->d_name : argv[1]; + int fd; + if ( strchr(name, '/') ) + fd = open(name, O_RDONLY); + else if ( strchr(name, ':') ) + { + fd = find_interface(name, if_name); + name = if_name; + } + else + { + char* path; + if ( asprintf(&path, "/dev/%s", name) < 0 ) + err(1, "malloc"); + fd = open(path, O_RDONLY); + free(path); + } + if ( fd < 0 ) + err(1, "%s", name); + struct if_all all; + if ( ioctl(fd, NIOC_GETINFO, &all.info) < 0 ) + err(1, "%s: ioctl: NIOC_GETINFO", name); + if ( ioctl(fd, NIOC_GETSTATUS, &all.status) < 0 ) + err(1, "%s: ioctl: NIOC_GETSTATUS", name); + if ( ioctl(fd, NIOC_GETCONFIG, &all.config) < 0 ) + err(1, "%s: ioctl: NIOC_GETCONFIG", name); + if ( list && argc == 1 ) + { + puts(name); + continue; + } + else if ( list && (argc == 2 || argc == 3) ) + { + bool found = false; + for ( size_t i = 0; i < ARRAY_LENGTH(protocols); i++ ) + { + struct protocol* protocol = &protocols[i]; + if ( 3 <= argc && strcmp(protocol->name, argv[2]) != 0 ) + continue; + if ( protocol->link_type_value && + all.info.type != protocol->link_type_value ) + { + if ( 3 <= argc ) + errx(1, "%s: %s: Interface does not support protocol", + name, argv[2]); + continue; + } + if ( argc < 3 ) + { + puts(protocol->name); + continue; + } + found = true; + for ( size_t j = 0; j < protocol->configurations_count; j++ ) + puts(protocol->configurations[j].name); + } + if ( 3 <= argc && !found ) + errx(1, "%s: %s: No such protocol", name, argv[2]); + continue; + } + else if ( argc <= 2 ) + { + printf("%s:\n", name); + for ( size_t i = 0; i < ARRAY_LENGTH(protocols); i++ ) + { + struct protocol* protocol = &protocols[i]; + if ( protocol->link_type_value && + all.info.type != protocol->link_type_value ) + continue; + putchar('\t'); + fputs(protocol->name, stdout); + for ( size_t j = 0; j < protocol->configurations_count; j++ ) + { + struct configuration* configuration = + &protocol->configurations[j]; + if ( configuration->hidden ) + continue; + putchar(' '); + fputs(configuration->name, stdout); + putchar(' '); + void* ptr = ((char*) &all + configuration->offset); + configuration->print(&all, ptr); + } + putchar('\n'); + } + continue; + } + struct protocol* protocol = NULL; + for ( int i = 2; i < argc; ) + { + const char* operand = argv[i++]; + bool found = false; + for ( size_t n = 0; + protocol && n < protocol->configurations_count; + n++ ) + { + struct configuration* configuration = + &protocol->configurations[n]; + if ( strcmp(operand, configuration->name) != 0 ) + continue; + found = true; + void* ptr = ((char*) &all + configuration->offset); + if ( list ) + { + configuration->print(&all, ptr); + putchar('\n'); + } + else + { + if ( !configuration->parse ) + errx(1, "%s: %s: %s: Configuration is read-only", + name, protocol->name, operand); + if ( i == argc ) + errx(1, "%s: %s: %s: Expected parameter", + name, protocol->name, operand); + const char* parameter = argv[i++]; + if ( !configuration->parse(&all, ptr, parameter) ) + errx(1, "%s: %s: %s: Invalid value: %s", + name, protocol->name, operand, parameter); + } + } + for ( size_t n = 0; !found && n < ARRAY_LENGTH(protocols); n++ ) + { + struct protocol* new_protocol = &protocols[n]; + if ( strcmp(operand, new_protocol->name) != 0 ) + continue; + if ( new_protocol->link_type_value && + all.info.type != new_protocol->link_type_value ) + errx(1, "%s: %s: Interface does not support protocol", + name, operand); + found = true; + protocol = new_protocol; + } + if ( !found ) + { + if ( !protocol ) + errx(1, "%s: %s: No such protocol", name, operand); + errx(1, "%s: %s: No such protocol or configuration of protocol " + "%s", name, operand, protocol->name); + } + } + if ( !list && ioctl(fd, NIOC_SETCONFIG, &all.config) < 0 ) + err(1, "%s: ioctl: NIOC_SETCONFIG", name); + close(fd); + } + + if ( ferror(stdout) || fflush(stdout) == EOF ) + err(1, "stdout"); + return result; +} diff --git a/share/man/man4/if.4 b/share/man/man4/if.4 index 7af4f862..dc326594 100644 --- a/share/man/man4/if.4 +++ b/share/man/man4/if.4 @@ -337,7 +337,8 @@ temporarily fail with .Xr inet 4 , .Xr ip 4 , .Xr lo 4 , -.Xr kernel 7 +.Xr kernel 7 , +.Xr ifconfig 8 .Sh STANDARDS .St -p1003.1-2008 only specifies a minimal diff --git a/share/man/man4/lo.4 b/share/man/man4/lo.4 index 02f5790a..fa78b6a7 100644 --- a/share/man/man4/lo.4 +++ b/share/man/man4/lo.4 @@ -26,7 +26,8 @@ in the subnet .Dv 127.0.0.0/8 . Packets with source or destination outside this subnet are dropped. .Sh SEE ALSO -.Xr kernel 7 +.Xr kernel 7 , +.Xr ifconfig 8 .Sh CAVEATS The default .Xr inet 4