AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / unix / 问题 / 759844
Accepted
Daniel Walker
Daniel Walker
Asked: 2023-10-26 04:19:56 +0800 CST2023-10-26 04:19:56 +0800 CST 2023-10-26 04:19:56 +0800 CST

将流量从 tun 设备路由到以太网

  • 772

我有两个正在运行的 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

然后,我的程序将 TCP SYN 数据包写入 tun 设备,源 IP 为 5.6.7.8,目标 IP 为 192.168.48.3(另一个容器)。然而,数据包永远不会到达另一个容器。我已经运行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

以下是重新创建我正在做的事情的方法:

Dockerfile:

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`
network-interface
  • 1 1 个回答
  • 73 Views

1 个回答

  • Voted
  1. Best Answer
    Daniel Walker
    2023-10-27T05:10:27+08:002023-10-27T05:10:27+08:00

    通过pwru,我能够看出该数据包fib_validate_source在内核中被视为火星数据包而被拒绝。

    我收集到的情况是,由于数据包的源 IP 与 tun 设备的源 IP 相同,因此该数据包看起来很可疑。想象一下,如果您有一个地址为 192.168.0.1 的物理接口,并且您在该接口上收到了一个源 IP 为 192.168.0.1 的数据包。您可能想知道为什么要给自己发送消息。

    因此,我将数据包的源 IP 调整为 5.6.7.9(保留 tun 设备的地址),然后!已收到 ICMP 回复!

    • 0

相关问题

  • 如何通过以太网直接与 IP 摄像机通信(无交换机、集线器、路由器......)。相机和以太网端口中的不同子网

  • 将任何以太网接口与 systemd.networkd 匹配

  • 在 CentOS 7 中获得意外的接口

  • 克服 ifconfig Ubuntu16.04 和 Ubuntu17.01 的格式差异

  • 通过静态 IP(NAT?)访问 VM

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    模块 i915 可能缺少固件 /lib/firmware/i915/*

    • 3 个回答
  • Marko Smith

    无法获取 jessie backports 存储库

    • 4 个回答
  • Marko Smith

    如何将 GPG 私钥和公钥导出到文件

    • 4 个回答
  • Marko Smith

    我们如何运行存储在变量中的命令?

    • 5 个回答
  • Marko Smith

    如何配置 systemd-resolved 和 systemd-networkd 以使用本地 DNS 服务器来解析本地域和远程 DNS 服务器来解析远程域?

    • 3 个回答
  • Marko Smith

    dist-upgrade 后 Kali Linux 中的 apt-get update 错误 [重复]

    • 2 个回答
  • Marko Smith

    如何从 systemctl 服务日志中查看最新的 x 行

    • 5 个回答
  • Marko Smith

    Nano - 跳转到文件末尾

    • 8 个回答
  • Marko Smith

    grub 错误:你需要先加载内核

    • 4 个回答
  • Marko Smith

    如何下载软件包而不是使用 apt-get 命令安装它?

    • 7 个回答
  • Martin Hope
    user12345 无法获取 jessie backports 存储库 2019-03-27 04:39:28 +0800 CST
  • Martin Hope
    Carl 为什么大多数 systemd 示例都包含 WantedBy=multi-user.target? 2019-03-15 11:49:25 +0800 CST
  • Martin Hope
    rocky 如何将 GPG 私钥和公钥导出到文件 2018-11-16 05:36:15 +0800 CST
  • Martin Hope
    Evan Carroll systemctl 状态显示:“状态:降级” 2018-06-03 18:48:17 +0800 CST
  • Martin Hope
    Tim 我们如何运行存储在变量中的命令? 2018-05-21 04:46:29 +0800 CST
  • Martin Hope
    Ankur S 为什么 /dev/null 是一个文件?为什么它的功能不作为一个简单的程序来实现? 2018-04-17 07:28:04 +0800 CST
  • Martin Hope
    user3191334 如何从 systemctl 服务日志中查看最新的 x 行 2018-02-07 00:14:16 +0800 CST
  • Martin Hope
    Marko Pacak Nano - 跳转到文件末尾 2018-02-01 01:53:03 +0800 CST
  • Martin Hope
    Kidburla 为什么真假这么大? 2018-01-26 12:14:47 +0800 CST
  • Martin Hope
    Christos Baziotis 在一个巨大的(70GB)、一行、文本文件中替换字符串 2017-12-30 06:58:33 +0800 CST

热门标签

linux bash debian shell-script text-processing ubuntu centos shell awk ssh

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve