Add ifconfig(8).

Co-authored-by: Juhani Krekelä <juhani@krekelä.fi>
This commit is contained in:
Jonas 'Sortie' Termansen 2017-06-30 23:11:16 +02:00
parent 3da0728fd5
commit 2edaf130fa
8 changed files with 848 additions and 3 deletions

View File

@ -17,6 +17,7 @@ editor \
ext \
games \
hostname \
ifconfig \
init \
kblayout \
kblayout-compiler \

View File

@ -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.

1
ifconfig/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
ifconfig

29
ifconfig/Makefile Normal file
View File

@ -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)

218
ifconfig/ifconfig.8 Normal file
View File

@ -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

593
ifconfig/ifconfig.c Normal file
View File

@ -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 <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <arpa/inet.h>
#include <dirent.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <net/if.h>
#include <netinet/if_ether.h>
#include <netinet/in.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
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;
}

View File

@ -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

View File

@ -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