一夜入冬啊一夜入冬,医院回来的路上已经困得不行了,不过还是仔细思考了我的Open×××内核版。和那些天天在微信朋友圈晒工作的相比我简直弱爆了,我 本应该说今天没有去上班,然后心里多么多么内疚的,什么耽误工作之类的,不过那不是我的style,就像我同样不喜欢在工位上贴大量的任务计划一样...
把本已不堪的OpenSSL移植到Linux Kernel,这真是一个疯狂的想法,极其疯狂的想法,过于疯狂的想法,以至于作为一个还算正常的人只好作罢!我需要的是使用现有的内核中的加密引擎或者 简单的扩展它,抑或稍微复杂的重写它..总之,我需要的是一套内核态的加密算法引擎,crypto模块中的crypto_alg不错,试试看吧。
Open×××的数据通道和控制通道是分离的,这就使数据通道的处理进入内核成为了可能,当初接触Open×××的时候,我总是在想为什么Open××× 不使用SSL加密通道来封装Open×××协议呢?是的,技术上的解释太多了,但同时也太苍白了,如果真的使用了SSL通道,那么我就不可能做今天的工作 了,再次感谢James Yonan!在内核态处理Open×××数据通道是及其合理的,这并不是因为我对内核比较熟悉才故意这么说,而是真理。说白了Open×××的协议不就是 和传输层协议一样吗?若不是它当初构建于用户态的话,Open×××数据通道协议不就和ESP/AH处在同一个层次吗?而Open×××控制协议难道不就 是和IKE差不多吗?
我需要做的仅仅就是在内核注册一个协议处理而已,为了兼容既有的协议,我使用了UDP的encap_rcv钩子,而没有直接将UDP干掉,这已经实现了, 请参看我的《Open×××的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本身并没有太大的关系,
+ * 只是想做一个简单的Open×××短路hack,仅此而已,我使用tun做修改是因为简单,
+ * 毕竟我只是需要将一个socket和tun联系起来,仅此而已,我需要做的就是短接
+ * UDP socket和tun网卡,仅此而已.... :)
+ *
+ * 数据通道进入内核的好处是显而易见的,多处理操作的效率由softirq分发系统决定,
+ * 而这个是简单的,在8核心处理器上,经过测试,使用Intel 82583多队列卡,按照
+ * tuple做hash中断分发,保持cache活性的基础上,也能首先Open×××协议的高速解析,
+ * 任何用户态的多线程架构与之相比都爆弱。但是此时问题浮现:
+ *
+ * 1.不是说内核态处理控制面而用户态处理数据面吗?对于Open×××,怎么反过来了啊,
+ * 有点懵了!是的,数据面放到用户态只善作个幻象,现如今不是还没有很好的实例嘛...
+ * 我并非说用户态多线程不好,只是对Open×××而言的,不信你试试。好了,在PF RING
+ * 还玩不转的时候,我只能这样,也不容易。
+ * 2.这里没有使用加密,接口是有了,但是没有高效的实现,我可不想Open×××成为Yet
+ * Another IPSec
+ *
+ * 问题多多,marywangran@126.com,还是这个邮箱
+ *
+ **/
+
#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
+/* 定义一个Open×××封装类型 */
+#define UDP_ENCAP_O××× 20
+/* 连接一个UDP套接字和TUN网卡的ioctl命令 */
+#define TUNLINKO××× _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 O×××_OPT_DEC 0
+#define O×××_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作为一个例子的实现 */
+
+/*
+ * Open×××的常量定义,我是不是该准备一个头文件和C文件呢?
+ * 借用tun.c总不是什么长久之事!tun又不是只用于Open×××啊,
+ * 然而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(o***_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];
+};
+
+/*
+ * 用于实现Open×××的防重放机制
+ **/
+struct packet_id_send
+{
+ packet_id_type id;
+ time_t time;
+};
+
+/*
+ * 用于实现Open×××的防重放机制,但是天啊...里面的字段在协议移植阶段
+ * 是没有任何用武之地的,是的,没有用...
+ * */
+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;
+};
+
+/*
+ * 用于实现Open×××的防重放机制,目前的版本仅仅是为了例行公事,发送前
+ * 在Open×××头中封装一个递增的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 *);
+};
+
+/*
+ * 我的本意并不是移植Open×××,而是实现一个新的协议,but,but,but,but
+ * 苦于没有客户端,我为何不使用现成的Open×××呢??它的协议足够简单啊足够简单!
+ * */
+struct encap_context {
+ struct hlist_head hash[MAX_HASH_BUCKETS];
+ struct hlist_head vhash[MAX_HASH_BUCKETS];
+ /* 最终还是说服了自己,解除了Open×××和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);
+};
+
+/*
+ * 就是它!这就是Open×××协议的本质!瞧瞧看吧,你仅仅需要设置3个字段足矣!
+ * ocode:这个字段其实包含以下两个部分
+ * opt :很显然,我在内核中只处理数据通道,那么它是P_DATA_V1常量
+ * key_id :这个keyid用于切换密钥。目前使用定值0,即版本0.1不支持密钥重协商,
+ * 然则这只是个开始...
+ * id: 此字段用于封装将要发送的数据包的ID,防重放***
+ * 可见,关键的关键就是如何填充以下结构体的问题...对了,我可以说填充UDP头和IP头不是个事儿
+ * 吗?如果它们都成了事儿,还怎么好意思说自己比较喜欢折腾内核协议栈呢... :(
+ **/
+struct o***hdr {
+ 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 o***_destroy(struct multi_instance *mi)
+{
+ return;
+}
+
+/*
+ * 根据一个IP地址和端口删除一个multi_instance
+ **/
+static void o***_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(&o***_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(&o***_lock);
+ return ;
+ }
+ hlist_del(&mi->rhnode);
+ hlist_del(&mi->vhnode);
+ spin_unlock_bh(&o***_lock);
+ kfree(mi);
+}
+
+/*
+ * 添加一个multi_instance
+ **/
+static struct multi_instance *o***_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(&o***_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(&o***_lock);
+ return tmi;
+ }
+ }
+ ret = kzalloc(sizeof(struct multi_instance), GFP_ATOMIC);
+ if (!ret) {
+ spin_unlock_bh(&o***_lock);
+ return NULL;
+ }
+ ret->dport = port;
+ ret->real_daddr = real_addr;
+ ret->sk = tun->encap_sock;
+ ret->mi_destroy = o***_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(&o***_lock);
+ return ret;
+}
+
+/*
+ * 为一个multi_instance添加一个虚拟IP地址,这个本来应该实现成一个虚拟交换机的
+ * BUT对于TUN模式而言,我采用了替换模式,也就是说,我的这个版本并不支持iroute
+ * 不支持又怎么样呢?早晚的事吧。希望,真心希望James Yonan不要打我哦。。。
+ **/
+static int o***_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(&o***_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(&o***_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(&o***_lock);
+ return 0;
+}
+
+static int o***_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 == O×××_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 == O×××_OPT_ENC){
+
+ } else {
+ ret = -1;
+ goto out;
+ }
+out:
+ return ret;
+}
+
+static int o***_endecrypt(int mode,
+ struct tun_struct *tun,
+ struct sk_buff *skb,
+ struct multi_instance *mi)
+{
+
+ /* return tun->ctx.endecrypt(tun, skb); */
+ return 0;
+}
+
+struct o***hdr *o***_hdr(struct sk_buff *skb)
+{
+ return (struct o***hdr*)(skb->data);
+}
+
+static int o***_post_endecrypt(int mode,
+ struct tun_struct *tun,
+ struct sk_buff *skb,
+ struct multi_instance *mi)
+{
+ int ret = 0;
+ struct o***hdr *ohdr;
+ if (mode == O×××_OPT_ENC) {
+ ohdr = o***_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 == O×××_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 o***_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(&o***_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(&o***_lock);
+ if (!mi) {
+ goto out;
+ }
+
+ skb_pull(skb, sizeof(struct udphdr));
+
+ /* decrypt
+ * 很显然,这是关键!数据解密!
+ * 但是谁能告诉我内核中怎么高效使用加解密,如果不能高效,
+ * 那么起码保证灵活,就像OpenSSL那样!进入了内核态,我突然
+ * 突然想到了OpenSSL的好,人,不能忘本啊 :<
+ */
+
+ /* 首先,判断是否是数据通道,进行例行检查,获取必要的密钥套件 */
+ if (o***_pre_endecrypt(O×××_OPT_DEC, tun, skb, mi)) {
+ skb_push(skb, sizeof(struct udphdr));
+ goto out;
+ }
+
+ /* 实际的解密操作,注意在内部可能要进行skb的realloc操作 */
+ if (o***_endecrypt(O×××_OPT_DEC, tun, skb, mi)) {
+ skb_push(skb, sizeof(struct udphdr));
+ goto out;
+ }
+
+ /* 参考Open×××的post decrypt操作 */
+ if (o***_post_endecrypt(O×××_OPT_DEC, tun, skb, mi)) {
+ skb_push(skb, sizeof(struct udphdr));
+ goto out;
+ }
+
+ /* 解密完成,推进一个Open×××头的长度 */
+ skb_pull(skb, sizeof(struct o***hdr));
+ 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 o***hdr));
+ skb_push(skb, sizeof(struct udphdr));
+ goto out;
+
+ }
+ skb_reset_mac_header(skb);
+ /* 是时候丢掉西装外衣了,口袋里的通行证会将你引入深渊,
+ * 不信的话,注释此言,在Open×××客户端机器上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_o***(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 (o***_pre_endecrypt(O×××_OPT_ENC, tun, skb, mi)) {
+ ret = -1;
+ goto out;
+ }
+
+ /* 实际的解密操作,注意在内部可能要进行skb的realloc操作 */
+ if (o***_endecrypt(O×××_OPT_ENC, tun, skb, mi)) {
+ ret = -1;
+ goto out;
+ }
+
+ /* 如影随形 */
+ skb_push(skb, sizeof(struct o***hdr));
+ *pdlen += sizeof(struct o***hdr);
+
+ /* 参考Open×××的post decrypt操作 */
+ if (o***_post_endecrypt(O×××_OPT_ENC, tun, skb, mi)) {
+ ret = -1;
+ skb_pull(skb, sizeof(struct o***hdr));
+ goto out;
+ }
+
+out:
+ return ret;
+}
+
+/*
+ * hard_xmit中的封装函数,用于短路处理
+ **/
+static int o***_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 o***hdr));
+
+ 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(&o***_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(&o***_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_o***(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) {
+ /* 这里的几个命令都是Open×××相关的 */
+ /* 但是我并不知道怎么将这些独立出去!*/
+ case TUNADDMILTI:
+ {
+ struct instance_req ir;
+ if (copy_from_user(&ir, argp, sizeof(ir))) {
+ ret = -EFAULT;
+ break;
+ }
+ if (!o***_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;
+ }
+ o***_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;
+ }
+ o***_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 TUNLINKO×××:
+ {
+ 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_O×××;
+ (udp_sk(sk))->encap_rcv = o***_data_channel_decap_recv;
+ /* link tun and sock ?? */
+ tun->encap_sock = sk;
+ sk->sk_user_data = tun;
+ tun->ctx.encap_xmit = o***_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)
转载于:https://blog.51cto.com/dog250/1585189