hwsim_test: Tool for testing data connectivity with mac80211_hwsim
This program can be used to verify that both unicast and broadcast data frames can be transmitted successfully through mac80211_hwsim interfaces.
This commit is contained in:
parent
92305c5d9a
commit
9e8cda5a46
2 changed files with 255 additions and 0 deletions
11
mac80211_hwsim/tools/Makefile
Normal file
11
mac80211_hwsim/tools/Makefile
Normal file
|
@ -0,0 +1,11 @@
|
|||
all: hwsim_test
|
||||
|
||||
ifndef CC
|
||||
CC=gcc
|
||||
endif
|
||||
|
||||
ifndef CFLAGS
|
||||
CFLAGS = -O2 -Wall -g
|
||||
endif
|
||||
|
||||
hwsim_test: hwsim_test.o
|
244
mac80211_hwsim/tools/hwsim_test.c
Normal file
244
mac80211_hwsim/tools/hwsim_test.c
Normal file
|
@ -0,0 +1,244 @@
|
|||
/*
|
||||
* hwsim_test - Data connectivity test for mac80211_hwsim
|
||||
* Copyright (c) 2009, Atheros Communications
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Alternatively, this software may be distributed under the terms of BSD
|
||||
* license.
|
||||
*
|
||||
* See README and COPYING for more details.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/select.h>
|
||||
#include <netpacket/packet.h>
|
||||
#include <net/ethernet.h>
|
||||
#include <net/if.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5]
|
||||
#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x"
|
||||
|
||||
#define HWSIM_ETHERTYPE ETHERTYPE_IP
|
||||
#define HWSIM_PACKETLEN 1500
|
||||
|
||||
static unsigned char addr1[ETH_ALEN], addr2[ETH_ALEN], bcast[ETH_ALEN];
|
||||
|
||||
static void tx(int s, const char *ifname, int ifindex,
|
||||
const unsigned char *src, const unsigned char *dst)
|
||||
{
|
||||
char buf[HWSIM_PACKETLEN], *pos;
|
||||
struct ether_header *eth;
|
||||
int i;
|
||||
|
||||
printf("TX: %s(ifindex=%d) " MACSTR " -> " MACSTR "\n",
|
||||
ifname, ifindex, MAC2STR(src), MAC2STR(dst));
|
||||
|
||||
eth = (struct ether_header *) buf;
|
||||
memcpy(eth->ether_dhost, dst, ETH_ALEN);
|
||||
memcpy(eth->ether_shost, src, ETH_ALEN);
|
||||
eth->ether_type = htons(HWSIM_ETHERTYPE);
|
||||
pos = (char *) (eth + 1);
|
||||
for (i = 0; i < sizeof(buf) - sizeof(*eth); i++)
|
||||
*pos++ = i;
|
||||
|
||||
if (send(s, buf, sizeof(buf), 0) < 0)
|
||||
perror("send");
|
||||
}
|
||||
|
||||
|
||||
struct rx_result {
|
||||
int rx_unicast1:1;
|
||||
int rx_broadcast1:1;
|
||||
int rx_unicast2:1;
|
||||
int rx_broadcast2:1;
|
||||
};
|
||||
|
||||
|
||||
static void rx(int s, int iface, const char *ifname, int ifindex,
|
||||
struct rx_result *res)
|
||||
{
|
||||
char buf[HWSIM_PACKETLEN + 1], *pos;
|
||||
struct ether_header *eth;
|
||||
int len, i;
|
||||
|
||||
len = recv(s, buf, sizeof(buf), 0);
|
||||
if (len < 0) {
|
||||
perror("recv");
|
||||
return;
|
||||
}
|
||||
eth = (struct ether_header *) buf;
|
||||
|
||||
printf("RX: %s(ifindex=%d) " MACSTR " -> " MACSTR " (len=%d)\n",
|
||||
ifname, ifindex,
|
||||
MAC2STR(eth->ether_shost), MAC2STR(eth->ether_dhost), len);
|
||||
|
||||
if (len != HWSIM_PACKETLEN) {
|
||||
printf("Ignore frame with unexpected RX length\n");
|
||||
return;
|
||||
}
|
||||
|
||||
pos = (char *) (eth + 1);
|
||||
for (i = 0; i < sizeof(buf) - 1 - sizeof(*eth); i++) {
|
||||
if ((unsigned char) *pos != (unsigned char) i) {
|
||||
printf("Ignore frame with unexpected contents\n");
|
||||
printf("i=%d received=0x%x expected=0x%x\n",
|
||||
i, (unsigned char) *pos, (unsigned char) i);
|
||||
return;
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
|
||||
if (iface == 1 &&
|
||||
memcmp(eth->ether_dhost, addr1, ETH_ALEN) == 0 &&
|
||||
memcmp(eth->ether_shost, addr2, ETH_ALEN) == 0)
|
||||
res->rx_unicast1 = 1;
|
||||
else if (iface == 1 &&
|
||||
memcmp(eth->ether_dhost, bcast, ETH_ALEN) == 0 &&
|
||||
memcmp(eth->ether_shost, addr2, ETH_ALEN) == 0)
|
||||
res->rx_broadcast1 = 1;
|
||||
else if (iface == 2 &&
|
||||
memcmp(eth->ether_dhost, addr2, ETH_ALEN) == 0 &&
|
||||
memcmp(eth->ether_shost, addr1, ETH_ALEN) == 0)
|
||||
res->rx_unicast2 = 1;
|
||||
else if (iface == 2 &&
|
||||
memcmp(eth->ether_dhost, bcast, ETH_ALEN) == 0 &&
|
||||
memcmp(eth->ether_shost, addr1, ETH_ALEN) == 0)
|
||||
res->rx_broadcast2 = 1;
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int s1 = -1, s2 = -1, ret = -1;
|
||||
struct ifreq ifr;
|
||||
int ifindex1, ifindex2;
|
||||
struct sockaddr_ll ll;
|
||||
fd_set rfds;
|
||||
struct timeval tv;
|
||||
struct rx_result res;
|
||||
|
||||
if (argc != 3) {
|
||||
fprintf(stderr, "usage: hwsim_test <ifname1> <ifname2>\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(bcast, 0xff, ETH_ALEN);
|
||||
|
||||
s1 = socket(PF_PACKET, SOCK_RAW, htons(HWSIM_ETHERTYPE));
|
||||
if (s1 < 0) {
|
||||
perror("socket");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
s2 = socket(PF_PACKET, SOCK_RAW, htons(HWSIM_ETHERTYPE));
|
||||
if (s2 < 0) {
|
||||
perror("socket");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
memset(&ifr, 0, sizeof(ifr));
|
||||
strncpy(ifr.ifr_name, argv[1], sizeof(ifr.ifr_name));
|
||||
if (ioctl(s1, SIOCGIFINDEX, &ifr) < 0) {
|
||||
perror("ioctl[SIOCGIFINDEX]");
|
||||
goto fail;
|
||||
}
|
||||
ifindex1 = ifr.ifr_ifindex;
|
||||
if (ioctl(s1, SIOCGIFHWADDR, &ifr) < 0) {
|
||||
perror("ioctl[SIOCGIFHWADDR]");
|
||||
goto fail;
|
||||
}
|
||||
memcpy(addr1, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
|
||||
|
||||
memset(&ifr, 0, sizeof(ifr));
|
||||
strncpy(ifr.ifr_name, argv[2], sizeof(ifr.ifr_name));
|
||||
if (ioctl(s2, SIOCGIFINDEX, &ifr) < 0) {
|
||||
perror("ioctl[SIOCGIFINDEX]");
|
||||
goto fail;
|
||||
}
|
||||
ifindex2 = ifr.ifr_ifindex;
|
||||
if (ioctl(s2, SIOCGIFHWADDR, &ifr) < 0) {
|
||||
perror("ioctl[SIOCGIFHWADDR]");
|
||||
goto fail;
|
||||
}
|
||||
memcpy(addr2, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
|
||||
|
||||
memset(&ll, 0, sizeof(ll));
|
||||
ll.sll_family = PF_PACKET;
|
||||
ll.sll_ifindex = ifindex1;
|
||||
ll.sll_protocol = htons(HWSIM_ETHERTYPE);
|
||||
if (bind(s1, (struct sockaddr *) &ll, sizeof(ll)) < 0) {
|
||||
perror("bind");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
memset(&ll, 0, sizeof(ll));
|
||||
ll.sll_family = PF_PACKET;
|
||||
ll.sll_ifindex = ifindex2;
|
||||
ll.sll_protocol = htons(HWSIM_ETHERTYPE);
|
||||
if (bind(s2, (struct sockaddr *) &ll, sizeof(ll)) < 0) {
|
||||
perror("bind");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
tx(s1, argv[1], ifindex1, addr1, addr2);
|
||||
tx(s1, argv[1], ifindex1, addr1, bcast);
|
||||
tx(s2, argv[2], ifindex2, addr2, addr1);
|
||||
tx(s2, argv[2], ifindex2, addr2, bcast);
|
||||
|
||||
tv.tv_sec = 1;
|
||||
tv.tv_usec = 0;
|
||||
|
||||
memset(&res, 0, sizeof(res));
|
||||
for (;;) {
|
||||
int r;
|
||||
FD_ZERO(&rfds);
|
||||
FD_SET(s1, &rfds);
|
||||
FD_SET(s2, &rfds);
|
||||
|
||||
r = select(s2 + 1, &rfds, NULL, NULL, &tv);
|
||||
if (r < 0) {
|
||||
perror("select");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (r == 0)
|
||||
break; /* timeout */
|
||||
|
||||
if (FD_SET(s1, &rfds))
|
||||
rx(s1, 1, argv[1], ifindex1, &res);
|
||||
if (FD_SET(s2, &rfds))
|
||||
rx(s2, 2, argv[2], ifindex2, &res);
|
||||
|
||||
if (res.rx_unicast1 && res.rx_broadcast1 &&
|
||||
res.rx_unicast2 && res.rx_broadcast2) {
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
printf("Did not receive all expected frames:\n"
|
||||
"rx_unicast1=%d rx_broadcast1=%d "
|
||||
"rx_unicast2=%d rx_broadcast2=%d\n",
|
||||
res.rx_unicast1, res.rx_broadcast1,
|
||||
res.rx_unicast2, res.rx_broadcast2);
|
||||
} else {
|
||||
printf("Both unicast and broadcast working in both "
|
||||
"directions\n");
|
||||
}
|
||||
|
||||
fail:
|
||||
close(s1);
|
||||
close(s2);
|
||||
|
||||
return ret;
|
||||
}
|
Loading…
Reference in a new issue