OpenVPN的Linux内核版,鬼魅的残缺 part II:The encrypt engine
把本已不堪的OpenSSL移植到Linux Kernel,这真是一个疯狂的想法,极其疯狂的想法,过于疯狂的想法,以至于作为一个还算正常的人只好作罢!我需要的是使用现有的内核中的加密引擎或者简单的扩展它,抑或稍微复杂的重写它..总之,我需要的是一套内核态的加密算法引擎,crypto模块中的crypto_alg不错,试试看吧。
OpenVPN的数据通道和控制通道是分离的,这就使数据通道的处理进入内核成为了可能,当初接触OpenVPN的时候,我总是在想为什么OpenVPN不使用SSL加密通道来封装OpenVPN协议呢?是的,技术上的解释太多了,但同时也太苍白了,如果真的使用了SSL通道,那么我就不可能做今天的工作了,再次感谢James Yonan!在内核态处理OpenVPN数据通道是及其合理的,这并不是因为我对内核比较熟悉才故意这么说,而是真理。说白了OpenVPN的协议不就是和传输层协议一样吗?若不是它当初构建于用户态的话,OpenVPN数据通道协议不就和ESP/AH处在同一个层次吗?而OpenVPN控制协议难道不就是和IKE差不多吗?
我需要做的仅仅就是在内核注册一个协议处理而已,为了兼容既有的协议,我使用了UDP的encap_rcv钩子,而没有直接将UDP干掉,这已经实现了,请参看我的《OpenVPN的Linux内核版,鬼魅的残缺 part I:The PROTOCOL》。
有了协议的支持,剩下的就是处理数据了,加密乎?解密乎?HMAC乎?我需要在内核做OpenSSL做的事情,而内核既有的crypto框架真的是简单且难用!接口调用太简单,参数列表太简单,但是这只能让我高兴一半!剩下的事简直让人头大,我试了一下AES-128加密解密,我必须做所有的事情,事实上,内核的crypto_cipher_encrypt_one/crypto_cipher_decrypt_one接口每次只能处理一个数据块。
在修改代码之前,我试了一个例子:
char data[16] = "abcdefg"; char key[16] = "abcdefg"; int blksize = 0; struct crypto_cipher *tfm; tfm = crypto_alloc_cipher("aes"/*ctx.cipher_name*/, 0/*CRYPTO_TFM_MODE_ECB*/, CRYPTO_ALG_ASYNC); blksize = crypto_cipher_blocksize(tfm); crypto_cipher_setkey(tfm, (const unsigned char *)&key[0], 16); for (i = 0; i < skb->len - 1; i += blksize) { crypto_cipher_encrypt_one(tfm, data + i, data + i); }代码还算简单,要做的就是把这些逻辑组织一下,放在udp_encap_rcv钩子以及tun xmit钩子里面就可以了。
但是我突然又感到不那么悲哀。因为我只是在试验既有的接口!最终我肯定会封装它的,或者找开源的现成实现,或者,直接使用硬件。这部分的代码还是接着协议的那部分修改,依然是tun.c,补丁文件如下:
--- tun.c.orig 2013-11-30 13:17:30.000000000 +0800 +++ tun.c 2014-02-08 18:44:34.000000000 +0800 @@ -34,6 +34,28 @@ * Modifications for 2.3.99-pre5 kernel. */ +/* + * 我,又一次自私地使用了tun.c,不过这次的工作和tun本身并没有太大的关系, + * 只是想做一个简单的OpenVPN短路hack,仅此而已,我使用tun做修改是因为简单, + * 毕竟我只是需要将一个socket和tun联系起来,仅此而已,我需要做的就是短接 + * UDP socket和tun网卡,仅此而已.... :) + * + * 数据通道进入内核的好处是显而易见的,多处理操作的效率由softirq分发系统决定, + * 而这个是简单的,在8核心处理器上,经过测试,使用Intel 82583多队列卡,按照 + * tuple做hash中断分发,保持cache活性的基础上,也能首先OpenVPN协议的高速解析, + * 任何用户态的多线程架构与之相比都爆弱。但是此时问题浮现: + * + * 1.不是说内核态处理控制面而用户态处理数据面吗?对于OpenVPN,怎么反过来了啊, + * 有点懵了!是的,数据面放到用户态只善作个幻象,现如今不是还没有很好的实例嘛... + * 我并非说用户态多线程不好,只是对OpenVPN而言的,不信你试试。好了,在PF RING + * 还玩不转的时候,我只能这样,也不容易。 + * 2.这里没有使用加密,接口是有了,但是没有高效的实现,我可不想OpenVPN成为Yet + * Another IPSec + * + * 问题多多,[email protected],还是这个邮箱 + * + **/ + #define DRV_NAME "tun" #define DRV_VERSION "1.6" #define DRV_DESCRIPTION "Universal TUN/TAP device driver" @@ -64,7 +86,19 @@ #include <net/net_namespace.h> #include <net/netns/generic.h> #include <net/rtnetlink.h> +#include <net/checksum.h> #include <net/sock.h> +#include <net/udp.h> +#include <linux/socket.h> +#include <net/inet_sock.h> +#include <linux/udp.h> +#include <linux/ip.h> +#include <linux/net.h> +#include <linux/file.h> +#include <linux/jhash.h> +#include <linux/netfilter.h> +#include <linux/netfilter_ipv4.h> + #include <asm/system.h> #include <asm/uaccess.h> @@ -82,6 +116,31 @@ #define DBG1( a... ) #endif +/* 定义一个OpenVPN封装类型 */ +#define UDP_ENCAP_OVPN 20 +/* 连接一个UDP套接字和TUN网卡的ioctl命令 */ +#define TUNLINKOVPN _IOW(‘T‘, 216, int) +/* 添加一个multi_instance的ioctl命令 */ +#define TUNADDMILTI _IOW(‘T‘, 217, int) +/* 为一个multi_instance添加一个虚拟地址的ioctl命令 */ +#define TUNSETMIVIP _IOW(‘T‘, 218, int) +/* 删除一个multi_instance的ioctl命令 */ +#define TUNDELMILTI _IOW(‘T‘, 219, int) +/* 设置密钥的ioctl命令 */ +#define TUNSETMKEY _IOW(‘T‘, 220, int) +/* 获取密钥的ioctl命令 */ +#define TUNGETMKEY _IOW(‘T‘, 221, int) + +#define OVPN_OPT_DEC 0 +#define OVPN_OPT_ENC 1 + +/* + * 用于封装ioctl命令,但不经常,也不绝对... + **/ +struct sockfd { + int fd; +}; + #define FLT_EXACT_COUNT 8 struct tap_filter { unsigned int count; /* Number of addrs. Zero means disabled */ @@ -97,6 +156,147 @@ struct tun_sock; + +/* UDP的encap返回正常路径 */ +#define UDP_DECAP_PASS 1 +/* UDP的encap自己消费了数据包 */ +#define UDP_DECAP_STOLEN 0 +/* 以上的规范详细情况自行看UDP处理以及IPSec/L2TP作为一个例子的实现 */ + +/* + * OpenVPN的常量定义,我是不是该准备一个头文件和C文件呢? + * 借用tun.c总不是什么长久之事!tun又不是只用于OpenVPN啊, + * 然而tun.c确实该加一个HOOK机制了... + **/ +#define MAX_HASH_BUCKETS 256 +/* 暂时先这么多 */ +#define MAX_KEY_LENGTH 512 +#define P_DATA_V1 6 +#define P_OPCODE_SHIFT 3 + +/* 这个锁的粒度有点粗 */ +DEFINE_SPINLOCK(ovpn_lock); + +typedef u32 packet_id_type; +typedef u32 net_time_t ; + +/* + * 使用IP地址/端口对建立multi_instance + **/ +struct instance_req { + u32 real_addr; + __be16 port; +}; + +/* + * 为一个multi_instance添加一个虚拟IP地址,此结构体目前仅适用于 + * TUN模式。因为对于TAP模式需要实现一个列表,基于该列表实现一个 + * 虚拟交换机,哦,是的,虚拟交换机... + * */ +struct instance_vreq { + u32 real_addr; + u32 vaddr; + __be16 port; +}; + +/* 用于向内核传递密钥或者反过来传递密钥 */ +/* 是不是应该用PF_KEY啊,小小说不能,我就不用了 */ +struct key_block { + struct instance_req ir; + unsigned char key1[MAX_KEY_LENGTH]; + unsigned char key2[MAX_KEY_LENGTH]; + unsigned char key3[MAX_KEY_LENGTH]; + unsigned char key4[MAX_KEY_LENGTH]; +}; + +/* + * 用于实现OpenVPN的防重放机制 + **/ +struct packet_id_send +{ + packet_id_type id; + time_t time; +}; + +/* + * 用于实现OpenVPN的防重放机制,但是天啊...里面的字段在协议移植阶段 + * 是没有任何用武之地的,是的,没有用... + * */ +struct packet_id_rec +{ + time_t last_reap; /* last call of packet_id_reap */ + time_t time; /* highest time stamp received */ + packet_id_type id; /* highest sequence number received */ + int seq_backtrack; /* set from --replay-window */ + int time_backtrack; /* set from --replay-window */ + int max_backtrack_stat; /* maximum backtrack seen so far */ + int initialized; /* true if packet_id_init was called */ + struct seq_list *seq_list; /* packet-id "memory" */ + const char *name; + int unit; +}; + +/* + * 用于实现OpenVPN的防重放机制,目前的版本仅仅是为了例行公事,发送前 + * 在OpenVPN头中封装一个递增的packet ID,但是注意,不支持LONG FORM!! + * */ +struct packet_id +{ + struct packet_id_send send; + struct packet_id_rec rec; +}; + +/* + * 万恶又万能的multi_instance,是不是有点熟悉呢??对!This is it! + * */ +struct multi_instance { + struct list_head list; + struct hlist_node rhnode; + struct hlist_node vhnode; + struct sock *sk; + struct packet_id packet_id; + u32 saddr; + u32 daddr; + unsigned char hsaddr[ETH_ALEN]; + /* for a learning Vswitch , it is a list! TODO */ + unsigned char hdaddr[ETH_ALEN]; + u32 real_saddr; + u32 real_daddr; + __be16 dport; + void (*mi_destroy)(struct multi_instance *); +}; + +/* + * 我的本意并不是移植OpenVPN,而是实现一个新的协议,but,but,but,but + * 苦于没有客户端,我为何不使用现成的OpenVPN呢??它的协议足够简单啊足够简单! + * */ +struct encap_context { + struct hlist_head hash[MAX_HASH_BUCKETS]; + struct hlist_head vhash[MAX_HASH_BUCKETS]; + /* 最终还是说服了自己,解除了OpenVPN和tun之间的耦合 :) */ + int (*encap_xmit)(struct tun_struct *tun, struct sk_buff *skb); + /* 我并没有区分cipher和auth,也就是说,我把加密运算和HMAC统一使用一套回调函数完成 :>| */ + int (*cipher_init)(void *arg); + int (*cipher_enc)(struct sk_buff *skb, void *arg); + int (*cipher_fini)(void *arg); +}; + +/* + * 就是它!这就是OpenVPN协议的本质!瞧瞧看吧,你仅仅需要设置3个字段足矣! + * ocode:这个字段其实包含以下两个部分 + * opt :很显然,我在内核中只处理数据通道,那么它是P_DATA_V1常量 + * key_id :这个keyid用于切换密钥。目前使用定值0,即版本0.1不支持密钥重协商, + * 然则这只是个开始... + * id: 此字段用于封装将要发送的数据包的ID,防重放攻击 + * 可见,关键的关键就是如何填充以下结构体的问题...对了,我可以说填充UDP头和IP头不是个事儿 + * 吗?如果它们都成了事儿,还怎么好意思说自己比较喜欢折腾内核协议栈呢... :( + **/ +struct ovpnhdr { + u8 ocode; + packet_id_type id; + /* 注意,不要按照最长字段自然对齐,这是在玩网络,而不是内存! */ +} __attribute__((packed)); + struct tun_struct { struct tun_file *tfile; unsigned int flags; @@ -108,6 +308,11 @@ struct tap_filter txflt; struct socket socket; + struct sock *encap_sock; + /* pass THIS into encap_xmit like OO ?? */ + /* 对于这个回调函数,我该说些什么呢?实际上,我真的该将其放在encap_context里面 */ + /* int (*encap_xmit)(struct tun_struct *tun, struct sk_buff *skb);*/ + struct encap_context ctx; #ifdef TUN_DEBUG int debug; @@ -119,6 +324,497 @@ struct tun_struct *tun; }; +/* + * 这个destroy函数用于清理一个multi_instance,一个析构 + **/ +void ovpn_destroy(struct multi_instance *mi) +{ + return; +} + +/* + * 根据一个IP地址和端口删除一个multi_instance + **/ +static void ovpn_del_real_instance( struct tun_struct *tun, + u32 real_addr, + __be16 port) +{ + struct multi_instance *tmi; + struct multi_instance *mi; + struct hlist_node *node; + unsigned int hash = jhash_2words(real_addr, port, 0); + + spin_lock_bh(&ovpn_lock); + hlist_for_each_entry(tmi, node, &tun->ctx.hash[hash % MAX_HASH_BUCKETS], rhnode) { + if (real_addr == tmi->real_daddr && + port == tmi->dport) { + mi = tmi; + } + } + if (!mi) { + spin_unlock_bh(&ovpn_lock); + return ; + } + hlist_del(&mi->rhnode); + hlist_del(&mi->vhnode); + spin_unlock_bh(&ovpn_lock); + kfree(mi); +} + +/* + * 添加一个multi_instance + **/ +static struct multi_instance *ovpn_add_real_instance( struct tun_struct *tun, + u32 real_addr, + __be16 port) +{ + struct multi_instance *ret = NULL; + struct multi_instance *tmi; + struct hlist_node *node; + unsigned int hash = jhash_2words(real_addr, port, 0); + + spin_lock_bh(&ovpn_lock); + hlist_for_each_entry(tmi, node, &tun->ctx.hash[hash % MAX_HASH_BUCKETS], rhnode) { + if (real_addr == tmi->real_daddr && + port == tmi->dport) { + spin_unlock_bh(&ovpn_lock); + return tmi; + } + } + ret = kzalloc(sizeof(struct multi_instance), GFP_ATOMIC); + if (!ret) { + spin_unlock_bh(&ovpn_lock); + return NULL; + } + ret->dport = port; + ret->real_daddr = real_addr; + ret->sk = tun->encap_sock; + ret->mi_destroy = ovpn_destroy; + ret->real_saddr = inet_sk(ret->sk)->saddr; + hash = jhash_2words(ret->real_daddr, ret->dport, 0); + INIT_HLIST_NODE(&ret->rhnode); + INIT_HLIST_NODE(&ret->vhnode); + hlist_add_head(&ret->rhnode, &tun->ctx.hash[hash % MAX_HASH_BUCKETS]); + spin_unlock_bh(&ovpn_lock); + return ret; +} + +/* + * 为一个multi_instance添加一个虚拟IP地址,这个本来应该实现成一个虚拟交换机的 + * BUT对于TUN模式而言,我采用了替换模式,也就是说,我的这个版本并不支持iroute + * 不支持又怎么样呢?早晚的事吧。希望,真心希望James Yonan不要打我哦。。。 + **/ +static int ovpn_add_virtual_instance( struct tun_struct *tun, + u32 real_addr, + __be16 port, + u32 addr) +{ + struct multi_instance *mi; + struct multi_instance *tmi; + struct hlist_node *node; + unsigned int hash = jhash_2words(real_addr, port, 0); + + spin_lock_bh(&ovpn_lock); + hlist_for_each_entry(tmi, node, &tun->ctx.hash[hash % MAX_HASH_BUCKETS], rhnode) { + if (real_addr == tmi->real_daddr && + port == tmi->dport) { + mi = tmi; + break; + } + } + if (!mi) { + spin_unlock_bh(&ovpn_lock); + return -1; + } + hlist_del_init(&mi->vhnode); + mi->daddr = addr; + hash = jhash_1word(mi->daddr, 0); + hlist_add_head(&mi->vhnode, &tun->ctx.vhash[hash % MAX_HASH_BUCKETS]); + spin_unlock_bh(&ovpn_lock); + return 0; +} + +static int ovpn_pre_endecrypt(int mode, + struct tun_struct *tun, + struct sk_buff *skb, + struct multi_instance *mi) +{ + u8 *data; + u8 ocode = 0; + int ret = 0; + int op; + if (mode == OVPN_OPT_DEC) { + data = skb->data; + ocode = data[0]; + op = ocode >> P_OPCODE_SHIFT; + if (op != P_DATA_V1) { + ret = -1; + goto out; + } + } else if (mode == OVPN_OPT_ENC){ + + } else { + ret = -1; + goto out; + } +out: + return ret; +} + +static int ovpn_endecrypt(int mode, + struct tun_struct *tun, + struct sk_buff *skb, + struct multi_instance *mi) +{ + + /* return tun->ctx.endecrypt(tun, skb); */ + return 0; +} + +struct ovpnhdr *ovpn_hdr(struct sk_buff *skb) +{ + return (struct ovpnhdr*)(skb->data); +} + +static int ovpn_post_endecrypt(int mode, + struct tun_struct *tun, + struct sk_buff *skb, + struct multi_instance *mi) +{ + int ret = 0; + struct ovpnhdr *ohdr; + if (mode == OVPN_OPT_ENC) { + ohdr = ovpn_hdr(skb); + ++mi->packet_id.send.id; + ohdr->id = htonl(mi->packet_id.send.id); + ohdr->ocode = (P_DATA_V1 << P_OPCODE_SHIFT) | 0x0; + } else if (mode == OVPN_OPT_DEC) { + } else { + ret = -1; + goto out; + } +out: + return ret; +} + +/* + * 真正的亡灵序曲在这里大肆打折! + * 它截取了UDP的receive处理流程,它可以自行处理数据包,也可以将数据包返回给正常的UDP receive流程 + * 点赞的说,它就是一个UDP Netfilter,或者叫做UDPFilter更好!它也有自己的规范: + * + * This is an encapsulation socket so pass the skb to + * the socket‘s udp_encap_rcv() hook. Otherwise, just + * fall through and pass this up the UDP socket. + * up->encap_rcv() returns the following value: + * =0 if skb was successfully passed to the encap + * handler or was discarded by it. + * >0 if skb should be passed on to UDP. + * <0 if skb should be resubmitted as proto -N + * + * 有点蹩脚,但是毕竟是一种HOOK机制,实用主义者会说,就是它了! + */ +static int ovpn_data_channel_decap_recv(struct sock *sk, struct sk_buff *skb) +{ + struct tun_struct *tun = NULL; + struct multi_instance *mi = NULL; + struct multi_instance *tmi; + struct hlist_node *node; + struct iphdr *hdr = ip_hdr(skb); + struct udphdr *ud = udp_hdr(skb); + int ret = UDP_DECAP_PASS; + u32 addr = hdr->daddr; + __be16 port = ud->source; + unsigned int hash = jhash_2words(addr, port, 0); + + tun = (struct tun_struct *)sk->sk_user_data; + + + spin_lock_bh(&ovpn_lock); + hlist_for_each_entry(tmi, node, &tun->ctx.hash[hash % MAX_HASH_BUCKETS], rhnode) { + if (addr == tmi->real_daddr && + port == tmi->dport) { + mi = tmi; + break; + } + } + spin_unlock_bh(&ovpn_lock); + if (!mi) { + goto out; + } + + skb_pull(skb, sizeof(struct udphdr)); + + /* decrypt + * 很显然,这是关键!数据解密! + * 但是谁能告诉我内核中怎么高效使用加解密,如果不能高效, + * 那么起码保证灵活,就像OpenSSL那样!进入了内核态,我突然 + * 突然想到了OpenSSL的好,人,不能忘本啊 :< + */ + + /* 首先,判断是否是数据通道,进行例行检查,获取必要的密钥套件 */ + if (ovpn_pre_endecrypt(OVPN_OPT_DEC, tun, skb, mi)) { + skb_push(skb, sizeof(struct udphdr)); + goto out; + } + + /* 实际的解密操作,注意在内部可能要进行skb的realloc操作 */ + if (ovpn_endecrypt(OVPN_OPT_DEC, tun, skb, mi)) { + skb_push(skb, sizeof(struct udphdr)); + goto out; + } + + /* 参考OpenVPN的post decrypt操作 */ + if (ovpn_post_endecrypt(OVPN_OPT_DEC, tun, skb, mi)) { + skb_push(skb, sizeof(struct udphdr)); + goto out; + } + + /* 解密完成,推进一个OpenVPN头的长度 */ + skb_pull(skb, sizeof(struct ovpnhdr)); + switch (tun->flags & TUN_TYPE_MASK) { + case TUN_TUN_DEV: + switch (skb->data[0] & 0xf0) { + /* 当前只支持IPv4 */ + case 0x40: + break; + default: + skb_push(skb, sizeof(struct ovpnhdr)); + skb_push(skb, sizeof(struct udphdr)); + goto out; + + } + skb_reset_mac_header(skb); + /* 是时候丢掉西装外衣了,口袋里的通行证会将你引入深渊, + * 不信的话,注释此言,在OpenVPN客户端机器上ping一下 + * 服务端的虚拟IP试一试 + **/ + skb_dst_drop(skb); + skb->protocol = htons(ETH_P_IP);; + skb->dev = tun->dev; + ret = UDP_DECAP_STOLEN; + break; + case TUN_TAP_DEV: + // TODO + goto out; + break; + } + /* 模拟TUN虚拟网卡接收,此时截获处理正式完成, + * 告诉UDP,嗨,你的数据我已经帮你处理了 + **/ + netif_rx_ni(skb); + +out: + return ret; +} + +/* + * 封装UDP + * 本来想直接调用socket的sendto/sendmsg的,然而太过恶心与繁琐,加之需要skb和msg之间的拷贝 + * 为了省事而影响效率这样不值!还是自己封装吧,反正也不难 + **/ +static int encap_udp(struct sk_buff *skb, struct multi_instance *mi, unsigned int *pdlen) +{ + struct udphdr *uh; + struct inet_sock *inet = inet_sk(mi->sk); + int len = *pdlen + sizeof(struct udphdr); + + skb_push(skb, sizeof(struct udphdr)); + skb_reset_transport_header(skb); + + uh = udp_hdr(skb); + uh->source = htons(inet->num); + uh->dest = mi->dport; + uh->len = htons(len); + uh->check = 0; + + /* 注意这里有优化空间,ufo是否启用,硬件是否能帮我计算checksum呢?? */ + uh->check = 0; + uh->check = csum_tcpudp_magic(mi->real_saddr, mi->real_daddr, len, + mi->sk->sk_protocol, csum_partial(uh, + len, + 0)); + + return 0; +} + +/* + * IP层的封装与发送函数,注意,这里很不方便使用ip_queue_xmit + **/ +static int encap_ip_xmit(struct sk_buff *skb, struct multi_instance *mi, struct iphdr *old) +{ + struct iphdr *iph; + struct dst_entry *dst; + + skb_push(skb, sizeof(struct iphdr)); + /* 如影随形 */ + skb_reset_network_header(skb); + + iph = ip_hdr(skb); + iph->version = 4; + iph->ihl = sizeof(struct iphdr)>>2; + iph->frag_off = old->frag_off; + iph->protocol = IPPROTO_UDP; + iph->tos = old->tos; + iph->daddr = mi->real_daddr; + iph->saddr = mi->real_saddr; + iph->ttl = old->ttl; + /* 这个reroute频繁用于OUTPUT Netfilter HOOK,但问Rusty本人, + * Netfilter的OUTPUT设计为何如何之好 */ + if (ip_route_me_harder(skb, RTN_LOCAL)!= 0) { + return -1; + } + dst = skb_dst(skb); + + ip_select_ident(iph, dst, NULL); + return ip_local_out(skb); +} + + +static int encap_ovpn(struct sk_buff *skb, struct multi_instance *mi, int *pdlen) +{ + struct tun_struct *tun; + int ret = 0; + + + if (!mi) { + ret = -1; + goto out; + } + + tun = mi->sk->sk_user_data; + if (!tun) { + ret = -1; + goto out; + } + + /* encrypt + * 很显然,这是关键!数据解密! + * 但是谁能告诉我内核中怎么高效使用加解密,如果不能高效, + * 那么起码保证灵活,就像OpenSSL那样!进入了内核态,我突然 + * 突然想到了OpenSSL的好,人,不能忘本啊 :< + */ + + /* 首先,判断是否是数据通道,进行例行检查,获取必要的密钥套件 */ + if (ovpn_pre_endecrypt(OVPN_OPT_ENC, tun, skb, mi)) { + ret = -1; + goto out; + } + + /* 实际的解密操作,注意在内部可能要进行skb的realloc操作 */ + if (ovpn_endecrypt(OVPN_OPT_ENC, tun, skb, mi)) { + ret = -1; + goto out; + } + + /* 如影随形 */ + skb_push(skb, sizeof(struct ovpnhdr)); + *pdlen += sizeof(struct ovpnhdr); + + /* 参考OpenVPN的post decrypt操作 */ + if (ovpn_post_endecrypt(OVPN_OPT_ENC, tun, skb, mi)) { + ret = -1; + skb_pull(skb, sizeof(struct ovpnhdr)); + goto out; + } + +out: + return ret; +} + +/* + * hard_xmit中的封装函数,用于短路处理 + **/ +static int ovpn_data_channel_encap_xmit(struct tun_struct *tun, struct sk_buff *skb) +{ + unsigned int max_headroom; + int ret = 0; + struct sock *sk; + struct multi_instance *mi = NULL; + struct hlist_node *node; + struct iphdr *old_iphdr = NULL; + unsigned int dlen = skb->len; + + sk = tun->encap_sock; + if (!sk) { + ret = -1; + goto out; + } + if (sk->sk_protocol != IPPROTO_UDP) { + ret = -1; + goto out; + } + +#define I_THINK_THIS_LENGTH_ENOUGH_BECAUSE_OF_XXX 40 + max_headroom = (I_THINK_THIS_LENGTH_ENOUGH_BECAUSE_OF_XXX + + LL_RESERVED_SPACE(tun->dev) + + sizeof(struct iphdr) + + sizeof(struct udphdr) + + sizeof(struct ovpnhdr)); + + switch (tun->flags & TUN_TYPE_MASK){ + case TUN_TUN_DEV: + { + struct iphdr *hdr = ip_hdr(skb); + u32 addr = hdr->daddr; + struct multi_instance *tmi; + unsigned int hash = jhash_1word(addr, 0); + + old_iphdr = hdr; + spin_lock_bh(&ovpn_lock); + hlist_for_each_entry(tmi, node, &tun->ctx.vhash[hash % MAX_HASH_BUCKETS], vhnode) { + if (addr == tmi->daddr) { + mi = tmi; + break; + } + } + spin_unlock_bh(&ovpn_lock); + } + break; + case TUN_TAP_DEV: + { + // TODO + ret = -1; + + } + break; + + } + if (!mi) { + ret = -1; + goto out; + } + if (skb_headroom(skb) < max_headroom || !skb_clone_writable(skb, 0)) { + struct sk_buff *new_skb = skb_realloc_headroom(skb, max_headroom); + if (!new_skb) { + ret = -1; + goto out; + } + skb_dst_set(new_skb, skb_dst(skb)); + + dev_kfree_skb(skb); + skb = new_skb; + } + + if (encap_ovpn(skb, mi, &dlen)) { + ret = 1; + dev_kfree_skb(skb); + goto out; + } + + if (encap_udp(skb, mi, &dlen)) { + dev_kfree_skb(skb); + ret = 1; + goto out; + } + /* GO AWAY?? 注意返回值转换 */ + ret = encap_ip_xmit(skb, mi, old_iphdr); + if (ret < 0) { + ret = 1; + } +out: + return ret; +} + static inline struct tun_sock *tun_sk(struct sock *sk) { return container_of(sk, struct tun_sock, sk); @@ -155,8 +851,38 @@ static void __tun_detach(struct tun_struct *tun) { + struct sock *sk; /* Detach from net device */ netif_tx_lock_bh(tun->dev); + /**/ + sk = tun->encap_sock; + if (sk) { + int i; + /* 重置操作 */ + (udp_sk(sk))->encap_type = 0; + (udp_sk(sk))->encap_rcv = NULL; + sk->sk_user_data = NULL; + tun->encap_sock = NULL; + tun->ctx.encap_xmit = NULL; + for (i = 0; i < MAX_HASH_BUCKETS; i++) { + struct multi_instance *mi; + struct hlist_head *head; + struct hlist_node *node, *tmp; + head = &tun->ctx.hash[i]; + hlist_for_each_entry_safe(mi, node, tmp, head, rhnode) { + hlist_del(node); + hlist_del(&mi->vhnode); + if (mi->mi_destroy) { + mi->mi_destroy(mi/* THIS ? self ? Okey,thinking in JAVA */); + } + kfree(mi); + } + } + /* 这里才减少引用计数!因为你并不晓得且不能假设tun和socket的关闭顺序 */ + if (sk) { + sockfd_put(sk->sk_socket); + } + } tun->tfile = NULL; netif_tx_unlock_bh(tun->dev); @@ -364,6 +1090,21 @@ if (!check_filter(&tun->txflt, skb)) goto drop; + /* ?? */ + if (tun->ctx.encap_xmit) { + + int ret = tun->ctx.encap_xmit(tun/*this就是那个叫做JAVA编程思想的!GEB之大成*/, skb); + /* Is this Okay?I don‘t known */ + /* Refer to the return value of UDP encap_rcv callback!*/ + if (ret == 0) { + /* encap_xmit drop skb*/ + goto out; + } else if (ret > 0) { + goto out; + } + /* fall through */ + } + if (skb_queue_len(&tun->socket.sk->sk_receive_queue) >= dev->tx_queue_len) { if (!(tun->flags & TUN_ONE_QUEUE)) { /* Normal queueing mode. */ @@ -393,6 +1134,7 @@ drop: dev->stats.tx_dropped++; kfree_skb(skb); +out: return NETDEV_TX_OK; } @@ -467,6 +1209,7 @@ dev->tx_queue_len = TUN_READQ_SIZE; /* We prefer our own queue length */ break; } + dev->priv_flags &= ~IFF_XMIT_DST_RELEASE; } /* Character device part */ @@ -1140,7 +1883,7 @@ if (cmd == TUNSETIFF && !tun) { ifr.ifr_name[IFNAMSIZ-1] = ‘\0‘; - ret = (tfile->net, file, &ifr); + ret = tun_set_iff(tfile->net, file, &ifr); if (ret) goto unlock; @@ -1158,6 +1901,98 @@ ret = 0; switch (cmd) { + /* 这里的几个命令都是OpenVPN相关的 */ + /* 但是我并不知道怎么将这些独立出去!*/ + case TUNADDMILTI: + { + struct instance_req ir; + if (copy_from_user(&ir, argp, sizeof(ir))) { + ret = -EFAULT; + break; + } + if (!ovpn_add_real_instance(tun, ir.real_addr, ir.port)) { + ret = -EFAULT; + break; + } + } + break; + case TUNSETMIVIP: + { + struct instance_vreq vir; + if (copy_from_user(&vir, argp, sizeof(vir))) { + ret = -EFAULT; + break; + } + ovpn_add_virtual_instance(tun, vir.real_addr, vir.port, vir.vaddr); + } + break; + case TUNDELMILTI: + { + struct instance_req ir; + if (copy_from_user(&ir, argp, sizeof(ir))) { + ret = -EFAULT; + break; + } + ovpn_del_real_instance(tun, ir.real_addr, ir.port); + } + break; + case TUNSETMKEY: + { + struct key_block *kb; + /* 这里为何非要不在栈上分配呢? + * 因为这里是内核,内核栈的大小是有限的,鉴于kb空间较大 + * 因此采用了动态分配,用后释放 + **/ + kb = kmalloc(sizeof(struct key_block), GFP_KERNEL); + if (!kb) { + ret = -ENOMEM; + break; + } + if (copy_from_user(kb, argp, sizeof(kb))) { + ret = -EFAULT; + break; + } + // TODO waht? find_set_key(tun, kb); + kfree(kb); + } + break; + case TUNGETMKEY: + // TODO + break; + case TUNLINKOVPN: + { + struct sockfd sfd; + struct socket *sock; + struct sock *sk; + int err; + int i; + if (copy_from_user(&sfd, argp, sizeof(sfd))) { + ret = -EFAULT; + break; + } + sock = sockfd_lookup(sfd.fd, &err); + if (!sock) { + ret = -EFAULT; + break; + } + sk = sock->sk; + if (sk->sk_protocol != IPPROTO_UDP) { + ret = -EFAULT; + break; + } + (udp_sk(sk))->encap_type = UDP_ENCAP_OVPN; + (udp_sk(sk))->encap_rcv = ovpn_data_channel_decap_recv; + /* link tun and sock ?? */ + tun->encap_sock = sk; + sk->sk_user_data = tun; + tun->ctx.encap_xmit = ovpn_data_channel_encap_xmit; + for (i = 0; i < MAX_HASH_BUCKETS; i++) { + INIT_HLIST_HEAD(&tun->ctx.hash[i]); + INIT_HLIST_HEAD(&tun->ctx.vhash[i]); + } + } + break; + case TUNGETIFF: ret = tun_get_iff(current->nsproxy->net_ns, tun, &ifr); if (ret)
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。