tap 转发数据包
1、打开tap虚拟网卡#
我将它封装在了一个函数opentap中,如下所示
int open_tap(const char *dev, short flags) {
struct ifreq ifr;
int fd, err;
char *device = "/dev/net/tun";
if ((fd = open(device, O_RDWR)) < 0) {
printf("[ERROR|%s|Cannot open TUN/TAP dev %d]\n", __TIME__, __LINE__);
exit(0);
}
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_ifru.ifru_flags |= flags;
if (dev != NULL && (*dev) != '\0') {
strncpy(ifr.ifr_ifrn.ifrn_name, dev, IFNAMSIZ);
}
if ((err = ioctl(fd, TUNSETIFF, (void *)&ifr)) < 0) {
close(fd);
return err;
}
return fd;
}
2、关闭防火墙,设置iptables策略,将流量导向tap网卡
# 关闭防火墙
systemctl stop ufw
systemctl disable ufw
# 开启转发
echo 1 > /proc/sys/net/ipv4/ip_forward
# 或者在/etc/sysctl.conf文件中加入一行(永久生效)
# 设置iptables
iptables -t nat -A PREROUTING -i ens33 -d 192.168.13.147 -p tcp --dport 1234 -j DNAT --to 10.0.1.123:6666
# 将从ens33来的目标地址为192.168.13.147且目的端口号为1234的tcp数据包做nat
iptables -t nat -A POSTROUTING --dst 10.0.1.123 -p tcp --dport 6666 -j SNAT --to-source 192.168.13.147
# 将目标地址为10.0.1.123、目的端口号为6666的tcp数据包做nat
# 此时tap网卡的ip地址为10.0.1.111
# 10.0.1.123和tap虚拟网卡在同一网段,因此会收到arp请求,询问10.0.1.123的mac地址
# 添加一条静态的arp条目
arp -s 10.0.1.123 xx:xx:xx:xx:xx:xx
通过工具从192.168.13.1向192.168.13.147发udp数据包
然后通过wireshark抓包就可以看到tap网卡有流量经过了
相应地,192.168.13.1也收到了回包
3、代码
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <fcntl.h>
#include <linux/if_tun.h>
#include <net/if.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>
int open_tap(const char *dev, short flags) {
struct ifreq ifr;
int fd, err;
char *device = "/dev/net/tun";
if ((fd = open(device, O_RDWR)) < 0) {
printf("[ERROR|%s|Cannot open TUN/TAP dev %d]\n", __TIME__, __LINE__);
exit(0);
}
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_ifru.ifru_flags |= flags;
if (dev != NULL && (*dev) != '\0') {
strncpy(ifr.ifr_ifrn.ifrn_name, dev, IFNAMSIZ);
}
if ((err = ioctl(fd, TUNSETIFF, (void *)&ifr)) < 0) {
close(fd);
return err;
}
return fd;
}
static u_int16_t checksum(u_int16_t *address, int count) {
long sum = 0;
while (count > 1) {
sum += *(ushort *)address++;
count -= 2;
}
if (count > 0) {
sum += *(u_char *)address;
}
while (sum >> 16) {
sum = (sum & 0xffff) + (sum >> 16);
}
return ~sum;
}
unsigned short check_sum(unsigned short *data, int num){
unsigned long sum = 0;
for(int i = 0;i < num; i++){
sum += *data++; //将2个16进制数相加
sum = (sum>>16) + (sum&0xffff); //取相加结果的低16位与高16位相加
}
return ~sum; //对最后的结果取反
}
struct udp_hdr {
u_int16_t src_port;
u_int16_t dest_port;
u_int16_t len;
u_int16_t checksum;
} __attribute__((__packed__));
struct icmp_hdr {
u_int8_t icmp_type;
u_int8_t icmp_code;
u_int16_t icmp_cksum;
u_int16_t icmp_ident;
u_int16_t icmp_seq_nb;
} __attribute__((__packed__));
struct ipv4_hdr {
__extension__
union {
u_int8_t version_ihl; /**< version and header length */
struct {
#if RTE_BYTE_ORDER == RTE_LITTLE_ENDIAN
u_int8_t ihl:4; /**< header length */
u_int8_t version:4; /**< version */
#elif RTE_BYTE_ORDER == RTE_BIG_ENDIAN
uint8_t version:4; /**< version */
uint8_t ihl:4; /**< header length */
#endif
};
};
u_int8_t type_of_service; /**< type of service */
u_int16_t total_length; /**< length of packet */
u_int16_t packet_id; /**< packet ID */
u_int16_t fragment_offset; /**< fragmentation offset */
u_int8_t time_to_live; /**< time to live */
u_int8_t next_proto_id; /**< protocol ID */
u_int16_t hdr_checksum; /**< header checksum */
u_int32_t src_addr; /**< source address */
u_int32_t dst_addr; /**< destination address */
} __attribute__((__packed__));
struct ether_addr {
u_int8_t addr_bytes[6]; /**< Addr bytes in tx order */
} __attribute__((__aligned__(2)));
struct ether_hdr {
struct ether_addr dst_addr; /**< Destination address. */
struct ether_addr src_addr; /**< Source address. */
u_int16_t ether_type; /**< Frame type. */
} __attribute__((__aligned__(2)));
int main() {
int tap;
long int ret;
char tap_name[IFNAMSIZ];
unsigned char buf[4096];
sprintf(tap_name, "tap1");
tap = open_tap(tap_name, IFF_TAP | IFF_NO_PI);
// tap = open_tap(tap_name, IFF_TUN | IFF_NO_PI);
if (tap < 0) {
printf("[ERROR|%s|Cannot open TUN/TAP dev %d]\n", __TIME__, __LINE__);
exit(0);
}
char *cmd = (char *)malloc(sizeof(char) * 60);
snprintf(cmd, 60, "ifconfig %s 10.0.1.111", tap_name);
printf("[DEBUG|%s|line:%d|cmd:%s %d]\n", __TIME__, __LINE__, cmd, system(cmd));
free(cmd);
while (1) {
ret = read(tap, buf, sizeof(buf));
printf("%ld\n", ret);
ssize_t len = ret;
if (ret <= 0) continue;
struct ether_hdr *ether = (struct ether_hdr *)(buf);
struct ipv4_hdr *ip = (struct ipv4_hdr *)(buf + sizeof(struct ether_hdr));
struct udp_hdr *udp = (struct udp_hdr *)(buf + sizeof(struct ether_hdr) + sizeof(struct ipv4_hdr));
udp->checksum = 0;
ip->dst_addr ^= ip->src_addr;
ip->src_addr ^= ip->dst_addr;
ip->dst_addr ^= ip->src_addr;
ip->hdr_checksum = 0;
ip->hdr_checksum = checksum((u_int16_t *)ip, ip->ihl * 4);
struct ether_addr addr;
addr = ether->src_addr;
ether->src_addr = ether->dst_addr;
ether->dst_addr = addr;
ret = write(tap, buf, len);
printf("Write Bytes %ld\n", ret);
}
return 0;
}
发包工具
作者: correct
出处:https://www.cnblogs.com/correct/p/16641914.html
本站使用「CC BY 4.0」创作共享协议,转载请在文章明显位置注明作者及出处。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话