Linux系统多链路互备份网络搭建方案

基于 Linux 系统的多链路互备份网络方案, 两条链路同时发送数据,并且通过 TUN(网络隧道)设备实现,以下是一个完整的设计和实现思路:


1. 方案概述

核心功能
  1. 使用 TUN 虚拟网络设备,作为逻辑网络接口。
  2. 通过两条物理链路发送数据(如 eth0eth1),实现多路径传输。
  3. 使用 C 语言实现,将同一数据包通过两条链路发送。
  4. 在接收端根据数据标识去重。
工作流程
  • 发送端

    1. 应用程序将数据发送到 TUN 设备。
    2. TUN 设备捕获数据,交给 C 程序处理。
    3. C 程序将数据通过两条链路发送到接收端。
  • 接收端

    1. 两条链路接收到相同的数据。
    2. 数据在逻辑层聚合,并通过标识去重。

2. 环境准备

安装 TUN/TAP 支持

确保 Linux 系统支持 TUN/TAP:

sudo modprobe tun

检查是否加载成功:

ls /dev/net/tun

如果未加载,创建设备:

sudo mknod /dev/net/tun c 10 200
sudo chmod 0666 /dev/net/tun
安装必要的工具

开发环境需要:

sudo apt-get install build-essential libpcap-dev

3. 实现

3.1 客户端(发送端)

AppTun2Cli.c 代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <linux/if_tun.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <stdbool.h>
#include <sys/time.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>


// 定义常量
#define TUN_DEVICE "tun0"
#define BUFSIZE 2000
#define DEST_SERVER_IP "70.11.61.112"
#define SERVER_IP "70.11.61.222"
#define SERVER_PORT 55500
#define GATEWAY1 "192.168.0.1"
#define GATEWAY2 "192.168.10.1"
#define LOCAL_IP1 "192.168.0.194"
#define LOCAL_IP2 "192.168.10.100"
#define LOCAL_NET1 "192.168.0.0/24"
#define LOCAL_NET2 "192.168.10.0/24"
#define INTERFACE1 "eth0"
#define INTERFACE2 "enx000ec6374317"
#define VIRTUAL_IP "192.168.100.100/24"
#define VIRTUAL_GW "192.168.100.1"
#define PKG_TIMEOUT 5000 //数据包接收超时(毫秒)

// 全局变量
int tun_fd;
int udp_sock1, udp_sock2, netlink_sock;
struct sockaddr_in addr1, addr2, server_addr;
unsigned short packet_number = 0;
unsigned char if1_connected = 0,if2_connected = 0;

long long get_relative_time()
{
    struct timeval tv;
    struct timezone tz;

    gettimeofday(&tv,&tz);
    return tv.tv_sec * 1000 + tv.tv_usec / 1000;
}

int is_network_cable_connected(int sockfd,const char *interface_name) {

    struct ifreq ifr;

    // 将接口名称复制到 ifreq 结构
    strncpy(ifr.ifr_name, interface_name, IFNAMSIZ - 1);
    ifr.ifr_name[IFNAMSIZ - 1] = '\0';

    // 获取接口标志
    if (ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0) {
        perror("ioctl failed");
        return -1;
    }

    // 检查 IFF_RUNNING 标志
    if (ifr.ifr_flags & IFF_RUNNING) {
        return 1;  // 网线已连接
    } else {
        return 0;  // 网线未连接
    }
}

// 函数:创建和配置 TUN 设备
int create_tun_device() {
    struct ifreq ifr;
    int fd = open("/dev/net/tun", O_RDWR);
    if (fd < 0) {
        perror("Opening /dev/net/tun");
        return fd;
    }

    memset(&ifr, 0, sizeof(ifr));
    ifr.ifr_flags = IFF_TUN | IFF_NO_PI;  // 使用 TUN 模式,无附加头
    strncpy(ifr.ifr_name, TUN_DEVICE, IFNAMSIZ);

    if (ioctl(fd, TUNSETIFF, &ifr) < 0) {
        perror("ioctl(TUNSETIFF)");
        close(fd);
        return -1;
    }

    return fd;
}

// 线程函数:从 TUN 设备读取数据并通过 UDP 发送
void *tun_to_udp(void *arg) {
    char buffer[BUFSIZE];
    //unsigned char if1_connected = 0,if2_connected = 0;
    while (1) {
        int n = read(tun_fd, buffer + 2, BUFSIZE - 2);  // 读取 IP 数据包
        if (n < 0) {
            perror("Reading from TUN device");
            continue;
        }

        // 在数据包前加上编号
        packet_number++;
        
        buffer[0] = (packet_number >> 8) & 0xFF;
        buffer[1] = packet_number & 0xFF;

        
        // 发送到第一个网关
        if(if1_connected)
            sendto(udp_sock1, buffer, n + 2, 0, (struct sockaddr *)&server_addr, sizeof(server_addr));

        
        // 发送到第二个网关
        if(if2_connected)
            sendto(udp_sock2, buffer, n + 2, 0, (struct sockaddr *)&server_addr, sizeof(server_addr));
    }
}

// 线程函数:接收 UDP 数据并写回 TUN 设备
void *udp_to_tun(void *arg) {
    unsigned char buffer[BUFSIZE];
    unsigned char received_numbers[65536] = {0};  // 用于记录已接收的编号
    unsigned char received_numbers_flags[2] = {0};
    unsigned short number = 0;
    long long last_recv_time = 0,cur_recv_time = 0;

    fd_set read_fds;
    int max_fd = (udp_sock1 > udp_sock2) ? udp_sock1 : udp_sock2;

    while (1) {
        FD_ZERO(&read_fds);
        FD_SET(udp_sock1, &read_fds);
        FD_SET(udp_sock2, &read_fds);

        // 使用 select() 等待任意一个套接字有数据可读
        int ret = select(max_fd + 1, &read_fds, NULL, NULL, NULL);
        if (ret < 0) {
            perror("select");
            continue;
        }

        cur_recv_time = get_relative_time();
        if(cur_recv_time - last_recv_time > PKG_TIMEOUT)
        {
            if(received_numbers_flags[0])
            {
                memset(received_numbers,0,0x8000);
                received_numbers_flags[0] = 0;
            }

            if(received_numbers_flags[1])
            {
                memset(received_numbers + 0x8000,0,0x8000);
                received_numbers_flags[1] = 0;
            }
        }
        last_recv_time = cur_recv_time;

        // 检查 udp_sock1 是否有数据
        if (FD_ISSET(udp_sock1, &read_fds)) {
            struct sockaddr_in from_addr;
            socklen_t addrlen = sizeof(from_addr);
            int n = recvfrom(udp_sock1, buffer, BUFSIZE, 0, (struct sockaddr *)&from_addr, &addrlen);
            if (n >= 2) {
                // 处理接收到的数据包
                number = (unsigned short)(buffer[0] << 8) | buffer[1];
                //printf("num:%d,cur:%d",number,cur_num);
                if (!received_numbers[number]) {
                    received_numbers[number] = 1;
                    write(tun_fd, buffer + 2, n - 2);  // 将数据写入 TUN 设备
                }

            }
        }

        // 检查 udp_sock2 是否有数据
        if (FD_ISSET(udp_sock2, &read_fds)) {
            struct sockaddr_in from_addr;
            socklen_t addrlen = sizeof(from_addr);
            int n = recvfrom(udp_sock2, buffer, BUFSIZE, 0, (struct sockaddr *)&from_addr, &addrlen);
            if (n >= 2) {
                // 处理接收到的数据包
                number = (unsigned short)(buffer[0] << 8) | buffer[1];
                //printf("num:%d,cur:%d",number,cur_num);
                if (!received_numbers[number]) {
                    received_numbers[number] = 1;
                    write(tun_fd, buffer + 2, n - 2);  // 将数据写入 TUN 设备
                }
            }
        }

        if (number < 0x8000)
        {
            received_numbers_flags[0] = 1;
            if(received_numbers_flags[1])
            {
                memset(received_numbers + 0x8000,0,0x8000);
                received_numbers_flags[1] = 0;
            }
        }
        else
        {
            received_numbers_flags[1] = 1;
            if(received_numbers_flags[0])
            {
                memset(received_numbers,0,0x8000);
                received_numbers_flags[0] = 0;
            }
        }
    }
}

void *netlink_detect(void *arg)
{
    char buffer[8192];
    while (1) {
        int len = recv(netlink_sock, buffer, sizeof(buffer), 0);
        if (len < 0) {
            perror("recv failed");
            close(netlink_sock);
            continue;
        }

        // 解析 netlink 消息
        struct nlmsghdr *nlh = (struct nlmsghdr *)buffer;
        while (NLMSG_OK(nlh, len)) {
            struct ifinfomsg *ifi = (struct ifinfomsg *)NLMSG_DATA(nlh);

            // 获取接口索引并检查是否匹配指定接口
            if (if_indextoname(ifi->ifi_index, (char[IFNAMSIZ]){0})) {
                char iface_name[IFNAMSIZ];
                if_indextoname(ifi->ifi_index, iface_name);
                if (strcmp(iface_name, INTERFACE1) == 0) {
                    if (nlh->nlmsg_type == RTM_NEWLINK) {
                        if (ifi->ifi_flags & IFF_RUNNING) {
                            char buf[100] = {0};
                            system("ip route flush table 1");
                            sprintf(buf,"ip route add default via %s dev %s table 1",GATEWAY1,INTERFACE1);
                            system(buf);
                            memset(buf,0,100);
                            sprintf(buf,"ip route add %s dev %s table 1",LOCAL_NET1,INTERFACE1);
                            system(buf);
                            memset(buf,0,100);
                            sprintf(buf,"ip rule add from %s table 1",LOCAL_IP1);
                            system(buf);
                            if1_connected = 1;
                            printf("Network interface %s is connected\n", INTERFACE1);
                        } else {
                            if1_connected = 0;
                            printf("Network interface %s is disconnected\n", INTERFACE1);
                        }
                    }
                }

                if (strcmp(iface_name, INTERFACE2) == 0) {
                    if (nlh->nlmsg_type == RTM_NEWLINK) {
                        if (ifi->ifi_flags & IFF_RUNNING) {
                            char buf[100] = {0};
                            system("ip route flush table 2");
                            sprintf(buf,"ip route add default via %s dev %s table 2",GATEWAY2,INTERFACE2);
                            system(buf);
                            memset(buf,0,100);
                            sprintf(buf,"ip route add %s dev %s table 2",LOCAL_NET2,INTERFACE2);
                            system(buf);
                            memset(buf,0,100);
                            sprintf(buf,"ip rule add from %s table 2",LOCAL_IP2);
                            system(buf);
                            if2_connected = 1;
                            printf("Network interface %s is connected\n", INTERFACE2);
                        } else {
                            if2_connected = 0;
                            printf("Network interface %s is disconnected\n", INTERFACE2);
                        }
                    }
                }
            }

            nlh = NLMSG_NEXT(nlh, len);
        }
    }
}

void write_file(const char* file_name,unsigned char* data,unsigned int offset,unsigned int len)
{
    int fd = open(file_name, O_WRONLY | O_CREAT | O_TRUNC, 0644);
    write(fd, data + offset,len);
    fsync(fd);
    close(fd);
}

int main() {
    // 创建和配置 TUN 设备
    tun_fd = create_tun_device();
    if (tun_fd < 0) {
        exit(1);
    }

    // 创建第一个 UDP 套接字
    udp_sock1 = socket(AF_INET, SOCK_DGRAM, 0);
    if (udp_sock1 < 0) {
        perror("Creating UDP socket1");
        exit(1);
    }
    
    //设置Socket为非阻塞模式
    int flags = fcntl(udp_sock1, F_GETFL, 0);
    fcntl(udp_sock1, F_SETFL, flags | O_NONBLOCK);

    // 绑定到第一个本地 IP 地址
    struct sockaddr_in local_addr1;
    memset(&local_addr1, 0, sizeof(local_addr1));
    local_addr1.sin_family = AF_INET;
    local_addr1.sin_port = htons(SERVER_PORT);
    inet_pton(AF_INET, LOCAL_IP1, &local_addr1.sin_addr);
    if (bind(udp_sock1, (struct sockaddr *)&local_addr1, sizeof(local_addr1)) < 0) {
        perror("Binding UDP socket1");
        exit(1);
    }

    // 创建第二个 UDP 套接字
    udp_sock2 = socket(AF_INET, SOCK_DGRAM, 0);
    if (udp_sock2 < 0) {
        perror("Creating UDP socket2");
        exit(1);
    }

    //设置Socket为非阻塞模式
    flags = fcntl(udp_sock2, F_GETFL, 0);
    fcntl(udp_sock2, F_SETFL, flags | O_NONBLOCK);

    // 绑定到第二个本地 IP 地址
    struct sockaddr_in local_addr2;
    memset(&local_addr2, 0, sizeof(local_addr2));
    local_addr2.sin_family = AF_INET;
    local_addr2.sin_port = htons(SERVER_PORT);
    inet_pton(AF_INET, LOCAL_IP2, &local_addr2.sin_addr);
    if (bind(udp_sock2, (struct sockaddr *)&local_addr2, sizeof(local_addr2)) < 0) {
        perror("Binding UDP socket2");
        exit(1);
    }

    // 设置目标服务器地址
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(SERVER_PORT);
    inet_pton(AF_INET, SERVER_IP, &server_addr.sin_addr);

    struct sockaddr_nl netlink_addr;
    netlink_sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
    if (netlink_sock < 0) {
        perror("net link socket creation failed");
        exit(1);
    }
    // 初始化 sockaddr_nl 结构
    memset(&netlink_addr, 0, sizeof(netlink_addr));
    netlink_addr.nl_family = AF_NETLINK;
    netlink_addr.nl_groups = RTMGRP_LINK;  // 监听网络接口的状态变化
    // 绑定套接字
    if (bind(netlink_sock, (struct sockaddr *)&netlink_addr, sizeof(netlink_addr)) < 0) {
        perror("netlink_sock bind failed");
        close(netlink_sock);
        exit(1);
    }

    // 创建线程
    pthread_t thread1, thread2, thread3;
    pthread_create(&thread1, NULL, tun_to_udp, NULL);
    pthread_create(&thread2, NULL, udp_to_tun, NULL);
    pthread_create(&thread3, NULL, netlink_detect, NULL);

    // 配置tun网卡IP地址
    //ip addr add 192.168.100.100/24 dev tun0
    //ip link set tun0 up
    //iptables -t nat -A POSTROUTING -o tun0 -j MASQUERADE
    {
        char buf[100] = {0};
        sprintf(buf,"ip addr add %s dev %s",VIRTUAL_IP,TUN_DEVICE);
        system(buf);
        memset(buf,0,100);
        sprintf(buf,"ip link set %s up",TUN_DEVICE);
        system(buf);
        memset(buf,0,100);
        sprintf(buf,"iptables -t nat -A POSTROUTING -o %s -j MASQUERADE",TUN_DEVICE);
        system(buf);

        memset(buf,0,100);
        sprintf(buf,"route add -host %s gw %s",DEST_SERVER_IP,VIRTUAL_GW);
        system(buf);
    }

    int ret = is_network_cable_connected(udp_sock1,INTERFACE1);
    if(ret == 1)
    {
        char buf[100] = {0};
        system("ip route flush table 1");
        sprintf(buf,"ip route add default via %s dev %s table 1",GATEWAY1,INTERFACE1);
        system(buf);
        memset(buf,0,100);
        sprintf(buf,"ip route add %s dev %s table 1",LOCAL_NET1,INTERFACE1);
        system(buf);
        memset(buf,0,100);
        sprintf(buf,"ip rule add from %s table 1",LOCAL_IP1);
        system(buf);
        if1_connected = 1;
    }

    ret = is_network_cable_connected(udp_sock2,INTERFACE2);
    if(ret == 1)
    {
        char buf[100] = {0};
        system("ip route flush table 2");
        sprintf(buf,"ip route add default via %s dev %s table 2",GATEWAY2,INTERFACE2);
        system(buf);
        memset(buf,0,100);
        sprintf(buf,"ip route add %s dev %s table 2",LOCAL_NET2,INTERFACE2);
        system(buf);
        memset(buf,0,100);
        sprintf(buf,"ip rule add from %s table 2",LOCAL_IP2);
        system(buf);
        if2_connected = 1;
    }

    // 等待线程结束
    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);
    pthread_join(thread3, NULL);

    // 关闭套接字和 TUN 设备
    close(udp_sock1);
    close(udp_sock2);
    close(tun_fd);
    close(netlink_sock);

    return 0;
}
3.2 服务端(接收端)

AppTun2Srv.c 代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/if.h>
#include <linux/if_tun.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <stdbool.h>
#include <sys/time.h>

#define INTERFACE "eth0" //外网接口
#define TUN_DEVICE "tun0"
#define BUFSIZE 2000
#define UDP_PORT 55500
#define MAX_PEERS 2
#define VIRTUAL_IP "192.168.100.1/24"
#define PKG_TIMEOUT 5000 //数据包接收超时(毫秒)

// 用于存储 UDP 源 IP 和端口的结构体
typedef struct {
    char ip[INET_ADDRSTRLEN];
    int port;
} Peer;

Peer peers[MAX_PEERS];
int peer_count = 0;
pthread_mutex_t peer_list_mutex = PTHREAD_MUTEX_INITIALIZER;

int tun_fd;
int udp_sock;
unsigned short packet_number = 0;

// 函数:创建和配置 TUN 设备
int create_tun_device() {
    struct ifreq ifr;
    int fd = open("/dev/net/tun", O_RDWR);
    if (fd < 0) {
        perror("Opening /dev/net/tun");
        return fd;
    }

    memset(&ifr, 0, sizeof(ifr));
    ifr.ifr_flags = IFF_TUN | IFF_NO_PI;  // 使用 TUN 模式,无附加头
    strncpy(ifr.ifr_name, TUN_DEVICE, IFNAMSIZ);

    if (ioctl(fd, TUNSETIFF, &ifr) < 0) {
        perror("ioctl(TUNSETIFF)");
        close(fd);
        return -1;
    }

    return fd;
}

long long get_relative_time()
{
    struct timeval tv;
    struct timezone tz;

    gettimeofday(&tv,&tz);
    return tv.tv_sec * 1000 + tv.tv_usec / 1000;
}

// 检查源 IP 和端口是否已在列表中
int is_peer_in_list(const char *ip, int port) {
    for (int i = 0; i < peer_count; ++i) {
        if (strcmp(peers[i].ip, ip) == 0 && peers[i].port == port) {
            return 1;
        }
    }
    return 0;
}

// 添加新的源 IP 和端口到列表中
void add_peer_to_list(const char *ip, int port) {
    pthread_mutex_lock(&peer_list_mutex);
    if (!is_peer_in_list(ip, port)) {
        if (peer_count < MAX_PEERS)
        {
            strncpy(peers[peer_count].ip, ip, INET_ADDRSTRLEN);
            peers[peer_count].port = port;
            peer_count++;
        }
        else if(peer_count == MAX_PEERS)
        {
            for (unsigned int i = 0; i < MAX_PEERS - 1; i++)
            {
                peers[i] = peers[i + 1];
            }
            strncpy(peers[MAX_PEERS - 1].ip, ip, INET_ADDRSTRLEN);
            peers[MAX_PEERS - 1].port = port;
        }
    }
    pthread_mutex_unlock(&peer_list_mutex);
}

// 线程函数:接收 UDP 数据并写入 TUN 设备
void *udp_to_tun(void *arg) {
    unsigned char buffer[BUFSIZE];
    unsigned char received_numbers[65536] = {0};  // 用于记录已接收的编号
    unsigned char received_numbers_flags[2] = {0};
    long long last_recv_time = 0,cur_recv_time = 0;

    struct sockaddr_in client_addr;
    socklen_t addr_len = sizeof(client_addr);

    fd_set read_fds;

    while (1) {
        FD_ZERO(&read_fds);
        FD_SET(udp_sock, &read_fds);

        // 使用 select() 套接字有数据可读
        int ret = select(udp_sock + 1, &read_fds, NULL, NULL, NULL);
        if (ret < 0) {
            perror("select");
            continue;
        }

        int len = recvfrom(udp_sock, buffer, BUFSIZE, 0, (struct sockaddr *)&client_addr, &addr_len);
        if (len < 2) {
            perror("Receive failed");
            continue;
        }

        cur_recv_time = get_relative_time();
        if(cur_recv_time - last_recv_time > PKG_TIMEOUT)
        {
            if(received_numbers_flags[0])
            {
                memset(received_numbers,0,0x8000);
                received_numbers_flags[0] = 0;
            }

            if(received_numbers_flags[1])
            {
                memset(received_numbers + 0x8000,0,0x8000);
                received_numbers_flags[1] = 0;
            }
        }
        last_recv_time = cur_recv_time;

        // 提取编号
        unsigned short number = (unsigned short)(buffer[0] << 8) | buffer[1];

         // 获取源 IP 和端口
        char client_ip[INET_ADDRSTRLEN];
        inet_ntop(AF_INET, &client_addr.sin_addr, client_ip, INET_ADDRSTRLEN);
        int client_port = ntohs(client_addr.sin_port);

         // 添加源 IP 和端口到列表
        add_peer_to_list(client_ip, client_port);

        if (received_numbers[number]) {
            continue;  // 已经收到过,丢弃
        }

        // 标记为已收到
        received_numbers[number] = 1;

        // 将数据包的前两字节移除,并写入 TUN 设备
        write(tun_fd, buffer + 2, len - 2);

        if (number < 0x8000)
        {
            received_numbers_flags[0] = 1;
            if(received_numbers_flags[1])
            {
                memset(received_numbers + 0x8000,0,0x8000);
                received_numbers_flags[1] = 0;
            }
        }
        else
        {
            received_numbers_flags[1] = 1;
            if(received_numbers_flags[0])
            {
                memset(received_numbers,0,0x8000);
                received_numbers_flags[0] = 0;
            }
        }
        
    }
}

// 线程函数:从 TUN 设备读取数据并通过 UDP 发送
void *tun_to_udp(void *arg) {
    unsigned char buffer[BUFSIZE];

    while (1) {
        int len = read(tun_fd, buffer + 2, BUFSIZE - 2);  // 读取 IP 数据包
        if (len < 0) {
            perror("Reading from TUN device");
            continue;
        }

        // 在数据包前加上编号
        packet_number++;
        buffer[0] = (packet_number >> 8) & 0xFF;
        buffer[1] = packet_number & 0xFF;

        // 发送数据包到所有记录的源 IP 和端口
        pthread_mutex_lock(&peer_list_mutex);

        for (int i = 0; i < peer_count; ++i) {
            struct sockaddr_in peer_addr;
            memset(&peer_addr, 0, sizeof(peer_addr));
            peer_addr.sin_family = AF_INET;
            peer_addr.sin_port = htons(peers[i].port);
            inet_pton(AF_INET, peers[i].ip, &peer_addr.sin_addr);

            sendto(udp_sock, buffer, len + 2, 0, (struct sockaddr *)&peer_addr, sizeof(peer_addr));
        }
        pthread_mutex_unlock(&peer_list_mutex);
    }
}

void write_file(const char* file_name,unsigned char* data,unsigned int offset,unsigned int len)
{
    int fd = open(file_name, O_WRONLY | O_CREAT | O_TRUNC, 0644);
    write(fd, data + offset,len);
    fsync(fd);
    close(fd);
}

int main() {
    // 创建和配置 TUN 设备
    tun_fd = create_tun_device();
    if (tun_fd < 0) {
        exit(1);
    }

    // 创建 UDP 套接字
    udp_sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (udp_sock < 0) {
        perror("Creating UDP socket");
        exit(1);
    }

    //设置Socket为非阻塞模式
    int flags = fcntl(udp_sock, F_GETFL, 0);
    fcntl(udp_sock, F_SETFL, flags | O_NONBLOCK);

    // 绑定套接字到指定端口
    struct sockaddr_in server_addr;
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = INADDR_ANY;
    server_addr.sin_port = htons(UDP_PORT);

    if (bind(udp_sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
        perror("Bind failed");
        close(udp_sock);
        exit(1);
    }

    // 创建线程
    pthread_t thread1, thread2;
    pthread_create(&thread1, NULL, udp_to_tun, NULL);
    pthread_create(&thread2, NULL, tun_to_udp, NULL);

    // 配置tun网卡IP地址
    {
        char buf[100] = {0};
        sprintf(buf,"ip addr add %s dev %s",VIRTUAL_IP,TUN_DEVICE);
        system(buf);
        memset(buf,0,100);
        sprintf(buf,"ip link set %s up",TUN_DEVICE);
        system(buf);
        memset(buf,0,100);
        sprintf(buf,"iptables -t nat -A POSTROUTING -o %s -j MASQUERADE",INTERFACE);
        system(buf);
    }

    // 等待线程结束
    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);

    // 关闭套接字和 TUN 设备
    close(udp_sock);
    close(tun_fd);

    return 0;
}

4. 使用方法

4.1 编译代码

使用以下命令编译:

gcc AppTun2Cli.c -lpthread -o AppTun2Cli
gcc AppTun2Srv.c -lpthread -o AppTun2Srv
4.2 服务文件

客户端服务 AppTun2Cli.service:

[Unit]
Description=AppTun2Cli service
After=network.target

[Service]
WorkingDirectory=/root/2net
Restart=on-failure
RestartSec=5
ExecStart=/root/2net/AppTun2Cli
User=root

[Install]
WantedBy=multi-user.target

服务端服务 AppTun2Srv.service:

[Unit]
Description=AppTun2Srv service
After=network.target

[Service]
WorkingDirectory=/root/2net
Restart=on-failure
RestartSec=5
ExecStart=/root/2net/AppTun2Srv
User=root

[Install]
WantedBy=multi-user.target

把service文件放到/etc/systemd/system目录下

4.2 启动程序

运行服务:

#启动客户端程序
sudo service AppTun2Cli start

#启动服务端程序
sudo service AppTun2Srv start

停止服务:

#停止客户端程序
sudo service AppTun2Cli stop

#停止服务端程序
sudo service AppTun2Srv stop

6. 注意事项

  1. TUN/TAP 权限:程序需要以 root 权限运行。
  2. 链路带宽和延迟:两条链路的带宽和延迟差异可能导致乱序,接收端需要处理乱序包。
  3. 错误处理:增强对异常的处理(如链路故障检测)。
  4. 去重优化:使用唯一标识符(如序列号)替代完整数据进行比较,提高效率。

通过以上实现,可以构建一个支持多链路传输和冗余发送的高可用网络方案!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值