Detect Wifi and cabled ethernet on linux systems, allow matching by type in config

This commit is contained in:
Jeremy Lakeman 2017-11-07 16:04:57 +10:30
parent 68deae8393
commit ce2c276570
8 changed files with 127 additions and 45 deletions

1
conf.h
View File

@ -682,6 +682,7 @@ int cf_fmt_sid(const char **, const sid_t *sidp);
int cf_opt_rhizome_bk(rhizome_bk_t *bkp, const char *text);
int cf_fmt_rhizome_bk(const char **, const rhizome_bk_t *bkp);
const char * interface_type_name(short type);
int cf_opt_interface_type(short *typep, const char *text);
int cf_fmt_interface_type(const char **, const short *typep);

View File

@ -688,22 +688,36 @@ int cf_opt_interface_type(short *typep, const char *text)
*typep = OVERLAY_INTERFACE_PACKETRADIO;
return CFOK;
}
if (strcasecmp(text, "any") == 0) {
*typep = OVERLAY_INTERFACE_ANY;
return CFOK;
}
if (strcasecmp(text, "other") == 0) {
*typep = OVERLAY_INTERFACE_OTHER;
return CFOK;
}
if (strcasecmp(text, "unknown") == 0) {
*typep = OVERLAY_INTERFACE_UNKNOWN;
return CFOK;
}
return CFINVALID;
}
const char * interface_type_name(short type){
switch (type) {
case OVERLAY_INTERFACE_ETHERNET: return "ethernet";
case OVERLAY_INTERFACE_WIFI: return "wifi";
case OVERLAY_INTERFACE_PACKETRADIO: return "catear";
case OVERLAY_INTERFACE_ANY: return "any";
case OVERLAY_INTERFACE_OTHER: return "other";
case OVERLAY_INTERFACE_UNKNOWN: return "unknown";
}
return NULL;
}
int cf_fmt_interface_type(const char **textp, const short *typep)
{
const char *t = NULL;
switch (*typep) {
case OVERLAY_INTERFACE_ETHERNET: t = "ethernet"; break;
case OVERLAY_INTERFACE_WIFI: t = "wifi"; break;
case OVERLAY_INTERFACE_PACKETRADIO: t = "catear"; break;
case OVERLAY_INTERFACE_UNKNOWN: t = "other"; break;
}
const char *t = interface_type_name(*typep);
if (!t)
return CFINVALID;
*textp = str_edup(t);
@ -1004,6 +1018,19 @@ int cf_cmp_network_interface(const struct config_network_interface *a, const str
int vld_network_interface(const struct cf_om_node *parent, struct config_network_interface *nifp, int result)
{
if (nifp->match_type == OVERLAY_INTERFACE_UNKNOWN){
int nodei_match_type = cf_om_get_child(parent, "match_type", NULL);
assert(nodei_match_type != -1);
cf_warn_node_value(parent->nodv[nodei_match_type], CFINVALID);
return result | CFSUB(CFINVALID);
}
if (nifp->type == OVERLAY_INTERFACE_ANY
|| nifp->type == OVERLAY_INTERFACE_UNKNOWN){
int nodei_type = cf_om_get_child(parent, "type", NULL);
assert(nodei_type != -1);
cf_warn_node_value(parent->nodv[nodei_type], CFINVALID);
return result | CFSUB(CFINVALID);
}
if (nifp->match.patc != 0 && nifp->file[0]) {
int nodei_match = cf_om_get_child(parent, "match", NULL);
int nodei_file = cf_om_get_child(parent, "file", NULL);

View File

@ -461,7 +461,8 @@ ATOM(struct in_addr, dummy_address, hton_in_addr(INADDR_LOOPBACK), in_a
ATOM(struct in_addr, dummy_netmask, hton_in_addr(0xFFFFFF00), in_addr,, "Dummy interface netmask")
ATOM(uint16_t, port, PORT_DNA, uint16_nonzero,, "Port number for network interface")
ATOM(uint16_t, drop_packets, 0, uint16_nonzero,, "Percentage of incoming packets that should be dropped for testing purposes")
ATOM(short, type, OVERLAY_INTERFACE_WIFI, interface_type,, "Type of network interface")
ATOM(short, match_type, OVERLAY_INTERFACE_ANY, interface_type,, "Type of network interface to match against")
ATOM(short, type, OVERLAY_INTERFACE_WIFI, interface_type,, "Type of network interface for default configuration")
ATOM(short, radiotype, RADIO_TYPE_RFD900, radio_type,, "Type of packet radio interface")
SUB_STRUCT(mdp_iftype, broadcast,)
SUB_STRUCT(mdp_iftype, unicast,)

View File

@ -28,10 +28,14 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#define CRYPT_SIGNED 2
#define CRYPT_PUBLIC 4
#define OVERLAY_INTERFACE_UNKNOWN 0
// Interface types for matching or default config
// Note that the numeric value is also used to choose the best link
#define OVERLAY_INTERFACE_ETHERNET 1
#define OVERLAY_INTERFACE_WIFI 2
#define OVERLAY_INTERFACE_PACKETRADIO 3
#define OVERLAY_INTERFACE_OTHER 3
#define OVERLAY_INTERFACE_UNKNOWN 4
#define OVERLAY_INTERFACE_ANY 5
#define OVERLAY_INTERFACE_PACKETRADIO 6
#define RADIO_TYPE_RFD900 0
#define RADIO_TYPE_RFM69 1

View File

@ -334,30 +334,15 @@ overlay_interface * overlay_interface_find_name_type(const char *name, int socke
return NULL;
}
static int interface_type_priority(int type)
{
switch(type){
case OVERLAY_INTERFACE_ETHERNET:
return 1;
case OVERLAY_INTERFACE_WIFI:
return 2;
case OVERLAY_INTERFACE_PACKETRADIO:
return 4;
}
return 3;
}
// Which interface is better for routing packets?
// returns -1 to indicate the first is better, 0 for equal, 1 for the second
int overlay_interface_compare(overlay_interface *one, overlay_interface *two)
{
if (one==two)
return 0;
int p1 = interface_type_priority(one->ifconfig.type);
int p2 = interface_type_priority(two->ifconfig.type);
if (p1<p2)
if (one->ifconfig.type<two->ifconfig.type)
return -1;
if (p2<p1)
if (two->ifconfig.type<one->ifconfig.type)
return 1;
return 0;
}
@ -493,13 +478,10 @@ int overlay_destination_configure(struct network_destination *dest, const struct
packet_interval = 100;
break;
case OVERLAY_INTERFACE_WIFI:
case OVERLAY_INTERFACE_OTHER:
tick_ms = 500;
packet_interval = 800;
break;
case OVERLAY_INTERFACE_UNKNOWN:
tick_ms = 500;
packet_interval = 100;
break;
}
if (dest->ifconfig.tick_ms<0)
@ -555,7 +537,8 @@ int overlay_interface_configure(struct overlay_interface *interface, const struc
* Returns -1 in case of error (misconfiguration or system error).
*/
int
overlay_interface_init(const char *name,
overlay_interface_init(const char *name,
short detected_type,
const struct socket_address *addr,
const struct socket_address *netmask,
const struct socket_address *broadcast,
@ -580,7 +563,8 @@ overlay_interface_init(const char *name,
interface->state=INTERFACE_STATE_DOWN;
buf_strncpy_nul(interface->name, name);
interface->detected_type = detected_type;
interface->destination = new_destination(interface);
interface->alarm.poll.fd=-1;
@ -648,7 +632,10 @@ overlay_interface_init(const char *name,
return -1;
interface->state=INTERFACE_STATE_UP;
INFOF("Interface %s addr %s, is up",interface->name, alloca_socket_address(addr));
INFOF("Interface %s type %s addr %s, is up",
interface->name,
interface_type_name(interface->detected_type),
alloca_socket_address(addr));
INFOF("Allowing a maximum of %d packets every %"PRId64"ms",
interface->destination->transfer_limit.burst_size,
interface->destination->transfer_limit.burst_length);
@ -1085,13 +1072,17 @@ int overlay_broadcast_ensemble(struct network_destination *destination, struct o
}
}
static const struct config_network_interface *find_interface_config(const char *name, int socket_type)
static const struct config_network_interface *find_interface_config(const char *name, short detected_type, int socket_type)
{
// Find a matching non-dummy interface rule.
unsigned i;
for (i = 0; i < config.interfaces.ac; ++i) {
const struct config_network_interface *ifconfig = &config.interfaces.av[i].value;
if (ifconfig->socket_type==socket_type) {
if (ifconfig->socket_type==socket_type && (
detected_type == ifconfig->match_type
|| detected_type == OVERLAY_INTERFACE_UNKNOWN
|| ifconfig->match_type == OVERLAY_INTERFACE_ANY
)) {
unsigned j;
for (j = 0; j < ifconfig->match.patc; ++j){
if (fnmatch(ifconfig->match.patv[j], name, 0) == 0)
@ -1109,8 +1100,31 @@ overlay_interface_register(const char *name,
const struct socket_address *netmask,
struct socket_address *broadcast)
{
short detected_type = OVERLAY_INTERFACE_UNKNOWN;
#ifdef linux
detected_type = OVERLAY_INTERFACE_OTHER;
// Try to determine the interface type from /sys/class/net/<name>/
char path[256];
struct stat file_stat;
strbuf sb = strbuf_local_buf(path);
strbuf_sprintf(sb, "/sys/class/net/%s/phy80211", name);
if (stat(path, &file_stat)==0){
// This interface has a symlink to a physical wifi device
detected_type = OVERLAY_INTERFACE_WIFI;
}else{
strbuf_reset(sb);
strbuf_sprintf(sb, "/sys/class/net/%s/speed", name);
int fd = open(path, O_RDONLY);
if (fd >= 0){
// this interface implements the ethtool get_settings method
// we *could* read this file to set config based on link speed
detected_type = OVERLAY_INTERFACE_ETHERNET;
close(fd);
}
}
#endif
// Find the matching non-dummy interface rule.
const struct config_network_interface *ifconfig = find_interface_config(name, SOCK_DGRAM);
const struct config_network_interface *ifconfig = find_interface_config(name, detected_type, SOCK_DGRAM);
if (!ifconfig) {
DEBUGF(overlayinterfaces, "Interface %s does not match any rule", name);
return 0;
@ -1131,7 +1145,7 @@ overlay_interface_register(const char *name,
return 0;
/* New interface, so register it */
if (overlay_interface_init(name, addr, netmask, broadcast, ifconfig))
if (overlay_interface_init(name, detected_type, addr, netmask, broadcast, ifconfig))
return WHYF("Could not initialise newly seen interface %s", name);
overlay_interface_init_any(ifconfig->port);
@ -1147,7 +1161,7 @@ static int interface_unregister(const char *name,
)
{
// Find the matching non-dummy interface rule.
const struct config_network_interface *ifconfig = find_interface_config(name, SOCK_DGRAM);
const struct config_network_interface *ifconfig = find_interface_config(name, OVERLAY_INTERFACE_UNKNOWN, SOCK_DGRAM);
if (!ifconfig)
return 0;
@ -1268,7 +1282,7 @@ void netlink_poll(struct sched_ent *alarm)
}
if (nlh->nlmsg_type==RTM_NEWADDR){
DEBUGF(overlayinterfaces, "New addr %s, %s, %s, %s",
DEBUGF(overlayinterfaces, "New addr %s, %s, %s, %s",
name,
alloca_socket_address(&addr),
alloca_socket_address(&broadcast),
@ -1428,7 +1442,7 @@ static void file_interface_init(const struct config_network_interface *ifconfig)
return;
}
overlay_interface_init(ifconfig->file, &addr, &netmask, &broadcast, ifconfig);
overlay_interface_init(ifconfig->file, OVERLAY_INTERFACE_UNKNOWN, &addr, &netmask, &broadcast, ifconfig);
}
static void rescan_soon(time_ms_t run_at){
@ -1461,7 +1475,8 @@ void overlay_interface_config_change()
continue;
const struct config_network_interface *ifconfig = find_interface_config(
overlay_interfaces[i].name,
overlay_interfaces[i].name,
OVERLAY_INTERFACE_UNKNOWN,
overlay_interfaces[i].ifconfig.socket_type
);

View File

@ -68,7 +68,8 @@ typedef struct overlay_interface {
struct sched_ent alarm;
char name[256];
short detected_type;
off_t recv_offset; /* file offset */
int recv_count;
@ -115,7 +116,8 @@ struct config_network_interface;
int overlay_interface_configure(struct overlay_interface *interface, const struct config_network_interface *ifconfig);
int
overlay_interface_init(const char *name,
overlay_interface_init(const char *name,
short detected_type,
const struct socket_address *addr,
const struct socket_address *netmask,
const struct socket_address *broadcast,

View File

@ -1408,7 +1408,7 @@ static void mdp_interface_packet(struct socket_address *client, struct mdp_heade
if (result == CFOK || result == CFEMPTY){
struct overlay_interface *interface=overlay_interface_find_name_addr(NULL, client);
if (!interface){
overlay_interface_init(ifconfig.match.patv[0], client, NULL, NULL, &ifconfig);
overlay_interface_init(ifconfig.match.patv[0], OVERLAY_INTERFACE_UNKNOWN, client, NULL, NULL, &ifconfig);
}else{
// reconfigure the interface with new / current settings
if (overlay_interface_configure(interface, &ifconfig)==-1)

View File

@ -255,6 +255,38 @@ test_InterfacesModernDummy() {
executeOk_servald config set interfaces.0.file dummyname
}
doc_InterfacesMatchType="Config options 'interfaces.N.[type|match_type]'"
test_InterfacesMatchType() {
executeOk_servald config set interfaces.0.match '*'
execute --stderr --core-backtrace --executable=$servald \
config set interfaces.0.type 'any'
assertExitStatus '==' 2
assert_stderr_log \
--warn-pattern='"interfaces".*invalid' \
--error-pattern='config file.*loaded despite defects.*invalid'
execute --stderr --core-backtrace --executable=$servald \
config set interfaces.0.type 'unknown'
assertExitStatus '==' 2
assert_stderr_log \
--warn-pattern='"interfaces".*invalid' \
--error-pattern='config file.*loaded despite defects.*invalid'
execute --stderr --core-backtrace --executable=$servald \
config del interfaces.0.type
executeOk_servald config set interfaces.0.type wifi
executeOk_servald config set interfaces.0.type ethernet
execute --stderr --core-backtrace --executable=$servald \
config set interfaces.0.match_type 'unknown'
assertExitStatus '==' 2
assert_stderr_log \
--warn-pattern='"interfaces".*invalid' \
--error-pattern='config file.*loaded despite defects.*invalid'
execute --stderr --core-backtrace --executable=$servald \
config del interfaces.0.match_type
executeOk_servald config set interfaces.0.match_type wifi
executeOk_servald config set interfaces.0.match_type ethernet
executeOk_servald config set interfaces.0.match_type any
}
doc_InterfacesModernIncompatible="Config options 'interfaces.match' and 'interfaces.file' are incompatible"
test_InterfacesModernIncompatible() {
executeOk_servald config set interfaces.0.match eth