向目标服务器发送 tun/tap 个数据包
sending tun/tap packets to destination server
如何将tun读取的数据包发送到服务器?我通过 Wireshark 检查数据包,发现它们都是 DNS 请求,并将我系统的默认网关更改为我的 VM 的 IP,所有数据包都到达那里。 tun 运行良好,我可以打印有关数据包的所有内容,但问题是我不知道如何将它们转发到目标服务器。 (我知道套接字编程)
代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <netdb.h>
#include <fcntl.h>
#include <signal.h>
#include <linux/if.h>
#include <linux/if_tun.h>
#include <linux/ip.h>
#include <arpa/inet.h>
#include <netinet/in.h>
struct sockaddr_in Packet, dest;
#define Tun_Device "tun0"
static int max(int a, int b){
return a > b ? a : b;
}
int read_tun(char *name, int type){
struct ifreq ifr;
int tun_fd;
tun_fd = open("/dev/net/tun", O_RDWR)
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_flags = type;
strncpy(ifr.ifr_name, name, IFNAMSIZ);
ioctl(tun_fd, TUNSETIFF, (void *)&ifr);
return tun_fd;
}
int main(){
int tun_fd, packet_length, tcpfd;
struct sockaddr_in cliaddr, servaddr;
char buffer[2048];
char destination[16];
int i = 0;
struct iphdr *iph = (struct iphdr*) buffer;
tun_fd = read_tun(Tun_Device, IFF_TUN | IFF_NO_PI);
while (1) {
fd_set readset;
FD_ZERO(&readset);
FD_SET(tun_fd, &readset);
FD_SET( , &readset);
int max_fd = max(tun_fd, udp_fd) + 1;
select(max_fd, &readset, NULL, NULL, NULL)
if (FD_ISSET( , &readset)) {
}
if (FD_ISSET( , &readset)) {
}
}
}
您的默认网关必须开启IP转发,您需要设置相应的iptables规则。
从网关机器,您需要:
# Turn on IPv4 forward
sudo sysctl net.ipv4.ip_forward=1
# Add MASQUERADE rule
iptables -t nat -I POSTROUTING -s x.x.x.x/y ! -o tun0 -j MASQUERADE
# Allow forwarding
iptables -t filter -I FORWARD -d x.x.x.x/y -j ACCEPT
iptables -t filter -I FORWARD -s x.x.x.x/y -j ACCEPT
iptables -t filter -I FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT
备注
- 使用您的 TUN/TAP 网络子网调整
x.x.x.x/y
。
- 使用您使用的虚拟网络接口名称调整
tun0
。
客户端的默认网关设置
当您覆盖到虚拟网络接口的任何流量时,您仍然需要与您的互联网路由器通信,因为您需要将封装的数据包发送到 VPN 服务器。
为此,您不应删除通过物理路由器的默认路由。
规则很简单:
- 其 destination/source 为 VPN 服务器 IP 地址 的数据包必须 sent/received 通过物理路由器。
- 其他数据包必须sent/received通过虚拟网络接口。
- 此外,如果有的话,您可能希望为本地网络设置一些例外。
配置示例
VPN_IFACE=tun0
VPN_PRIVATE_IP=10.8.8.2
VPN_GATEWAY_IP=10.8.8.1
VPN_BROADCAST_IP=10.8.8.25
VPN_PUBLIC_IP=123.123.123.123
REAL_DEFAULT_IP=192.168.1.1
# Raise up virtual network interface
sudo ip link set dev $VPN_IFACE up mtu 1480
sudo ip addr add dev $VPN_IFACE $VPN_PRIVATE_IP/24 broadcast $VPN_BROADCAST_IP
# Encapsulated packet must be routed via your real router.
sudo ip route add $VPN_PUBLIC_IP/32 via $REAL_DEFAULT_IP
# Override any traffic, except for encapsulated packet
# Make them routed via virtual network interface gateway
sudo ip route add 0.0.0.0/1 dev $VPN_IFACE via $VPN_GATEWAY_IP
sudo ip route add 128.0.0.0/1 dev $VPN_IFACE via $VPN_GATEWAY_IP
# Don't delete default route to your real physical router!
如何将tun读取的数据包发送到服务器?我通过 Wireshark 检查数据包,发现它们都是 DNS 请求,并将我系统的默认网关更改为我的 VM 的 IP,所有数据包都到达那里。 tun 运行良好,我可以打印有关数据包的所有内容,但问题是我不知道如何将它们转发到目标服务器。 (我知道套接字编程)
代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <netdb.h>
#include <fcntl.h>
#include <signal.h>
#include <linux/if.h>
#include <linux/if_tun.h>
#include <linux/ip.h>
#include <arpa/inet.h>
#include <netinet/in.h>
struct sockaddr_in Packet, dest;
#define Tun_Device "tun0"
static int max(int a, int b){
return a > b ? a : b;
}
int read_tun(char *name, int type){
struct ifreq ifr;
int tun_fd;
tun_fd = open("/dev/net/tun", O_RDWR)
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_flags = type;
strncpy(ifr.ifr_name, name, IFNAMSIZ);
ioctl(tun_fd, TUNSETIFF, (void *)&ifr);
return tun_fd;
}
int main(){
int tun_fd, packet_length, tcpfd;
struct sockaddr_in cliaddr, servaddr;
char buffer[2048];
char destination[16];
int i = 0;
struct iphdr *iph = (struct iphdr*) buffer;
tun_fd = read_tun(Tun_Device, IFF_TUN | IFF_NO_PI);
while (1) {
fd_set readset;
FD_ZERO(&readset);
FD_SET(tun_fd, &readset);
FD_SET( , &readset);
int max_fd = max(tun_fd, udp_fd) + 1;
select(max_fd, &readset, NULL, NULL, NULL)
if (FD_ISSET( , &readset)) {
}
if (FD_ISSET( , &readset)) {
}
}
}
您的默认网关必须开启IP转发,您需要设置相应的iptables规则。
从网关机器,您需要:
# Turn on IPv4 forward
sudo sysctl net.ipv4.ip_forward=1
# Add MASQUERADE rule
iptables -t nat -I POSTROUTING -s x.x.x.x/y ! -o tun0 -j MASQUERADE
# Allow forwarding
iptables -t filter -I FORWARD -d x.x.x.x/y -j ACCEPT
iptables -t filter -I FORWARD -s x.x.x.x/y -j ACCEPT
iptables -t filter -I FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT
备注
- 使用您的 TUN/TAP 网络子网调整
x.x.x.x/y
。 - 使用您使用的虚拟网络接口名称调整
tun0
。
客户端的默认网关设置
当您覆盖到虚拟网络接口的任何流量时,您仍然需要与您的互联网路由器通信,因为您需要将封装的数据包发送到 VPN 服务器。
为此,您不应删除通过物理路由器的默认路由。
规则很简单:
- 其 destination/source 为 VPN 服务器 IP 地址 的数据包必须 sent/received 通过物理路由器。
- 其他数据包必须sent/received通过虚拟网络接口。
- 此外,如果有的话,您可能希望为本地网络设置一些例外。
配置示例
VPN_IFACE=tun0
VPN_PRIVATE_IP=10.8.8.2
VPN_GATEWAY_IP=10.8.8.1
VPN_BROADCAST_IP=10.8.8.25
VPN_PUBLIC_IP=123.123.123.123
REAL_DEFAULT_IP=192.168.1.1
# Raise up virtual network interface
sudo ip link set dev $VPN_IFACE up mtu 1480
sudo ip addr add dev $VPN_IFACE $VPN_PRIVATE_IP/24 broadcast $VPN_BROADCAST_IP
# Encapsulated packet must be routed via your real router.
sudo ip route add $VPN_PUBLIC_IP/32 via $REAL_DEFAULT_IP
# Override any traffic, except for encapsulated packet
# Make them routed via virtual network interface gateway
sudo ip route add 0.0.0.0/1 dev $VPN_IFACE via $VPN_GATEWAY_IP
sudo ip route add 128.0.0.0/1 dev $VPN_IFACE via $VPN_GATEWAY_IP
# Don't delete default route to your real physical router!