mirror of
https://gitlab.com/sortix/sortix.git
synced 2023-02-13 20:55:38 -05:00
Add ifconfig(8).
Co-authored-by: Juhani Krekelä <juhani@krekelä.fi>
This commit is contained in:
parent
3da0728fd5
commit
2edaf130fa
8 changed files with 848 additions and 3 deletions
1
Makefile
1
Makefile
|
@ -17,6 +17,7 @@ editor \
|
||||||
ext \
|
ext \
|
||||||
games \
|
games \
|
||||||
hostname \
|
hostname \
|
||||||
|
ifconfig \
|
||||||
init \
|
init \
|
||||||
kblayout \
|
kblayout \
|
||||||
kblayout-compiler \
|
kblayout-compiler \
|
||||||
|
|
|
@ -76,7 +76,8 @@ Delete a resolver:
|
||||||
.Sh SEE ALSO
|
.Sh SEE ALSO
|
||||||
.Xr getdnsconfig 2 ,
|
.Xr getdnsconfig 2 ,
|
||||||
.Xr setdnsconfig 2 ,
|
.Xr setdnsconfig 2 ,
|
||||||
.Xr inet 4
|
.Xr inet 4 ,
|
||||||
|
.Xr ifconfig 8
|
||||||
.Sh HISTORY
|
.Sh HISTORY
|
||||||
.Nm
|
.Nm
|
||||||
originally appeared in Sortix 1.1.
|
originally appeared in Sortix 1.1.
|
||||||
|
|
1
ifconfig/.gitignore
vendored
Normal file
1
ifconfig/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
ifconfig
|
29
ifconfig/Makefile
Normal file
29
ifconfig/Makefile
Normal 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
218
ifconfig/ifconfig.8
Normal 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
593
ifconfig/ifconfig.c
Normal 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;
|
||||||
|
}
|
|
@ -337,7 +337,8 @@ temporarily fail with
|
||||||
.Xr inet 4 ,
|
.Xr inet 4 ,
|
||||||
.Xr ip 4 ,
|
.Xr ip 4 ,
|
||||||
.Xr lo 4 ,
|
.Xr lo 4 ,
|
||||||
.Xr kernel 7
|
.Xr kernel 7 ,
|
||||||
|
.Xr ifconfig 8
|
||||||
.Sh STANDARDS
|
.Sh STANDARDS
|
||||||
.St -p1003.1-2008
|
.St -p1003.1-2008
|
||||||
only specifies a minimal
|
only specifies a minimal
|
||||||
|
|
|
@ -26,7 +26,8 @@ in the subnet
|
||||||
.Dv 127.0.0.0/8 .
|
.Dv 127.0.0.0/8 .
|
||||||
Packets with source or destination outside this subnet are dropped.
|
Packets with source or destination outside this subnet are dropped.
|
||||||
.Sh SEE ALSO
|
.Sh SEE ALSO
|
||||||
.Xr kernel 7
|
.Xr kernel 7 ,
|
||||||
|
.Xr ifconfig 8
|
||||||
.Sh CAVEATS
|
.Sh CAVEATS
|
||||||
The default
|
The default
|
||||||
.Xr inet 4
|
.Xr inet 4
|
||||||
|
|
Loading…
Reference in a new issue