Skip to content

Instantly share code, notes, and snippets.

@majek
Last active Aug 20, 2020
Embed
What would you like to do?
conntrack experiment
#define PFATAL(x...) \
do { \
fprintf(stderr, "[-] SYSTEM ERROR : " x); \
fprintf(stderr, "\n\tLocation : %s(), %s:%u\n", __FUNCTION__, __FILE__, \
__LINE__); \
perror(" OS message "); \
fprintf(stderr, "\n"); \
exit(EXIT_FAILURE); \
} while (0)
static inline uint32_t ip_add_csum(uint8_t *p, int len) {
uint32_t sum =0;
int i;
for(i=0; i< len-1; i+=2) {
sum += p[i] | (p[i+1]<<8);
}
if (len%2) {
sum += p[len-1];
}
return sum;
}
static inline uint16_t ip_cksum_carry(uint32_t x) {
x = (x >> 16) + (x & 0xffff);
x = (x + (x >> 16)) & 0xffff;
return ~x;
}
static inline uint16_t compute_ip_checksum(struct iphdr *iphdr) {
uint32_t sum = 0;
sum += ip_add_csum((uint8_t*)iphdr, iphdr->ihl * 4);
// compensate for existing checksum
sum -= iphdr->check;
return ip_cksum_carry(sum);
}
static inline uint16_t compute_tcp_checksum(struct iphdr *iphdr, struct tcphdr *tcphdr) {
uint16_t tcp_len = ntohs(iphdr->tot_len) - iphdr->ihl*4;
uint32_t sum = 0;
sum += ip_add_csum((uint8_t*)(&iphdr->saddr), 8);
sum += htons(iphdr->protocol);
sum += htons(tcp_len);
sum += ip_add_csum((uint8_t*)tcphdr, tcp_len);
// 16th byte is checksum (may be non-zero), compensate
sum -= tcphdr->check;
return ip_cksum_carry(sum);
}
/* utils.c */
const char *optstring_from_long_options(const struct option *opt);
int setup_server(struct sockaddr_storage *addr);
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <linux/if_tun.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <net/if.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>
#include <netinet/in.h>
#include <getopt.h>
#include <arpa/inet.h>
#include "common.h"
void iface_hwaddr(int sd, const char *if_name, uint8_t *hwaddr)
{
struct ifreq ifreq;
memset(&ifreq, 0, sizeof(ifreq));
strncpy(ifreq.ifr_name, if_name, sizeof(ifreq.ifr_name));
int r = ioctl(sd, SIOCGIFHWADDR, &ifreq);
if (r < 0) {
PFATAL("ioctl(%s, SIOCGIFHWADDR)", if_name);
}
memcpy(hwaddr, ifreq.ifr_hwaddr.sa_data, 6);
}
static void print_usage()
{
printf(
#include "help.txt"
);
}
int main(int argc, char *argv[]) {
char *iface_name = "tap0";
struct sockaddr_in src = {
.sin_family = AF_INET,
.sin_port = htons(1),
.sin_addr = {htonl(INADDR_LOOPBACK)},
};
struct sockaddr_in dst = {
.sin_family = AF_INET,
.sin_port = htons(80),
.sin_addr = {htonl(INADDR_LOOPBACK)},
};
{
static struct option long_options[] = {
{"iface", required_argument, 0, 'i'},
{"src", required_argument, 0, 's'},
{"dst", required_argument, 0, 'd'},
{"help", no_argument, 0, 'h'},
{NULL, 0, 0, 0}};
optind = 1;
while (1) {
int option_index = 0;
int arg = getopt_long(
argc, argv,
optstring_from_long_options(long_options),
long_options, &option_index);
if (arg == -1) {
break;
}
switch (arg) {
default:
case 0:
fprintf(stderr, "Unknown option: %s",
argv[optind]);
exit(-1);
break;
case '?':
exit(-1);
break;
case 'i':
iface_name = optarg;
break;
case 's':
inet_pton(AF_INET, optarg, &src.sin_addr);
break;
case 'd':
inet_pton(AF_INET, optarg, &dst.sin_addr);
break;
case 'h':
print_usage();
exit(0);
break;
}
}
}
{
char *last_arg = argv[optind];
if (last_arg != NULL) {
fprintf(stderr, "[!] unrecognized parameter %s\n", last_arg);
exit(-1);
}
}
int main_fd = open("/dev/net/tun", O_RDWR);
if (main_fd < 0) {
PFATAL("open(\"/dev/net/tun\")");
}
struct ifreq ifr = {
.ifr_flags = IFF_TAP | IFF_NO_PI | IFF_MULTI_QUEUE,
// .ifr_name = iface_name,
};
if (iface_name != NULL) {
strncpy(ifr.ifr_name, iface_name, IFNAMSIZ);
}
int r = ioctl(main_fd, TUNSETIFF, &ifr);
if (r < 0) {
PFATAL("ioctl(%s, TUNSETIFF, IFF_TAP)",
ifr.ifr_name);
}
if (iface_name != NULL) {
r = ioctl(main_fd, TUNSETPERSIST, 1);
if (r < 0) {
PFATAL("ioctl(%s, TUNSETPERSIST)",
ifr.ifr_name);
}
}
/* From the kernel commit:
*
* > For now the accounting is essentially disabled by default
* > for backwards compatibility. In particular, we set the
* > cap to INT_MAX. This is so that existing applications
* > don't get confused by the sudden arrival EAGAIN errors.
*/
int sndbuf = 0;
r = ioctl(main_fd, TUNGETSNDBUF, &sndbuf);
if (r < 0) {
PFATAL("ioctl(%s, TUNGETSNDBUF)", ifr.ifr_name);
}
/* struct iface iface = {}; */
uint8_t hwaddr[6];
iface_hwaddr(main_fd, ifr.ifr_name, hwaddr);
/* strncpy(iface.name, ifr.ifr_name, sizeof(iface.name)); */
/* iface.fd = main_fd; */
/* Sort out global settings */
fprintf(stderr,
"[ ] Opened tap interface %s sndbuf=%i "
"hwaddr=%02x:%02x:%02x:%02x:%02x:%02x\n",
ifr.ifr_name,
sndbuf == INT_MAX ? -1 : sndbuf, hwaddr[0],
hwaddr[1], hwaddr[2], hwaddr[3],
hwaddr[4], hwaddr[5]);
uint8_t packet[2048];
packet[12] = 0x08;
packet[13] = 0x00;
/* "\x00\x00\x00\x00\x00\x01" // smac */
/* "\x00\x00\x00\x00\x00\x01" // dmac */
/* "\x08\x00" // ipv4 */
/* "\x45\x00\x00\x34" */
/* "\x00\x00\x40\x00" */
/* "\x40\x06" */
/* "\x3c\xc2" // checksum */
/* "\x7f\x00\x00\x01" // sip */
/* "\x7f\x00\x00\x01" // dip */
/* "\x00\x01" // sport */
/* "\x00\x51" // dport */
/* "\xde\xad\xbe\xef" // seq */
/* "\xfa\x01\xba\xbe" // ack */
/* "\x50\x02" // hlen + flags */
/* "\x02\x00" // window size */
/* "\x90\x18\x00\x00" // checksum + urg */
/* "\x01\x01\x08\x0a" */
/* "\x53\xc6\xf6\x39\x53\xc6\xf6\x39" */
/* }; */
struct sockaddr_in srv_addr = {
.sin_family = AF_INET,
.sin_port = htons(80),
.sin_addr = {htonl(INADDR_ANY)},
};
int srv_fd = setup_server((struct sockaddr_storage*)&srv_addr);
int i;
for (i=0; i<10; i++) {
struct iphdr *iphdr = (struct iphdr*)&packet[14];
*iphdr = (struct iphdr){
.ihl = 5,
.version = 4,
.tot_len = htons(40+1),
.ttl = 64,
.protocol = IPPROTO_TCP,
.saddr = src.sin_addr.s_addr,
.daddr = dst.sin_addr.s_addr,
};
iphdr->check = compute_ip_checksum(iphdr);
struct tcphdr *tcphdr = (struct tcphdr*)&packet[14+20];
*tcphdr = (struct tcphdr){
.source = htons(ntohs(src.sin_port)+i),
.dest = dst.sin_port,
.seq = htonl(0xdeadbeef),
.ack_seq = htonl(0xbadbabe),
.doff = 5,
.syn = 1,
.window = htons(0x0200),
};
tcphdr->check = compute_tcp_checksum(iphdr, tcphdr);
int packet_len = ntohs(iphdr->tot_len) + 14;
char buf[2048];
memcpy(buf, hwaddr, 6);
memcpy(buf+6, packet+6, packet_len-6);
r = write(main_fd, buf, packet_len);
if (r < 0 && errno == EIO) {
fprintf(stderr, "Did you bring the tap device up?\n");
break;
}
}
if (0){
int flags = fcntl(main_fd, F_GETFL);
if (flags == -1) { PFATAL(""); }
r = fcntl(main_fd, F_SETFL, flags | O_NONBLOCK);
if (r == -1) { PFATAL(""); }
while (1) {
char buf[4096];
r = read(main_fd, buf, sizeof(buf));
if (r<0 || errno == EAGAIN) {
break;
}
if (r < 0) {
PFATAL("read");
}
printf("got packet len=%d\n", r);
}
}
close(main_fd);
close(srv_fd);
return 0;
}
"Usage: flood_tun\n"
"\n"
"--iface INTERFACE Name of interface. Default: tap0\n"
"--src IP ADDRESS Specify source IP address\n"
"--dst IP ADDRESS Specify destination IP address\n"
#include <errno.h>
#include <linux/bpf.h>
#include <linux/tcp.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#include <linux/filter.h>
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
#define PFATAL(x...) \
do { \
fprintf(stderr, "[-] SYSTEM ERROR : " x); \
fprintf(stderr, "\n\tLocation : %s(), %s:%u\n", __FUNCTION__, __FILE__, \
__LINE__); \
perror(" OS message "); \
fprintf(stderr, "\n"); \
exit(EXIT_FAILURE); \
} while (0)
static int net_setup_bpf(int sd)
{
struct sock_filter code[] = {
// ret #0
{0x06, 0, 0, 0x00000000},
};
struct sock_fprog bpf = {
.len = ARRAY_SIZE(code),
.filter = code,
};
int r = setsockopt(sd, SOL_SOCKET, SO_ATTACH_FILTER, &bpf, sizeof(bpf));
if (r < 0) {
PFATAL("setsockopt(SO_ATTACH_FILTER)");
}
return sd;
}
int setup_server(struct sockaddr_storage *addr)
{
int sd = socket(AF_INET, SOCK_STREAM, 0);
if (sd < 0) {
PFATAL("socket()");
}
int r = bind(sd, (struct sockaddr *)addr, sizeof(*addr));
if (r != 0) {
PFATAL("bind()");
}
r = listen(sd, 16);
if (r != 0) {
PFATAL("listen()");
}
socklen_t addr_sz = sizeof(*addr);
r = getsockname(sd, (struct sockaddr *)addr, &addr_sz);
if (r != 0) {
PFATAL("getsockname()");
}
net_setup_bpf(sd);
return sd;
}
#!/bin/bash
set -e
NS=xxx
#gcc -Wall -Wextra flood_tun.c -o flood_tun
ip netns del ${NS} || true
ip netns add ${NS}
ip -n ${NS} link set lo up
ip -n ${NS} tuntap add mode tap multi_queue name tap0
ip -n ${NS} link set dev tap0 up
#ip -n ${NS} -s link
ip -n ${NS} route add 10.111.112.0/24 dev tap0
ip -n ${NS} neigh add 10.111.112.1 lladdr 70:71:aa:4b:29:aa dev tap0
ip -n ${NS} route add 192.0.2.0/24 via 10.111.112.1 dev tap0
ip -n ${NS} addr add 10.111.112.2 dev tap0
# echo 1 | ip netns exec ${NS} tee /proc/sys/net/ipv4/ip_forward
# echo 1 | ip netns exec ${NS} tee /proc/sys/net/ipv4/conf/all/forwarding
ip netns exec ${NS} iptables -t raw -A PREROUTING -i tap+ -j CT
ip netns exec ${NS} iptables -t mangle -A PREROUTING -i tap+
CTMAX=`cat /proc/sys/net/netfilter/nf_conntrack_max`
echo 4 | ip netns exec ${NS} tee /proc/sys/net/netfilter/nf_conntrack_max
ip netns exec ${NS} iptables -A INPUT -m conntrack --ctstate INVALID
ip netns exec ${NS} iptables -A INPUT -m conntrack --ctstate NEW
ip netns exec ${NS} iptables -A INPUT -m conntrack --ctstate ESTABLISHED
ip netns exec ${NS} iptables -A INPUT -m conntrack --ctstate RELATED
ip netns exec ${NS} iptables -A INPUT -m conntrack --ctstate UNTRACKED
ip netns exec ${NS} iptables -A INPUT
#ip netns exec ${NS} sysctl -w net.ipv4.conf.all.rp_filter=0
#ip netns exec ${NS} sysctl -w net.ipv4.conf.tap0.rp_filter=0
#ip netns exec ${NS} sysctl -w net.ipv4.conf.tap0.accept_local=1
#ip netns exec ${NS} sysctl -w net.ipv4.conf.tap0.route_localnet=1
ip netns exec ${NS} tcpdump -ni tap0 -B 16384 -ttt &
TCPDUMP_PID=$!
function finish_tcpdump {
kill ${TCPDUMP_PID}
ip netns del ${NS}
echo ${CTMAX} | tee /proc/sys/net/netfilter/nf_conntrack_max
echo
}
trap finish_tcpdump EXIT
sleep 0.3
ip netns exec ${NS} ./flood_tun --src 192.0.2.8 --dst 10.111.112.2
ip netns exec ${NS} conntrack -L
ip netns exec ${NS} iptables -t raw -L PREROUTING -v -x
ip netns exec ${NS} iptables -t mangle -L PREROUTING -v -x
#ip netns exec ${NS} iptables -L -v -x
#ip netns exec ${NS} cat /proc/net/ip_conntrack
# ip netns exec ${NS} iptables -t raw -L -v -x
# ip netns exec ${NS} iptables -t filter -L -v -x
# ip netns exec ${NS} iptables -t mangle -L -v -x
# ip netns exec ${NS} ss -n -a -t
# ip netns exec ${NS} cat /proc/sys/net/netfilter/nf_conntrack_max
#ip netns exec ${NS} bash
#ip -n ${NS} -s link
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <unistd.h>
const char *optstring_from_long_options(const struct option *opt)
{
static char optstring[256] = {0};
char *osp = optstring;
for (; opt->name != NULL; opt++) {
if (opt->flag == 0 && opt->val > 0 && opt->val < 256) {
*osp++ = opt->val;
switch (opt->has_arg) {
case optional_argument:
*osp++ = ':';
*osp++ = ':';
break;
case required_argument:
*osp++ = ':';
break;
}
}
}
*osp++ = '\0';
if (osp - optstring >= (int)sizeof(optstring)) {
abort();
}
return optstring;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment