Commit 4bd751ee authored by Sven Eckelmann's avatar Sven Eckelmann Committed by Simon Wunderlich

batctl: Implement non-routing batadv_icmp in userspace

The current endpoint for batadv_icmp* is implemented in the kernel module
and can be accessed via debugfs. But the debugfs cannot be accessed from
non-default netns or when debugfs is disabled. Thus it has be possible to
use it via the netlink infrastructure to make it compatible with future
setups.

The use of the socket file is completely removed and instead raw sockets
with BPF filters are used to send/receive batadv_icmp_packet* directly. All
information about interfaces and available originators are received via
rtnetlink and the batman-adv netlink.

The originators debugfs file is used when the batman-adv netlink commands
are not available. The routing of batadv_icmp_packets is still done inside
the kernel module.
Signed-off-by: default avatarSven Eckelmann <sven@narfation.org>
Signed-off-by: default avatarSimon Wunderlich <sw@simonwunderlich.de>
parent 98541a89
......@@ -30,6 +30,7 @@ OBJ += debug.o
OBJ += functions.o
OBJ += genl.o
OBJ += hash.o
OBJ += icmp_helper.o
OBJ += interface.o
OBJ += ioctl.o
OBJ += main.o
......
This diff is collapsed.
/*
* Copyright (C) 2007-2016 B.A.T.M.A.N. contributors:
*
* Andreas Langer <an.langer@gmx.de>, Marek Lindner <mareklindner@neomailbox.ch>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA
*
*/
#ifndef _BATCTL_ICMP_HELPER_H
#define _BATCTL_ICMP_HELPER_H
#include "main.h"
#include <linux/if_link.h>
#include <net/ethernet.h>
#include <net/if.h>
#include <netlink/netlink.h>
#include <netlink/msg.h>
#include <netlink/attr.h>
#include <stddef.h>
#include <stdint.h>
#include "list.h"
#include "packet.h"
struct timeval;
struct icmp_interface {
char name[IFNAMSIZ];
uint8_t mac[ETH_ALEN];
int sock;
int mark;
struct list_head list;
};
int icmp_interfaces_init(void);
int icmp_interface_write(const char *mesh_iface,
struct batadv_icmp_header *icmp_packet, size_t len);
void icmp_interfaces_clean(void);
ssize_t icmp_interface_read(struct batadv_icmp_header *icmp_packet, size_t len,
struct timeval *tv);
#endif
......@@ -1309,7 +1309,7 @@ static int nlquery_stop_cb(struct nl_msg *msg, void *arg)
}
static int netlink_query_common(const char *mesh_iface, uint8_t nl_cmd,
nl_recvmsg_msg_cb_t callback,
nl_recvmsg_msg_cb_t callback, int flags,
struct nlquery_opts *query_opts)
{
struct nl_sock *sock;
......@@ -1359,7 +1359,7 @@ static int netlink_query_common(const char *mesh_iface, uint8_t nl_cmd,
goto err_free_cb;
}
genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, family, 0, NLM_F_DUMP,
genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, family, 0, flags,
nl_cmd, 1);
nla_put_u32(msg, BATADV_ATTR_MESH_IFINDEX, ifindex);
......@@ -1448,7 +1448,8 @@ int translate_mac_netlink(const char *mesh_iface, const struct ether_addr *mac,
ret = netlink_query_common(mesh_iface,
BATADV_CMD_GET_TRANSTABLE_GLOBAL,
translate_mac_netlink_cb, &opts.query_opts);
translate_mac_netlink_cb, NLM_F_DUMP,
&opts.query_opts);
if (ret < 0)
return ret;
......@@ -1459,3 +1460,174 @@ int translate_mac_netlink(const char *mesh_iface, const struct ether_addr *mac,
return 0;
}
static const int get_nexthop_netlink_mandatory[] = {
BATADV_ATTR_ORIG_ADDRESS,
BATADV_ATTR_NEIGH_ADDRESS,
BATADV_ATTR_HARD_IFINDEX,
};
struct get_nexthop_netlink_opts {
struct ether_addr mac;
uint8_t *nexthop;
char *ifname;
bool found;
struct nlquery_opts query_opts;
};
static int get_nexthop_netlink_cb(struct nl_msg *msg, void *arg)
{
struct nlattr *attrs[BATADV_ATTR_MAX+1];
struct nlmsghdr *nlh = nlmsg_hdr(msg);
struct nlquery_opts *query_opts = arg;
struct get_nexthop_netlink_opts *opts;
struct genlmsghdr *ghdr;
const uint8_t *orig;
const uint8_t *neigh;
uint32_t index;
const char *ifname;
opts = container_of(query_opts, struct get_nexthop_netlink_opts,
query_opts);
if (!genlmsg_valid_hdr(nlh, 0))
return NL_OK;
ghdr = nlmsg_data(nlh);
if (ghdr->cmd != BATADV_CMD_GET_ORIGINATORS)
return NL_OK;
if (nla_parse(attrs, BATADV_ATTR_MAX, genlmsg_attrdata(ghdr, 0),
genlmsg_len(ghdr), batadv_netlink_policy)) {
return NL_OK;
}
if (missing_mandatory_attrs(attrs, get_nexthop_netlink_mandatory,
ARRAY_SIZE(get_nexthop_netlink_mandatory)))
return NL_OK;
orig = nla_data(attrs[BATADV_ATTR_ORIG_ADDRESS]);
neigh = nla_data(attrs[BATADV_ATTR_NEIGH_ADDRESS]);
index = nla_get_u32(attrs[BATADV_ATTR_HARD_IFINDEX]);
if (!attrs[BATADV_ATTR_FLAG_BEST])
return NL_OK;
if (memcmp(&opts->mac, orig, ETH_ALEN) != 0)
return NL_OK;
/* save result */
memcpy(opts->nexthop, neigh, ETH_ALEN);
ifname = if_indextoname(index, opts->ifname);
if (!ifname)
return NL_OK;
opts->found = true;
opts->query_opts.err = 0;
return NL_STOP;
}
int get_nexthop_netlink(const char *mesh_iface, const struct ether_addr *mac,
uint8_t *nexthop, char *ifname)
{
struct get_nexthop_netlink_opts opts = {
.nexthop = 0,
.found = false,
.query_opts = {
.err = 0,
},
};
int ret;
memcpy(&opts.mac, mac, ETH_ALEN);
opts.nexthop = nexthop;
opts.ifname = ifname;
ret = netlink_query_common(mesh_iface, BATADV_CMD_GET_ORIGINATORS,
get_nexthop_netlink_cb, NLM_F_DUMP,
&opts.query_opts);
if (ret < 0)
return ret;
if (!opts.found)
return -ENOENT;
return 0;
}
static const int get_primarymac_netlink_mandatory[] = {
BATADV_ATTR_HARD_ADDRESS,
};
struct get_primarymac_netlink_opts {
uint8_t *primarymac;
bool found;
struct nlquery_opts query_opts;
};
static int get_primarymac_netlink_cb(struct nl_msg *msg, void *arg)
{
struct nlattr *attrs[BATADV_ATTR_MAX+1];
struct nlmsghdr *nlh = nlmsg_hdr(msg);
struct nlquery_opts *query_opts = arg;
struct get_primarymac_netlink_opts *opts;
struct genlmsghdr *ghdr;
const uint8_t *primary_mac;
opts = container_of(query_opts, struct get_primarymac_netlink_opts,
query_opts);
if (!genlmsg_valid_hdr(nlh, 0))
return NL_OK;
ghdr = nlmsg_data(nlh);
if (ghdr->cmd != BATADV_CMD_GET_MESH_INFO)
return NL_OK;
if (nla_parse(attrs, BATADV_ATTR_MAX, genlmsg_attrdata(ghdr, 0),
genlmsg_len(ghdr), batadv_netlink_policy)) {
return NL_OK;
}
if (missing_mandatory_attrs(attrs, get_primarymac_netlink_mandatory,
ARRAY_SIZE(get_primarymac_netlink_mandatory)))
return NL_OK;
primary_mac = nla_data(attrs[BATADV_ATTR_HARD_ADDRESS]);
/* save result */
memcpy(opts->primarymac, primary_mac, ETH_ALEN);
opts->found = true;
opts->query_opts.err = 0;
return NL_STOP;
}
int get_primarymac_netlink(const char *mesh_iface, uint8_t *primarymac)
{
struct get_primarymac_netlink_opts opts = {
.primarymac = 0,
.found = false,
.query_opts = {
.err = 0,
},
};
int ret;
opts.primarymac = primarymac;
ret = netlink_query_common(mesh_iface, BATADV_CMD_GET_MESH_INFO,
get_primarymac_netlink_cb, 0,
&opts.query_opts);
if (ret < 0)
return ret;
if (!opts.found)
return -ENOENT;
return 0;
}
......@@ -47,6 +47,9 @@ int netlink_print_bla_backbone(char *mesh_iface, char *orig_iface, int read_opt,
int translate_mac_netlink(const char *mesh_iface, const struct ether_addr *mac,
struct ether_addr *mac_out);
int get_nexthop_netlink(const char *mesh_iface, const struct ether_addr *mac,
uint8_t *nexthop, char *ifname);
int get_primarymac_netlink(const char *mesh_iface, uint8_t *primarymac);
extern struct nla_policy batadv_netlink_policy[];
......
......@@ -42,6 +42,7 @@
#include "packet.h"
#include "bat-hosts.h"
#include "debugfs.h"
#include "icmp_helper.h"
char is_aborted = 0;
......@@ -78,8 +79,7 @@ int ping(char *mesh_iface, int argc, char **argv)
struct ether_addr *dst_mac = NULL, *rr_mac = NULL;
struct bat_host *bat_host, *rr_host;
ssize_t read_len;
fd_set read_socket;
int ret = EXIT_FAILURE, ping_fd = -1, res, optchar, found_args = 1;
int ret = EXIT_FAILURE, res, optchar, found_args = 1;
int loop_count = -1, loop_interval = 0, timeout = 1, rr = 0, i;
unsigned int seq_counter = 0, packets_out = 0, packets_in = 0, packets_loss;
char *dst_string, *mac_string, *rr_string;
......@@ -88,7 +88,6 @@ int ping(char *mesh_iface, int argc, char **argv)
uint8_t last_rr_cur = 0, last_rr[BATADV_RR_LEN][ETH_ALEN];
size_t packet_len;
char *debugfs_mnt;
char icmp_socket[MAX_PATH+1];
int disable_translate_mac = 0;
while ((optchar = getopt(argc, argv, "hc:i:t:RT")) != -1) {
......@@ -163,17 +162,7 @@ int ping(char *mesh_iface, int argc, char **argv)
goto out;
}
debugfs_make_path(SOCKET_PATH_FMT, mesh_iface, icmp_socket, sizeof(icmp_socket));
ping_fd = open(icmp_socket, O_RDWR);
if (ping_fd < 0) {
fprintf(stderr, "Error - can't open a connection to the batman adv kernel module via the socket '%s': %s\n",
icmp_socket, strerror(errno));
printf("Check whether the module is loaded and active.\n");
goto out;
}
icmp_interfaces_init();
packet_len = sizeof(struct batadv_icmp_packet);
memset(&icmp_packet_out, 0, sizeof(icmp_packet_out));
......@@ -208,36 +197,32 @@ int ping(char *mesh_iface, int argc, char **argv)
icmp_packet_out.seqno = htons(++seq_counter);
if (write(ping_fd, (char *)&icmp_packet_out, packet_len) < 0) {
fprintf(stderr, "Error - can't write to batman adv kernel file '%s': %s\n", icmp_socket, strerror(errno));
res = icmp_interface_write(mesh_iface,
(struct batadv_icmp_header *)&icmp_packet_out,
packet_len);
if (res < 0) {
fprintf(stderr, "Error - can't send icmp packet: %s\n", strerror(res));
goto sleep;
}
read_packet:
start_timer();
FD_ZERO(&read_socket);
FD_SET(ping_fd, &read_socket);
res = select(ping_fd + 1, &read_socket, NULL, NULL, &tv);
read_len = icmp_interface_read((struct batadv_icmp_header *)&icmp_packet_in,
packet_len, &tv);
if (is_aborted)
break;
packets_out++;
if (res == 0) {
if (read_len == 0) {
printf("Reply from host %s timed out\n", dst_string);
goto sleep;
}
if (res < 0)
goto sleep;
read_len = read(ping_fd, (char *)&icmp_packet_in, packet_len);
if (read_len < 0) {
fprintf(stderr, "Error - can't read from batman adv kernel file '%s': %s\n", icmp_socket, strerror(errno));
fprintf(stderr, "Error - can't receive icmp packets: %s\n", strerror(read_len));
goto sleep;
}
......@@ -353,8 +338,7 @@ sleep:
ret = EXIT_NOSUCCESS;
out:
icmp_interfaces_clean();
bat_hosts_free();
if (ping_fd >= 0)
close(ping_fd);
return ret;
}
......@@ -39,6 +39,7 @@
#include "packet.h"
#include "bat-hosts.h"
#include "debugfs.h"
#include "icmp_helper.h"
#define TTL_MAX 50
......@@ -60,14 +61,12 @@ int traceroute(char *mesh_iface, int argc, char **argv)
struct bat_host *bat_host;
struct ether_addr *dst_mac = NULL;
struct timeval tv;
fd_set read_socket;
ssize_t read_len;
char *dst_string, *mac_string, *return_mac, dst_reached = 0;
int ret = EXIT_FAILURE, res, trace_fd = -1, i;
int ret = EXIT_FAILURE, res, i;
int found_args = 1, optchar, seq_counter = 0, read_opt = USE_BAT_HOSTS;
double time_delta[NUM_PACKETS];
char *debugfs_mnt;
char icmp_socket[MAX_PATH+1];
int disable_translate_mac = 0;
while ((optchar = getopt(argc, argv, "hnT")) != -1) {
......@@ -122,16 +121,7 @@ int traceroute(char *mesh_iface, int argc, char **argv)
goto out;
}
debugfs_make_path(SOCKET_PATH_FMT, mesh_iface, icmp_socket, sizeof(icmp_socket));
trace_fd = open(icmp_socket, O_RDWR);
if (trace_fd < 0) {
fprintf(stderr, "Error - can't open a connection to the batman adv kernel module via the socket '%s': %s\n",
icmp_socket, strerror(errno));
fprintf(stderr, "Check whether the module is loaded and active.\n");
goto out;
}
icmp_interfaces_init();
memset(&icmp_packet_out, 0, sizeof(icmp_packet_out));
memcpy(&icmp_packet_out.dst, dst_mac, ETH_ALEN);
......@@ -154,8 +144,11 @@ int traceroute(char *mesh_iface, int argc, char **argv)
icmp_packet_out.seqno = htons(++seq_counter);
time_delta[i] = 0.0;
if (write(trace_fd, (char *)&icmp_packet_out, sizeof(icmp_packet_out)) < 0) {
fprintf(stderr, "Error - can't write to batman adv kernel file '%s': %s\n", icmp_socket, strerror(errno));
res = icmp_interface_write(mesh_iface,
(struct batadv_icmp_header *)&icmp_packet_out,
sizeof(icmp_packet_out));
if (res < 0) {
fprintf(stderr, "Error - can't send icmp packet: %s\n", strerror(res));
continue;
}
......@@ -165,21 +158,11 @@ read_packet:
tv.tv_sec = 2;
tv.tv_usec = 0;
FD_ZERO(&read_socket);
FD_SET(trace_fd, &read_socket);
res = select(trace_fd + 1, &read_socket, NULL, NULL, &tv);
if (res <= 0)
read_len = icmp_interface_read((struct batadv_icmp_header *)&icmp_packet_in,
sizeof(icmp_packet_in), &tv);
if (read_len <= 0)
continue;
read_len = read(trace_fd, (char *)&icmp_packet_in, sizeof(icmp_packet_in));
if (read_len < 0) {
fprintf(stderr, "Error - can't read from batman adv kernel file '%s': %s\n", icmp_socket, strerror(errno));
continue;
}
if ((size_t)read_len < sizeof(icmp_packet_in)) {
printf("Warning - dropping received packet as it is smaller than expected (%zu): %zd\n",
sizeof(icmp_packet_in), read_len);
......@@ -241,8 +224,7 @@ read_packet:
ret = EXIT_SUCCESS;
out:
icmp_interfaces_clean();
bat_hosts_free();
if (trace_fd >= 0)
close(trace_fd);
return ret;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment