Tunデバイスからイーサネットへのトラフィックルーティング

Tunデバイスからイーサネットへのトラフィックルーティング

2つのDockerコンテナが実行されています(Ubuntu 22.04)。彼らは同じネットワーク上にあるので、互いに会話することができます(私はこれをで確認しましたnc)。最初のコンテナでは、トンデバイスを作成し、IPv4アドレスを割り当ててから起動するプログラムを実行しました。 ip addrプログラム

4: tun0: <POINTOPOINT,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UNKNOWN group default qlen 500
    link/none 
    inet 5.6.7.8/32 scope global tun0
       valid_lft forever preferred_lft forever
89: eth0@if90: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:c0:a8:30:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 192.168.48.2/20 brd 192.168.63.255 scope global eth0
       valid_lft forever preferred_lft forever

その後、私のプログラムは、送信元IPが5.6.7.8で宛先IPが192.168.48.3(別のコンテナ)のトンデバイスにTCP SYNパケットを書き込みます。ただし、パケットは他のコンテナに到達しません。これを実行しましたが、tcpdumpパケットがtun0インターフェイスに表示されますが、他のコンテナに到着したeth0パケット tcpdumpは表示されません。

パケットが経由してルーティングされないのはなぜですかeth0

コンテナを起動するdocker-compose

sysctls:
  - net.ipv4.ip_forward=1

最初のコンテナの場合。

NATも設定しました。

iptables -I FORWARD -i tun0 -o eth0 -j ACCEPT
iptables -I FORWARD -i eth0 -o tun0 -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -t nat -I POSTROUTING -o eth0 -j MASQUERADE

私がやっていることを再現する方法は次のとおりです。

ドッカーファイル:

FROM ubuntu:22.04

RUN apt -y update && \
    apt -y install build-essential iptables tcpdump

WORKDIR /app 

打ち上げ.c:

#include <arpa/inet.h>
#include <fcntl.h>
#include <linux/if_tun.h>
#include <net/if.h>
#include <netinet/in.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>

#define TUN_DEVICE "tun0"

#define SOURCE_IP "5.6.7.8"
#define DESTINATION_IP "192.168.48.3" // change to the actual address

unsigned char packet[] = {
    0x45, 0x00, 0x00, 0x54, 0x3a, 0x58, 0x40, 0x00, 0x40, 0x01, 0x03, 0x9d,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x48, 0xc1,
    0x00, 0x03, 0x00, 0x01, 0xa1, 0x81, 0x3a, 0x65, 0x00, 0x00, 0x00, 0x00,
    0x0d, 0x81, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x11, 0x12, 0x13,
    0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
    0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37
};

/*
Note: The IPv4 checksum will be wrong.  Run this program once and cause it to emit
the packet by sending the process SIGUSR1.  Capture the packet with tcpdump which
will tell you that the checksum is wrong and what it should be.  The result goes
at offset 10 in the packet.
*/

int tun_fd = -1;

void signal_handler(int signum)
{
    (void)signum;

    dprintf(STDOUT_FILENO, "Emitting packet\n");

    if ( write(tun_fd, packet, sizeof(packet)) < 0 ) {
        _exit(1);
    }
}


int set_address_and_bring_up(void)
{
    int ret = -1, sock;
    struct ifreq ifr = {.ifr_name = TUN_DEVICE};
    struct sockaddr_in addr = {.sin_family = AF_INET};

    inet_pton(AF_INET, SOURCE_IP, &addr.sin_addr);
    memcpy(&ifr.ifr_addr, &addr, sizeof(addr));

    sock = socket(AF_INET, SOCK_DGRAM, 0);
    if ( sock < 0 ) {
        perror("socket");
        return -1;
    }

    if ( ioctl(sock, SIOCSIFADDR, &ifr) == -1 ) {
        perror("ioctl (SIOCSIFADDR)");
        goto done;
    }

    ifr.ifr_flags = IFF_UP;
    if ( ioctl(sock, SIOCSIFFLAGS, &ifr) == -1 ) {
        perror("ioctl (SIOCSIFFLAGS)");
        goto done;
    }

    ret = 0;

done:
    close(sock);
    return ret;
}

int tun_device_create(void) {
    int fd;
    struct ifreq ifr = {.ifr_name = TUN_DEVICE, .ifr_flags = IFF_TUN | IFF_NO_PI};

    fd = open("/dev/net/tun", O_RDWR);
    if ( fd < 0 ) {
        perror("open");
        return -1;
    }

    if ( ioctl(fd, TUNSETIFF, &ifr) == -1 ) {
        perror("ioctl (TUNSETIFF)");
        goto error;
    }

    if ( set_address_and_bring_up() != 0 ) {
        goto error;
    }

    return fd;

error:
    close(fd);
    return -1;
}

int main() {
    struct sigaction action = {.sa_handler = signal_handler};

    inet_pton(AF_INET, SOURCE_IP, packet + 12);
    inet_pton(AF_INET, DESTINATION_IP, packet + 16);

    tun_fd = tun_device_create();
    if ( tun_fd < 0 ) {
        return 1;
    }

    sigaction(SIGUSR1, &action, NULL);

    while (1) {
        pause();
    }

    return 0;
}

iptables_script.sh:

#!/bin/sh -ex

iptables -I FORWARD -i tun0 -o eth0 -j ACCEPT
iptables -I FORWARD -i eth0 -o tun0 -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -t nat -I POSTROUTING -o eth0 -j MASQUERADE

コンテナを起動します。

# First container
docker run --rm -it --cap-add NET_ADMIN -v $PWD:/app --device=/dev/net/tun my_image bash

# Second container
docker run --rm -it ubuntu:22.04 bash

最初のコンテナで次を実行します。

test "`cat /proc/sys/net/ipv4/ip_forward`" -eq 1
gcc emit.c
./a.out &
./iptables_script.sh
kill -s USR1 `pgrep a.out`

ベストアンサー1

渡すフワル、パケットが拒否されたことがわかりました。fib_validate_sourceカーネルでは、火星パケット

私が収集したのは、送信元IPがトンデバイスの送信元IPと同じであるため、パケットが疑わしいようです。アドレスが192.168.0.1の物理インターフェイスがあり、そのインターフェイスから送信元IPが192.168.0.1のパケットを受信するとします。なぜ自分にメッセージを送るのか疑問に思うでしょう。

そのため、パケットのソースIPを5.6.7.9(tunデバイスのアドレスはそのまま維持)に調整してから! ICMPレスポンスを頂きました!

おすすめ記事