#include <string.h>
#include <stdlib.h>
#include <libipq.h>
#include <stdio.h>
#include <libnetfilter_queue/libnetfilter_queue.h>
#include <linux/ip.h>
#include <signal.h>
#define BUFSIZE 2048
static int condition = 0;
void condition_handler(int num)
{
if (condition == 1) {
condition = 0;
} else {
condition = 1;
}
}
int main(int argc, char **argv)
{
int status;
unsigned char buf[BUFSIZE];
struct ipq_handle *h;
signal (SIGUSR1, condition_handler);
h = ipq_create_handle(0, 2/*PROTO_IPV4*/);
status = ipq_set_mode(h, IPQ_COPY_PACKET, BUFSIZE);
do{
status = ipq_read(h, buf, BUFSIZE, 0);
switch (ipq_message_type(buf)) {
case IPQM_PACKET: {
ipq_packet_msg_t *m = ipq_get_packet(buf);
size_t data_len = m->data_len;
//读取到有数据包后,触发执行启动VPN脚本的命令
system("/home/zhaoya/start_vpn param1 param2 paramX");
//等待start_vpn命令执行完毕,它会建立VPN隧道,建立完成发送信号给该进程
while(!condition) {
pause();
}
//成功建立隧道以后,将数据包原封不动地重新注入回去
status = ipq_set_verdict(h, m->packet_id,
NF_ACCEPT, data_len+sizeof(struct iphdr), (char*)m->payload);
break;
}
default:
break;
}
} while (1);
ipq_destroy_handle(h);
return 0;
}
上述程序很简单,逻辑上它就是读取queue的数据包,然后触发命令建立隧道,建立好隧道后,发送信号给进程,进程接着将数据包重新注入。隧道建立好以后,还需要修改vpn这个condition变量的值,使后续的数据包不再queue,如此策略化的事情用脚本封装是比较好的选择:#!/bin/bash # 模拟创建隧道,添加一条路由 ip route add 192.168.1.1/32 via 172.16.49.88 echo 0 >/proc/net/nf_condition/vpn killall -SIGUSR1 queue # 模拟5秒没有流量 sleep 5; # 模拟断开隧道 ip route del 192.168.1.1/32 echo 1 >/proc/net/nf_condition/vpn killall -SIGUSR1 queue经过测试,效果非常不错!但是那个用户态进程实在需要再润色润色啊。最后,我觉得最重要的不是如何想出解决问题的办法,而是想出如何验证你的解决方案是正确的方法,在你不可能实际证明的时候,或者当你实际证明要付出很大代价的时候,就要想出模拟的办法。但是很多人只是闷头做自己擅长的事情,如果模拟不了15454500053535300042453535004343535350043534647430043532578053535353005330535305454636365363530046464640535353500个用户,他们就要想办法模拟146560053353666535005353535305463320465479024214794546424235364657007684000646465789032558970032646320053646758320个用户,精确本身没有错,错在知道精确的尺度和粒度以及层次更加重要。要认为什么东西都可以通过“设置”解决,远远不是!你需要的是:删除!