我想测试一个在用户空间运行完整 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系统。
可能出了什么问题?谢谢。