[PATCH wpan-tools 1/3] wpan-ping: Ping utility on IEEE 802.15.4 level

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



wpan-ping aims to offer ping/ping6 like functionality on IEEE 802.15.4 level.

No control message protocol is defined so we will simply use DGRAM's over a
plain socket with a server component to emulate the behaviour. Right now it
is possible to specify packet count as well as size and the interface to be
used. So far it uses short addresses only. Support for extended addresses is
on the todo list.

Signed-off-by: Stefan Schmidt <stefan@xxxxxxxxxxxxxxx>
---
 Makefile.am           |   3 +-
 configure.ac          |   1 +
 wpan-ping/Makefile.am |   6 +
 wpan-ping/wpan-ping.c | 416 ++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 425 insertions(+), 1 deletion(-)
 create mode 100644 wpan-ping/Makefile.am
 create mode 100644 wpan-ping/wpan-ping.c

diff --git a/Makefile.am b/Makefile.am
index 06c72cd..2584cb2 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -20,4 +20,5 @@ AM_LDFLAGS = \
 	-Wl,--as-needed
 
 SUBDIRS = \
-	src
+	src \
+	wpan-ping
diff --git a/configure.ac b/configure.ac
index 81dc596..908e5ce 100644
--- a/configure.ac
+++ b/configure.ac
@@ -55,6 +55,7 @@ AC_CONFIG_HEADERS(config.h)
 AC_CONFIG_FILES([
         Makefile
 	src/Makefile
+	wpan-ping/Makefile
 ])
 
 AC_OUTPUT
diff --git a/wpan-ping/Makefile.am b/wpan-ping/Makefile.am
new file mode 100644
index 0000000..6b511a9
--- /dev/null
+++ b/wpan-ping/Makefile.am
@@ -0,0 +1,6 @@
+bin_PROGRAMS = wpan-ping
+
+wpan_ping_SOURCES = wpan-ping.c
+
+wpan_ping_CFLAGS = $(AM_CFLAGS) $(LIBNL3_CFLAGS)
+wpan_ping_LDADD = $(LIBNL3_LIBS)
diff --git a/wpan-ping/wpan-ping.c b/wpan-ping/wpan-ping.c
new file mode 100644
index 0000000..dbcc780
--- /dev/null
+++ b/wpan-ping/wpan-ping.c
@@ -0,0 +1,416 @@
+/*
+ * Linux IEEE 802.15.4 ping tool
+ *
+ * Copyright (C) 2015 Stefan Schmidt <stefan@xxxxxxxxxxxxxxxxxx>
+ *
+ * Permission to use, copy, modify, and/or 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <net/if.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <getopt.h>
+
+#include <netlink/netlink.h>
+
+#include <netlink/genl/genl.h>
+#include <netlink/genl/family.h>
+#include <netlink/genl/ctrl.h>
+#include <netlink/msg.h>
+#include <netlink/attr.h>
+
+#include "../src/nl802154.h"
+
+#define MIN_PAYLOAD_LEN 5
+#define MAX_PAYLOAD_LEN 115
+#define IEEE802154_ADDR_LEN 8
+
+enum {
+	IEEE802154_ADDR_NONE = 0x0,
+	IEEE802154_ADDR_SHORT = 0x2,
+	IEEE802154_ADDR_LONG = 0x3,
+};
+
+struct ieee802154_addr_sa {
+	int addr_type;
+	uint16_t pan_id;
+	union {
+		uint8_t hwaddr[IEEE802154_ADDR_LEN];
+		uint16_t short_addr;
+	};
+};
+
+struct sockaddr_ieee802154 {
+	sa_family_t family;
+	struct ieee802154_addr_sa addr;
+};
+
+#ifdef HAVE_GETOPT_LONG
+static const struct option perf_long_opts[] = {
+	{ "daemon", required_argument, NULL, 'd' },
+	{ "address", required_argument, NULL, 'a' },
+	{ "count", required_argument, NULL, 'c' },
+	{ "size", required_argument, NULL, 's' },
+	{ "interface", required_argument, NULL, 'i' },
+	{ "version", no_argument, NULL, 'v' },
+	{ "help", no_argument, NULL, 'h' },
+	{ NULL, 0, NULL, 0 },
+};
+#endif
+
+struct config {
+	char packet_len;
+	unsigned short packets;
+	uint16_t dst_addr;
+	uint16_t short_addr;
+	uint16_t pan_id;
+	char server;
+	char *interface;
+	struct nl_sock *nl_sock;
+	int nl802154_id;
+};
+
+extern char *optarg;
+
+void usage(const char *name) {
+	printf("Usage: %s OPTIONS\n"
+	"OPTIONS:\n"
+	"--daemon |-d client address\n"
+	"--address | -a server address\n"
+	"--count | -c number of packets\n"
+	"--size | -s packet length\n"
+	"--interface | -i listen on this interface (default wpan0)\n"
+	"--version | -v print out version\n"
+	"--help This usage text\n", name);
+}
+
+static int nl802154_init(struct config *conf)
+{
+	int err;
+
+	conf->nl_sock = nl_socket_alloc();
+	if (!conf->nl_sock) {
+		fprintf(stderr, "Failed to allocate netlink socket.\n");
+		return -ENOMEM;
+	}
+
+	nl_socket_set_buffer_size(conf->nl_sock, 8192, 8192);
+
+	if (genl_connect(conf->nl_sock)) {
+		fprintf(stderr, "Failed to connect to generic netlink.\n");
+		err = -ENOLINK;
+		goto out_handle_destroy;
+	}
+
+	conf->nl802154_id = genl_ctrl_resolve(conf->nl_sock, "nl802154");
+	if (conf->nl802154_id < 0) {
+		fprintf(stderr, "nl802154 not found.\n");
+		err = -ENOENT;
+		goto out_handle_destroy;
+	}
+
+	return 0;
+
+out_handle_destroy:
+	nl_socket_free(conf->nl_sock);
+	return err;
+}
+
+static void nl802154_cleanup(struct config *conf)
+{
+	nl_close(conf->nl_sock);
+	nl_socket_free(conf->nl_sock);
+}
+
+static int nl_msg_cb(struct nl_msg* msg, void* arg)
+{
+	struct config *conf = arg;
+	struct sockaddr_nl nla;
+	struct nlmsghdr *nlh = nlmsg_hdr(msg);
+	struct nlattr *attrs[NL802154_ATTR_MAX+1];
+
+	struct genlmsghdr *gnlh = (struct genlmsghdr*) nlmsg_data(nlh);
+
+	nla_parse(attrs, NL802154_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+
+	if (!attrs[NL802154_ATTR_SHORT_ADDR] || !attrs[NL802154_ATTR_PAN_ID])
+		return NL_SKIP;
+
+	/* We only handle short addresses right now */
+	conf->short_addr = nla_get_u16(attrs[NL802154_ATTR_SHORT_ADDR]);
+	conf->pan_id = nla_get_u16(attrs[NL802154_ATTR_PAN_ID]);
+
+	return NL_SKIP;
+}
+
+static int get_interface_info(struct config *conf) {
+	struct nl_msg *msg;
+
+	nl802154_init(conf);
+
+	/* Build and send message */
+	nl_socket_modify_cb(conf->nl_sock, NL_CB_VALID, NL_CB_CUSTOM, nl_msg_cb, conf);
+	msg = nlmsg_alloc();
+	genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, conf->nl802154_id, 0, NLM_F_DUMP, NL802154_CMD_GET_INTERFACE, 0);
+	nla_put_string(msg, NL802154_ATTR_IFNAME, "conf->interface");
+	nl_send_sync(conf->nl_sock, msg);
+
+	nl802154_cleanup(conf);
+	return 0;
+}
+
+static void dump_packet(unsigned char *buf, int len) {
+	int i;
+
+	fprintf(stdout, "Packet payload:");
+	for (i = 0; i < len; i++) {
+		printf(" %x", buf[i]);
+	}
+	printf("\n");
+}
+
+static int generate_packet(unsigned char *buf, struct config *conf, unsigned int seq_num) {
+	int i;
+
+	buf[0] = conf->packet_len;
+	buf[1] = seq_num >> 8; /* Upper byte */
+	buf[2] = seq_num & 0xFF; /* Lower byte */
+	for (i = 3; i < conf->packet_len; i++) {
+		buf[i] = 0xAB;
+	}
+
+	return 0;
+}
+
+static int measure_roundtrip(struct config *conf, int sd) {
+	unsigned char *buf;
+	struct timeval start_time, end_time, timeout;
+	long sec = 0, usec = 0;
+	long sec_max = 0, usec_max = 0;
+	long sec_min = 2147483647, usec_min = 2147483647;
+	long sum_sec = 0, sum_usec = 0;
+	int i, ret, count;
+	unsigned short seq_num;
+	float rtt_min = 0.0, rtt_avg = 0.0, rtt_max = 0.0;
+	float packet_loss = 100.0;
+
+	fprintf(stdout, "PING 0x%04x (PAN ID 0x%04x) %i data bytes\n",
+		conf->dst_addr, conf->pan_id, conf->packet_len);
+	buf = (unsigned char *)malloc(MAX_PAYLOAD_LEN);
+
+	/* 2 seconds packet receive timeout */
+	timeout.tv_sec = 2;
+	timeout.tv_usec = 0;
+	setsockopt(sd, SOL_SOCKET, SO_RCVTIMEO, (struct timeval *)&timeout,sizeof(struct timeval));
+
+	count = 0;
+	for (i = 0; i < conf->packets; i++) {
+		generate_packet(buf, conf, i);
+		seq_num = (buf[1] << 8)| buf[2];
+		send(sd, buf, conf->packet_len, 0);
+		gettimeofday(&start_time, NULL);
+		ret = recv(sd, buf, conf->packet_len, 0);
+		if (seq_num != ((buf[1] << 8)| buf[2])) {
+			printf("Sequenze number did not match\n");
+			continue;
+		}
+		if (ret > 0) {
+			gettimeofday(&end_time, NULL);
+			count++;
+			sec = end_time.tv_sec - start_time.tv_sec;
+			sum_sec += sec;
+			usec = end_time.tv_usec - start_time.tv_usec;
+			if (usec < 0) {
+				usec += 1000000;
+				sec--;
+				sum_sec--;
+			}
+
+			sum_usec += usec;
+			if (sec > sec_max)
+				sec_max = sec;
+			else if (sec < sec_min)
+				sec_min = sec;
+			if (usec > usec_max)
+				usec_max = usec;
+			else if (usec < usec_min)
+				usec_min = usec;
+			if (sec > 0)
+				fprintf(stdout, "Warning: packet return time over a second!\n");
+
+			fprintf(stdout, "%i bytes from 0x%04x seq=%i time=%.1f ms\n", ret,
+				conf->dst_addr, (int)seq_num, (float)usec/1000);
+		} else
+			fprintf(stderr, "Hit 2s packet timeout\n");
+	}
+
+	if (count)
+		packet_loss = 100 - ((100 * count)/conf->packets);
+
+	if (usec_min)
+		rtt_min = (float)usec_min/1000;
+	if (sum_usec && count)
+		rtt_avg = ((float)sum_usec/(float)count)/1000;
+	if (usec_max)
+		rtt_max = (float)usec_max/1000;
+
+	fprintf(stdout, "\n--- 0x%04x ping statistics ---\n", conf->dst_addr);
+	fprintf(stdout, "%i packets transmitted, %i received, %.0f%% packet loss\n",
+		conf->packets, count, packet_loss);
+	fprintf(stdout, "rtt min/avg/max = %.3f/%.3f/%.3f ms\n", rtt_min, rtt_avg, rtt_max);
+
+	free(buf);
+	return 0;
+}
+
+static void init_server(struct config *conf, int sd) {
+	ssize_t len;
+	unsigned char *buf;
+//	struct sockaddr_ieee802154 src;
+//	socklen_t addrlen;
+
+//	addrlen = sizeof(src);
+
+	len = 0;
+	fprintf(stdout, "Server mode. Waiting for packets...\n");
+	buf = (unsigned char *)malloc(MAX_PAYLOAD_LEN);
+
+	while (1) {
+		//len = recvfrom(sd, buf, MAX_PAYLOAD_LEN, 0, (struct sockaddr *)&src, &addrlen);
+		len = recv(sd, buf, MAX_PAYLOAD_LEN, 0);
+		//dump_packet(buf, len);
+		/* Send same packet back */
+		send(sd, buf, len, 0);
+	}
+	free(buf);
+}
+
+static int init_network(struct config *conf) {
+	int sd;
+	int ret;
+	struct sockaddr_ieee802154 a;
+
+	sd = socket(PF_IEEE802154, SOCK_DGRAM, 0);
+	if (sd < 0) {
+		perror("socket");
+		return 1;
+	}
+
+	get_interface_info(conf);
+
+	a.family = AF_IEEE802154;
+	a.addr.addr_type = IEEE802154_ADDR_SHORT;
+	a.addr.pan_id = conf->pan_id;
+
+	/* Bind socket on this side */
+	a.addr.short_addr = conf->short_addr;
+	ret = bind(sd, (struct sockaddr *)&a, sizeof(a));
+	if (ret) {
+		perror("bind");
+		return 1;
+	}
+
+	/* Connect to other side */
+	a.addr.short_addr = conf->dst_addr;
+	ret = connect(sd, (struct sockaddr *)&a, sizeof(a));
+	if (ret) {
+		perror("connect");
+		return 1;
+	}
+
+	if (conf->server)
+		init_server(conf, sd);
+
+	measure_roundtrip(conf, sd);
+
+	shutdown(sd, SHUT_RDWR);
+	close(sd);
+	return 0;
+}
+
+int main(int argc, char *argv[]) {
+	int c;
+	struct config *conf;
+
+	conf = (struct config *) malloc(sizeof(struct config));
+
+	/* Default to interface wpan0 if nothing else is given */
+	conf->interface = "wpan0";
+
+	/* Deafult to minimum packet size */
+	conf->packet_len = MIN_PAYLOAD_LEN;
+
+	if (argc < 2) {
+		usage(argv[0]);
+		exit(1);
+	}
+
+	while (1) {
+#ifdef HAVE_GETOPT_LONG
+		int opt_idx = -1;
+		c = getopt_long(argc, argv, "b:a:c:s:i:d:vh", perf_long_opts, &opt_idx);
+#else
+		c = getopt(argc, argv, "b:a:c:s:i:d:vh");
+#endif
+		if (c == -1)
+			break;
+		switch(c) {
+		case 'a':
+			conf->dst_addr = strtol(optarg, NULL, 16);
+			break;
+		case 'd':
+			conf->server = 1;
+			conf->dst_addr = strtol(optarg, NULL, 16);
+			break;
+		case 'c':
+			conf->packets = atoi(optarg);
+			break;
+		case 's':
+			conf->packet_len = atoi(optarg);
+			if (conf->packet_len >= MAX_PAYLOAD_LEN || conf->packet_len < MIN_PAYLOAD_LEN) {
+				printf("Packet size must be between %i and %i.\n",
+				       MIN_PAYLOAD_LEN, MAX_PAYLOAD_LEN - 1);
+				return 1;
+			}
+			break;
+		case 'i':
+			conf->interface = optarg;
+			break;
+		case 'v':
+			fprintf(stdout, "wpan-ping 0.1\n");
+			return 1;
+		case 'h':
+			usage(argv[0]);
+			return 1;
+		default:
+			usage(argv[0]);
+			return 1;
+		}
+	}
+
+	init_network(conf);
+	free(conf);
+	return 0;
+}
-- 
2.1.0

--
To unsubscribe from this list: send the line "unsubscribe linux-wpan" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Linux Audio Users]     [Photo]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux