Tun 장치에서 이더넷으로 트래픽 라우팅

Tun 장치에서 이더넷으로 트래픽 라우팅

두 개의 Docker 컨테이너가 실행 중입니다(Ubuntu 22.04). 그들은 동일한 네트워크에 있으므로 서로 대화할 수 있습니다(저는 이것을 로 확인했습니다 nc). 첫 번째 컨테이너에서는 tun 장치를 생성하고 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(다른 컨테이너)인 tun 장치에 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가 tun 장치의 소스 IP와 동일하기 때문에 패킷이 의심스러워 보인다는 것입니다. 주소가 192.168.0.1인 물리적 인터페이스가 있고 해당 인터페이스에서 소스 IP가 192.168.0.1인 패킷을 수신한다고 가정해 보겠습니다. 왜 자신에게 메시지를 보내는지 궁금할 것입니다.

그래서 패킷의 소스 IP를 5.6.7.9(tun 장치의 주소는 그대로 유지)로 조정한 다음! ICMP 응답을 받았습니다!

관련 정보