mirror of
https://github.com/servalproject/serval-dna.git
synced 2024-12-18 20:57:56 +00:00
Detect Wifi and cabled ethernet on linux systems, allow matching by type in config
This commit is contained in:
parent
68deae8393
commit
ce2c276570
1
conf.h
1
conf.h
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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,)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
);
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
32
tests/config
32
tests/config
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user