/*
Raw socket (un) marshalling tests
Copyright (C) Martin Schwenke 2018
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
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, see .
*/
#include "replace.h"
#include
/* For ether_aton() */
#ifdef _AIX
#include
#endif
#ifdef __FreeBSD__
#include
#endif
#ifdef linux
#include
#endif
#include "common/system_socket.c"
#include "protocol/protocol_util.h"
#include "tests/src/test_backtrace.h"
static void hexdump(uint8_t *buf, size_t len)
{
size_t i;
for (i = 0; i < len; i++) {
if (i % 16 == 0) {
if (i != 0) {
printf("\n");
}
printf("%06zx", i);
}
printf(" %02x", buf[i]);
}
printf("\n%06zx\n", i);
}
static void test_types(void)
{
/*
* We use this struct in the code but don't pack it due to
* portability concerns. It should have no padding.
*/
struct {
struct ip ip;
struct tcphdr tcp;
} ip4pkt;
assert(sizeof(ip4pkt) == sizeof(struct ip) + sizeof(struct tcphdr));
}
#ifdef HAVE_PACKETSOCKET
static void test_arp(const char *addr_str, const char *hwaddr_str, bool reply)
{
ctdb_sock_addr addr;
struct ether_addr *hw, *dhw;
uint8_t buf[512];
size_t buflen = sizeof(buf);
size_t len;
int ret;
ret = ctdb_sock_addr_from_string(addr_str, &addr, false);
assert(ret == 0);
hw = ether_aton(hwaddr_str);
assert(hw != NULL);
switch (addr.ip.sin_family) {
case AF_INET:
ret = arp_build(buf, buflen, &addr.ip, hw, reply, &dhw, &len);
break;
case AF_INET6:
ret = ip6_na_build(buf, buflen, &addr.ip6, hw, &dhw, &len);
break;
default:
abort();
}
assert(ret == 0);
hexdump(buf, len);
}
#else /* HAVE_PACKETSOCKET */
static void test_arp(const char *addr_str, const char *hwaddr_str, bool reply)
{
fprintf(stderr, "PACKETSOCKET not supported\n");
}
#endif /* HAVE_PACKETSOCKET */
static void test_tcp(const char *src_str,
const char *dst_str,
const char *seq_str,
const char *ack_str,
const char *rst_str)
{
ctdb_sock_addr src, dst;
uint32_t seq, ack;
int rst;
uint8_t buf[512];
struct ether_header *eth;
size_t expected_len, len;
char src_str_out[64], dst_str_out[64];
uint32_t seq_out, ack_out;
int rst_out = 0;
uint16_t window;
int ret;
ret = ctdb_sock_addr_from_string(src_str, &src, true);
assert(ret == 0);
ret = ctdb_sock_addr_from_string(dst_str, &dst, true);
assert(ret == 0);
seq = atoi(seq_str);
ack = atoi(ack_str);
rst = atoi(rst_str);
/* Need to fake this up */
eth = (struct ether_header *) buf;
memset(eth, 0, sizeof(*eth));
switch (src.ip.sin_family) {
case AF_INET:
eth->ether_type = htons(ETHERTYPE_IP);
expected_len = 40;
ret = tcp4_build(buf + sizeof(struct ether_header),
sizeof(buf) - sizeof(struct ether_header),
&src.ip,
&dst.ip,
seq,
ack,
rst,
&len);
break;
case AF_INET6:
eth->ether_type = htons(ETHERTYPE_IP6);
expected_len = 60;
ret = tcp6_build(buf + sizeof(struct ether_header),
sizeof(buf) - sizeof(struct ether_header),
&src.ip6,
&dst.ip6,
seq,
ack,
rst,
&len);
break;
default:
abort();
}
assert(ret == 0);
assert(len == expected_len);
hexdump(buf + sizeof(struct ether_header), len);
switch (ntohs(eth->ether_type)) {
case ETHERTYPE_IP:
ret = tcp4_extract(buf + sizeof(struct ether_header),
len,
&src.ip,
&dst.ip,
&ack_out,
&seq_out,
&rst_out,
&window);
break;
case ETHERTYPE_IP6:
ret = tcp6_extract(buf + sizeof(struct ether_header),
len,
&src.ip6,
&dst.ip6,
&ack_out,
&seq_out,
&rst_out,
&window);
break;
default:
abort();
}
assert(ret == 0);
assert(seq == seq_out);
assert(ack == ack_out);
assert((rst != 0) == (rst_out != 0));
assert(window == htons(1234));
ret = ctdb_sock_addr_to_buf(src_str_out, sizeof(src_str_out),
&src, true);
assert(ret == 0);
ret = strcmp(src_str, src_str_out);
assert(ret == 0);
ret = ctdb_sock_addr_to_buf(dst_str_out, sizeof(dst_str_out),
&dst, true);
assert(ret == 0);
ret = strcmp(dst_str, dst_str_out);
assert(ret == 0);
}
static void usage(const char *prog)
{
fprintf(stderr, "usage: %s [ ...]\n", prog);
fprintf(stderr, " commands:\n");
fprintf(stderr, " types\n");
fprintf(stderr, " arp [reply]\n");
fprintf(stderr, " tcp \n");
exit(1);
}
int main(int argc, char **argv)
{
if (argc < 2) {
usage(argv[0]);
}
test_backtrace_setup();
if (strcmp(argv[1], "types") == 0) {
test_types();
} else if (strcmp(argv[1], "arp") == 0) {
/*
* Extra arg indicates that a reply should be
* constructed for IPv4 - value is ignored
*/
if (argc != 4 && argc != 5) {
usage(argv[0]);
}
test_arp(argv[2], argv[3], (argc == 5));
} else if (strcmp(argv[1], "tcp") == 0) {
if (argc != 7) {
usage(argv[0]);
}
test_tcp(argv[2], argv[3], argv[4], argv[5], argv[6]);
} else {
usage(argv[0]);
}
return 0;
}