Open×××以及其它IP层×××的完全链路层处理的实现
我们知道Open×××是一个用户态的程序,靠字符设备接收从虚拟网卡进来的以太帧或者IP数据报后,作为套接字buffer从本地发送出去,对于返回的数据,写入字符设备,模拟虚拟网卡接收动作,然后再通过路由结果发送数据到下一跳,这一切的重中之重就是路由,也就是说,如果你部署了一个基于Open×××的×××,那么该×××上配置路由是在所难免的,而我们知道,路由配置基本就是一个体力活儿,不难,但是一旦弄错,后果很严重,于是避免配置路由就成了一个重要需求,能不能利用一种机制,让系统自动识别下一跳呢?在回答这个问题之前,我们必须弄明白“下一跳”这个概念的本质,它有两个层面的含义:
1.它将IP数据报继续推向目标,因为IP数据报是逐跳发送的;
2.它为链路层实际发送数据提供了一个依据。
如果我们不考虑虚拟的东西,仅仅从实际发送数据这个角度来看,对于以太网,所谓的下一跳的作用仅仅就是获得一个目标MAC地址而已,然后链路层以该MAC地址作为目标MAC,之后将数据包仍出去就完事了。
到此,一切变得简单了。我们能在original方向获得一个数据帧的源MAC,将其缓存在ip_conntrack结构中,然后对于reply的数据,用该缓存的MAC作为目标MAC封装数据帧,之后直接发送,不再经过路由层。这个设想由于Netfilter的存在使其实际实现变得简单。
先来看一个图,图示上给出了一个实际的需求:

××× endpoint已经做成了网桥,既然做成了网桥,当然是不希望在它上面配置路由了,那么就需要程序可以自动存储和识别要发送数据帧的目标MAC地址,然后直接用该MAC封装数据,以下的代码实现了这一点:
/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include <linux/types.h> #include <linux/netfilter.h> #include <linux/module.h> #include <linux/sysctl.h> #include <net/dst.h> #include <net/netfilter/nf_conntrack.h> #include <net/netfilter/nf_conntrack_core.h> struct gwinfo { //自动保存回复帧的目标MAC地址 unsigned char reply_gw_mac[ETH_ALEN]; //自动保存正向帧的原始目标MAC地址 unsigned char orig_gw_mac[ETH_ALEN]; unsigned int local_flag; //自动保存设备 struct net_device *dev; }; static struct nf_ct_ext_type sggw_extend __read_mostly = { .len = sizeof(struct gwinfo), .align = __alignof__(struct gwinfo), .id = NF_CT_EXT_SGGW, }; struct gwmark { __u32 reply_mark; __u32 orig_mark; }; struct gwmark g_mark = {0x32, 0x32}; static unsigned int gwmark_target(struct sk_buff *skb, const struct xt_target_param *par) { struct gwmark *gm = (struct gwmark*)par->targinfo; g_mark.reply_mark = gm->reply_mark; g_mark.orig_mark = gm->orig_mark; return NF_ACCEPT; } static bool gwmark_check(const struct xt_target_param *par) { //TODO return true; } static struct xt_target gwmark_tg __read_mostly = { .name = "GWMARK", .family = NFPROTO_IPV4, .target = gwmark_target, .targetsize = sizeof(struct gwmark), .table = "mangle", .hooks = (1 << NF_INET_PRE_ROUTING) | (1 << NF_INET_LOCAL_OUT), .checkentry = gwmark_check, .me = THIS_MODULE, }; static unsigned int ipv4_conntrack_get(unsigned int hooknum, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { enum ip_conntrack_info ctinfo; struct gwinfo *ginfo = NULL; struct nf_conn *ct = nf_ct_get(skb, &ctinfo); if (!ct) { return NF_ACCEPT; } //仅仅针对正方向的包进行ginfo管理 if (CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL || ctinfo == IP_CT_NEW) { //如果是一个流的头包,那么设置该流的ginfo if(ctinfo == IP_CT_NEW) { ginfo = nf_ct_ext_add(ct, NF_CT_EXT_SGGW, GFP_ATOMIC); } else { //否则直接取出 ginfo = nf_ct_ext_find(ct, NF_CT_EXT_SGGW); } if (ginfo) { struct ethhdr *eth = (struct ethhdr*)(skb->data - ETH_HLEN); //保存数据包的源MAC地址,用以自动封装返回包的目标MAC memcpy(ginfo->reply_gw_mac, eth->h_source, ETH_ALEN); //保存数据包的目标,以求在数据被用户态(比如Open×××)重新封装后直接封装目标MAC地址 memcpy(ginfo->orig_gw_mac, eth->h_dest, ETH_ALEN); //保存设备变量,因为想直接发送一个数据而不经路由,必须指定一个设备 ginfo->dev = skb->dev; //默认数据不是本地用户态发出的 ginfo->local_flag = 0; } } return NF_ACCEPT; } //这个HOOK函数没什么大不了的,旨在设置local标志 static unsigned int ipv4_conntrack_get_local(unsigned int hooknum, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { struct gwinfo *ginfo = NULL; enum ip_conntrack_info ctinfo; struct nf_conn *ct = nf_ct_get(skb, &ctinfo); if (!ct) { return NF_ACCEPT; } ipv4_conntrack_get(hooknum, skb, in, out, okfn); if (CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL || ctinfo == IP_CT_NEW) { ginfo = nf_ct_ext_find(ct, NF_CT_EXT_SGGW); if (ginfo) { ginfo->local_flag = 1; } } return NF_ACCEPT; } //以下的HOOK函数实现自动封装以太帧而不经路由的逻辑 static unsigned int ipv4_conntrack_set(unsigned int hooknum, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { enum ip_conntrack_info ctinfo; struct gwinfo *ginfo = NULL; struct net_device *dev = NULL; unsigned int local = 0; struct nf_conn *ct = nf_ct_get(skb, &ctinfo); if (skb->mark == g_mark.reply_mark && CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY && hooknum == NF_INET_PRE_ROUTING) { ginfo = nf_ct_ext_find(ct, NF_CT_EXT_SGGW); if (ginfo) { dev = ginfo->dev; } } else if (skb->mark == g_mark.orig_mark && CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL && hooknum == NF_INET_LOCAL_OUT) { ginfo = nf_ct_ext_find(ct, NF_CT_EXT_SGGW); if (ginfo) { struct dst_entry * dst = skb_dst(skb); dev = (struct net_device*)dst->dev; local = 1; } } if (dev) { //这里的逻辑再明显不过了,直接在PREROUTING这个HOOK点上执行XMIT... struct ethhdr *eth = (struct ethhdr*)skb->data - ETH_HLEN; if (local) { memcpy(eth->h_dest, ginfo->orig_gw_mac, ETH_ALEN); } else { memcpy(eth->h_dest, ginfo->reply_gw_mac, ETH_ALEN); } skb->dev = dev; dev_queue_xmit(skb); return NF_STOLEN; } return NF_ACCEPT; } static struct nf_hook_ops ipv4_conntrack_gwops[] __read_mostly = { { .hook = ipv4_conntrack_get, .owner = THIS_MODULE, .pf = NFPROTO_IPV4, .hooknum = NF_INET_PRE_ROUTING, .priority = NF_IP_PRI_CONNTRACK+1, }, { .hook = ipv4_conntrack_get_local, .owner = THIS_MODULE, .pf = NFPROTO_IPV4, .hooknum = NF_INET_LOCAL_OUT, .priority = NF_IP_PRI_CONNTRACK+1, }, { .hook = ipv4_conntrack_set, .owner = THIS_MODULE, .pf = NFPROTO_IPV4, .hooknum = NF_INET_PRE_ROUTING, .priority = NF_IP_PRI_MANGLE+1, }, }; //init函数为例行注册 static int __init nf_conntrack_sggw_init(void) { int ret = 0; ret = nf_register_hooks(ipv4_conntrack_gwops, ARRAY_SIZE(ipv4_conntrack_gwops)); if (ret < 0) { printk("nf_conntrack_ipv4: can't register gw hooks.\n"); } ret = nf_ct_extend_register(&sggw_extend); //注册 if (ret < 0) { printk(KERN_ERR "sggw: Unable to register extension\n"); return ret; } ret = xt_register_target(&gwmark_tg); if (ret < 0) { printk(KERN_ERR "sggw: Unable to register target\n"); return ret; } return ret; } //fini函数为例行解注册 static void __exit nf_conntrack_sggw_fini(void) { synchronize_net(); nf_unregister_hooks(ipv4_conntrack_gwops, ARRAY_SIZE(ipv4_conntrack_gwops)); nf_ct_extend_unregister(&sggw_extend); xt_unregister_target(&gwmark_tg); } module_init(nf_conntrack_sggw_init); module_exit(nf_conntrack_sggw_fini); MODULE_ALIAS("sggw"); MODULE_LICENSE("GPL");
除了以上的内核模块代码,还需要一个iptables程序,用以设置两个mark,该用户态模块我没有写,而是使用了别的方式达到了目标。近期实在太忙了,做事不再有始有终...以上的模块自己测试了一下,运行得还不错,不过有一点我没有考虑到,那就是以上的代码没有实现任何的MAC地址变更的Notify机制,因此只适合于物理位置稳定的以太网环境,遇到上游路由器的热备切换,可能会有问题,然而如果热备组中的它们共享一个MAC地址,那敢情好了,可是VRRP/HSRP志不在此!!
在产品实施中受辱,一时难以释怀,近期志在实现基于Open×××的传输模式的×××,任何人可以侮辱我这个人,然而不能侮辱我的产品!
转载于:https://blog.51cto.com/dog250/1268892
相关文章:

Windows 7 64位机上OpenCV2.4.3的编译、安装与配置
1. 从http://sourceforge.net/projects/opencvlibrary/files/opencv-win/2.4.3/下载OpenCV2.4.3; 2. 将OpenCV-2.4.3.exe放到D:\soft\OpenCV2.4.3文件夹下,解压到当前文件夹下,生成一个opencv文件夹; 3. 下载并安…

有望替代卷积神经网络?微软最新研究提基于关系网络的视觉建模
导语:最近两年,自注意力机制、图和关系网络等模型在NLP领域刮起了一阵旋风,基于这些模型的Transformer、BERT、MASS等框架已逐渐成为NLP的主流方法。这些模型在计算机视觉领域是否能同样有用呢?近日,微软亚洲研究院视觉…

Word 2013无法发布文章到博客园
2018年12月12日突然发现word2013无法发布文章到博客园了, 虽然不常发布博客, 但作为一个强迫症患者, 不折腾好了, 吃肉都不香呀! 删除之前的账户, 想重新注册, 居然遇到了灰色对话框!…

1 sec on Large Judge (java): https://github.com/l...
1 sec on Large Judge (java): https://github.com/leoyonn/leetcode/blob/master/src/q029_substring_of_all_words/Solution.java转载于:https://www.cnblogs.com/codingtmd/archive/2013/03/31/5079017.html

性能提升3倍的树莓派4,被爆设计缺陷!
整理 | 屠敏转载自CSDN(ID:CSDNnews)一直以来,素有世界最小电脑之称的 Raspberry Pi(树莓派)是一种独特的存在。它不仅只有一块信用卡般的体积,还具备主机电脑所具备的功能,如运行 L…

Windows7 64位机上Emgu CV2.4.2安装与配置
1. 从http://sourceforge.net/projects/emgucv/?sourcedirectory下载最新的Emgu CV2.4.2; 2. 将libemgucv-windows-x86-gpu-2.4.2.1777拷贝到D:\soft\Emgu2.4.2文件夹下,运行此.exe文件,将其安装到D:\soft\Emgu2.4.2\emgucv-wind…

2018年12月,华为HCNP大面积更新题目,军哥独家解题咯
2018年12月,华为HCNP大面积更新题目,乾颐堂军哥独家解题咯2018年是华为认证变动比较大的一年,华为认证走过这几年不得不说是有一定进步的,而且最近华为孟女侠确实让我也小小的骄傲了一把,所以当然希望华为认证能做的更…

关于ProGuard的学习了解(从别处转来)
from:http://www.cnitblog.com/zouzheng/archive/2011/01/12/72639.html在Android项目中用到JNI,当用了proguard后,发现native方法找不到很多变量,原来是被produard优化掉了。所以,在JNI应用中该慎用progurad啊。解决办…

tesseract-ocr3.02字符识别过程操作步骤
1、 从http://code.google.com/p/tesseract-ocr/downloads/list下载tesseract-ocr-3.02-vs2008、tesseract-ocr-3.02.chi_sim.tar、tesseract-ocr-3.02.02.tar、tesseract-ocr-3.02.02-doc-html.tar、leptonica-1.68-win32-lib-include-dirs相关文件; 2、 将所有…

中文repo“霸榜”GitHub Trending,国外开发者不开心了
编译整理 | 一一出品 | AI科技大本营(ID:rgznai100)近日,一位叫Balazs Saros 的国外开发者在Medium上发表了一篇名为"Chinese repos are ruining the Github trending page"的博文,翻译一下他的意思就是“中文 repo 正在…

使用 electron-updater 自动更新应用
前端工程师可以使用 Electron 非常方便的编写出 PC 端应用,而应用更新的方式也有很多,详细可见更新应用程序。 我的项目是基于 electron-vue 搭建的,构建打包生成安装包,则用的是 electron-builder,所以更新自然选择 e…

struts2请求处理过程源代码分析(1)
2019独角兽企业重金招聘Python工程师标准>>> 转载自:http://www.see-source.com/ 源码解析网 网上对于struts2请求处理流程的讲解还是比较多的,有的还是非常详细的,所以这里我就简单地将大概流程总结下,有了个大概印象…

Ubuntu中C代码静态检查工具Splint的安装配置和使用
1、 从http://www.splint.org/download.html下载splint-3.1.2.src.tgz,存放到/home/spring/Splint文件夹下; 2、 打开终端; 3、 解压缩:tar zxvfsplint-3.1.2.src.tgz 4、 安装到/usr/local/splint目录下: …

Fetch 入门
一、什么是Fetch ? Fetch的定义 Fetch本质上是一种标准,该标准定义了请求、响应和绑定的流程。 Fetch标准还定义了Fetch () JavaScript API,它在相当低的抽象级别上公开了大部分网络功能,我们今天讲的主要是Fetch API。Fetch API …

保障数据安全,强调科技向善,旷视发布《人工智能应用准则》
目录 AI应用落地加速 善用科技是关键 《人工智能应用准则》全文 2019年7月17日,旷视正式全文公布基于企业自身管理标准的《人工智能应用准则》(以下简称《准则》)。《准则》从正当性、人的监督、技术可靠性和安全性、公平和多样性、问责和及…

胜者树和败者树 - qianye0905 - 博客园
胜者树和败者树 - qianye0905 - 博客园胜者树和败者树胜者树和败者树都是完全二叉树,是树形选择排序的一种变型。每个叶子结点相当于一个选手,每个中间结点相当于一场比赛,每一层相当于一轮比赛。不同的是,胜者树的中间结点记录的…

C/C++代码静态检查工具PC-lint在VS2008开发环境中的安装配置和使用
PC-Lint偏重于代码的逻辑分析,它能够发现代码中潜在的错误,比如数组访问越界、内存泄漏、使用未初始化变量等。 1、 从http://download.csdn.net/detail/liuchang5/3005191 下载破解版PC-lint9.0; 2、 解压缩到D:\soft\PC-lint,…
k8s使用kube-router网络插件并监控流量状态
简介 kube-router是一个新的k8s的网络插件,使用lvs做服务的代理及负 载均衡,使用iptables来做网络的隔离策略。部署简单,只需要在每个节点部署一个daemonset即可,高性能,易维护。支持pod间通信,以及服务的代…

作业盒子完成1.5亿美元D轮融资,更名“小盒科技”
作者 | 夕颜 导读:2019 年 7 月 18 日,AI 在线教育创企“作业盒子”召开发布会,宣布已于今年 5 月完成 1.5 亿美元 D 轮融资,由阿里巴巴领投。同时,“作业盒子”宣布进行品牌升级,正式更名为“小盒科技”&a…

8500WN流畅高速上网高端卡 12核心不锁倍频
据台湾媒体最新报道,台湾无线网卡厂商最新推出一款大功率80DBI无线网卡-横空出世8500WN集成机。售价约1180新台币(折合人民币约298元) 台湾卡王是全球著名的大功率无线网卡生产厂商,2007年曾最早推出大功率无线网卡8G,以其卓越的品质…

Fiddler抓包工具总结(转)
序章 Fiddler是一个蛮好用的抓包工具,可以将网络传输发送与接受的数据包进行截获、重发、编辑、转存等操作。也可以用来检测网络安全。反正好处多多,举之不尽呀!当年学习的时候也蛮费劲,一些蛮实用隐藏的小功能用了之后就忘记了&a…

Windows 64位机上C/C++代码静态检查工具Logiscope RuleChecker的安装和使用
1、 从http://download.csdn.net/detail/zmywly/3611820 和 http://download.csdn.net/detail/zmywly/3611854下载破解版; 2、 将文件解压缩到D:\soft\logiScope文件夹下,会生成一个logiScope[6.1.30]文件夹; 3、 双击D:\soft\lo…

作业盒子完成1.5亿美元D轮融资,用AI普及教育资源
作者 | 夕颜出品 | AI科技大本营(ID:rgznai100)导读:2019 年 7 月 18 日,AI 在线教育创企“作业盒子”召开发布会,宣布已于今年 5 月完成 1.5 亿美元 D 轮融资,由阿里巴巴领投。同时,“作业盒子…

迭代器接口IteratorAggregate 与类 ArrayIterator(转)
也许你很想使用foreach来遍历一个类中的属性,然而你却没有很好的方式来这么做。可能使用PHP中class的操作的方式能够帮助你实现一些,但是现在我想你有了更好的方式。通过继承接口:IteratorAggregate来实现。 示例 [php] view plaincopy <?…

整理《Mastering OpenCV with Practical Computer Vision Projects》中第8章用Eigenfaces或Fisherfaces进行人脸识别操作流程
These generally involve four main steps:(1)、Face detection;(2)、Face preprocessing;(3)、Collect and learn faces;(4)、Face recognition. 一、Face detection(Haar-based、LBP-based) LBP-based detectors are potential…

性能比GPU高100倍!华人教授研发全球首个可编程忆阻器AI计算机
译者 | 陆离责编 | 夕颜出品 | AI科技大本营(ID:rgznai100)导读:近日,密歇根大学研发成功第一台可编程的忆阻器计算机,它不仅是一个通过外部计算机运行的忆阻器阵列,而且还是可以在智能手机等小型设备上进行…

深入解析redis cluster gossip机制
社区版redis cluster是一个P2P无中心节点的集群架构,依靠gossip协议传播协同自动化修复集群的状态。本文将深入redis cluster gossip协议的细节,剖析redis cluster gossip协议机制如何运转。协议解析 cluster gossip协议定义在在ClusterMsg这个结构中&am…

Python 3.8即将发布,这几个变化你必须知道
作者 | Jake Edge译者 | Rachel出品 | Python大本营(ID:pythonnews)【导读】近日, Python 3.8.0b1 版本和 beta 版相继发布, Python 3.8 版的新特征已经基本成型。尽管最终版本的官方发布时间为 10 月,但实际上&#x…

Ubuntu 12.10 拨号上网及停用方法
2019独角兽企业重金招聘Python工程师标准>>> 本人电脑在设置了拨号上网过后,本来原先插上网线就能上网,现在反而不能上了,在网上找了些办法,再进行了些修改,最后成功了,现在把我的方法给大家分享…

011:视图函数介绍
视图: 视图一般都写在 app 的 views.py 中。并且视图的第一个参数永远都是 request (一个HttpRequest)对象。这个对象存储了请求过来的所有信息,包括携带的参数以及一些头部信息等。在视图中,一般是完成逻辑相关的操作…