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 / 问题 / 793502
Accepted
Richard W
Richard W
Asked: 2025-04-09 14:49:17 +0800 CST2025-04-09 14:49:17 +0800 CST 2025-04-09 14:49:17 +0800 CST

我无法在 tap 接口上接收任何数据包

  • 772

我想测试一个在用户空间运行完整 IPv6 协议栈的应用程序。该应用程序应该处理并发送以太网帧。为此,我想在 Linux 上设置一个网络接口,以便我可以从本地计算机访问它,就像它是网络中的另一个参与者一样。我了解到应该使用 TAP 设备来实现这一点。因此,我创建了一个脚本来设置接口和测试应用程序。

这是我设置界面的脚本:

#!/bin/bash

MAC_ADDR="00:11:22:33:44:55"
IP_ADDR="2001:db8::2"

setup() {
    # Create the TAP interface (if not already created)
    ip tuntap add dev tap0 mode tap

    # Assign an IPv6 address to the TAP interface
    ip -6 addr add "${IP_ADDR}/64" dev tap0

    # Bring the TAP interface up
    ip link set dev tap0 up

    # Set a custom MAC address for the TAP interface
    # (Replace with your desired MAC address)
    ip link set dev tap0 address $MAC_ADDR

    # Disable kernel IPv6 handling on this interface
    sysctl -w net.ipv6.conf.tap0.forwarding=0
    sysctl -w net.ipv6.conf.tap0.accept_ra=0

    # Print interface details to verify the MAC address and IP
    ip addr show dev tap0
}

cleanup() {
    ip link set tap0 down || true
    ip tuntap del dev tap0 mode tap
}

# Setup tap interface
setup

# Cleanup when exiting
trap cleanup SIGINT

# Keep the interface up indefinitely
echo "Press Ctrl+C to bring down the interface"
sleep infinity

这是我的测试应用程序:

// TunInterface.h
#pragma once

#include <string>
#include <functional>
#include <sys/socket.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <fcntl.h>
#include <cstring>
#include <iostream>
#include <netinet/if_ether.h>
#include <netinet/ip.h>

class TunInterface {
public:
    TunInterface(const std::string& dev_name,
                 std::function<void(const unsigned char*, size_t)> recv_callback,
                 const unsigned char* mac_address = nullptr)
        : dev_name(dev_name), receive_callback(recv_callback), mac_filter(mac_address) {
        // Open the TUN device
        tun_fd = open("/dev/net/tun", O_RDWR);
        if (tun_fd < 0) {
            std::cerr << "Failed to open TUN device" << std::endl;
            exit(1);
        }

        struct ifreq ifr;
        std::memset(&ifr, 0, sizeof(ifr));
        std::strncpy(ifr.ifr_name, dev_name.c_str(), IFNAMSIZ);

               // Set up the TUN interface
        if (ioctl(tun_fd, TUNSETIFF, &ifr) < 0) {
            std::cerr << "Failed to configure TUN interface" << std::endl;
            close(tun_fd);
            exit(1);
        }

        std::cout << "TUN interface " << dev_name << " configured successfully." << std::endl;
    }

    ~TunInterface() {
        close(tun_fd);
    }

           // Function to start receiving and calling the callback on incoming packets
    void receive() {
        unsigned char buffer[2048];  // Adjust the buffer size as needed
        while (true) {
            ssize_t len = read(tun_fd, buffer, sizeof(buffer));
            if (len < 0) {
                std::cerr << "Error reading from TUN interface" << std::endl;
                continue;
            }

                   // Call the user-defined callback
            if (mac_filter && !is_mac_filtered(buffer)) {
                // If the MAC address does not match the filter, skip the packet
                continue;
            }

            receive_callback(buffer, len);
        }
    }

           // Function to send a packet
    void send(const unsigned char* data, size_t len) {
        ssize_t bytes_written = write(tun_fd, data, len);
        if (bytes_written < 0) {
            std::cerr << "Error writing to TUN interface" << std::endl;
        }
    }

private:
    int tun_fd;
    std::string dev_name;
    std::function<void(const unsigned char*, size_t)> receive_callback;
    const unsigned char* mac_filter;  // If nullptr, no filtering is done

    bool is_mac_filtered(const unsigned char* packet) {
        if (mac_filter) {
            struct ethhdr* eth_header = reinterpret_cast<struct ethhdr*>(const_cast<unsigned char*>(packet));
            return std::memcmp(eth_header->h_dest, mac_filter, ETH_ALEN) == 0;
        }
        return true;  // No filter if mac_filter is nullptr
    }
};

#endif

#include "TapInterface.h"
#include <iostream>

int main() {
    // Define the MAC address for filtering (e.g., 00:11:22:33:44:55)
    uint8_t macAddr[6] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 };

           // Create a TapInterface with MAC filtering enabled
    TapInterface tap("tap0", macAddr);

    if (!tap.isValid()) {
        std::cerr << "Failed to create TAP interface\n";
        return 1;
    }

           // Register a callback to handle received frames
    tap.registerReceiveCallback([](const uint8_t* data, size_t len) {
        std::cout << "Received frame of length " << len << " bytes\n";
    });

    std::cout << "Listening on tap0. Press Ctrl-C to exit.\n";
    while (true) pause(); // Or use any other suitable loop
}

我运行时,ping6 2001:db8::2期望在测试应用程序中至少看到邻居发现以太网帧。但我什么也没看到。相反,我一启动测试应用程序就收到了ping6尝试的回复。看起来内核仍然在干扰。我尝试了类似的方法,使用tun设备,也成功了,但在第2层不起作用。我使用的是Ubuntu 24.04系统。

可能出了什么问题?谢谢。

linux
  • 1 1 个回答
  • 19 Views

1 个回答

  • Voted
  1. Best Answer
    grawity
    2025-04-09T23:57:03+08:002025-04-09T23:57:03+08:00

    当我运行时,ping6 2001:db8::2我希望在我的测试应用程序中至少看到邻居发现以太网帧。

    2001:db8::2是您自己的本地地址。发送到机器本地地址(即直接分配给接口的地址)的数据包永远不会通过该接口发送,因为接口不会将这些数据包“环回”给您——这意味着即使主机发送了邻居发现查询,它也永远不会看到它自己的查询;并且它永远不会“接收”任何它本应发送到自身 MAC 地址的帧。相反,所有此类数据包都会被隐式路由通过lo(即使“本地”路由表没有显示这一点)。

    从 /64 ping 一些其他地址,您应该会看到数据包。

    您的应用程序与 Linux 主机操作系统不是同一个网络实体 - 它作为一个单独的网络实体存在,可以说是在“电缆的另一端” - 因此它应该具有与主机不同的 IP 地址,以及不同的 MAC 地址。

    • 0

相关问题

  • 有没有办法让 ls 只显示某些目录的隐藏文件?

  • 使用键盘快捷键启动/停止 systemd 服务 [关闭]

  • 需要一些系统调用

  • astyle 不会更改源文件格式

  • 通过标签将根文件系统传递给linux内核

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