最近有点走火入魔了!本文所用技术非标准,较真儿者慎入!!
一个局域网内,两台机器拥有同样的IP,可以吗?

这不就是IP地址冲突吗?当然不行!

可是要知道,如果搞点旁门左道,还是可以做到的!

首先要明白的是,IP数据报在以太网中的收发特征:

对于发送来讲:只要你有一个目标MAC供你封装成帧,就可以发出去,而这个MAC地址是由ARP来获取的;
对于接收来讲:只要收到帧的目标MAC是接收到帧的网卡的MAC地址,就可以正确接收!


现在我们逐步的来实现一个局域网内拥有同样IP还能正常越过默认网关访问不同外网的情景。实际上,在我实现的简版SDN中,一切都是保存在conntrack中的,它甚至可以保存一个流的上一跳和下一跳neighbour,这就意味着完全架空了本机的路由逻辑和arp逻辑!使本机完全退化成了一个switch!专注于数据包的转发!neighbour的定义是次要的,完全没有必要照抄Linux内核的neighbour结构体,它的实质就是一个MAC地址而已,思路很简单:

1.流头进来在正方向的PREROUTING中将其源MAC地址保存在conntrack;
2.属于同一流的数据包在反方向的POSTROUTING中获取conntrack中的MAC地址封装为目标MAC;
3.发送并返回STOLEN。


就是这么简单!实际上如果能在Linux中实现Policy ARP就好了,也就是说,可以为同一个IP地址映射多条ARP项,每个项有不同的MAC地址。毕竟neighbour是和dst_entry即路由表项相关联的,因此只要保证同一个IP地址的多个ARP映射属于不同的路由表项即可,而这很容易通过IPMARK+Policy Routing来实现!

        不幸的是,Linux并没有实现如此让人走火入魔的Policy ARP,现有的隔离措施就是使用NET命名空间,然而它是用于虚拟化的,同一个网卡只能属于同一个命名空间,此场景不适用!那么怎么办?只能自己写Netfilter代码了啊,还好,由于这个逻辑超级清晰,因此所做的修改也是很容易的,实现了这个的话,仰天长啸,嘲笑一下地址冲突!

        保存源MAC地址在ipv4_conntrack_in的最后来做,而还原保存的MAC地址到目标MAC地址的操作在ipv4_confirm中来做。代码如下:

//继续偷梁换柱借用nat的extend
struct nf_conn_nat {
//自动保存回复帧的目标MAC地址
unsigned char reply_gw_mac[ETH_ALEN];
//自动保存本机MAC地址
unsigned char orig_gw_mac[ETH_ALEN];
//自动保存设备
struct net_device *dev;
};
static unsigned int ipv4_conntrack_in(unsigned int hooknum,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
unsigned int ret = nf_conntrack_in(dev_net(in), PF_INET, hooknum, skb);
if (ret == NF_ACCEPT) {
enum ip_conntrack_info ctinfo;
struct nf_conn *ct;
struct nf_conn_nat *automac;
ct = nf_ct_get(skb, &ctinfo);
if (!ct) {
goto out;
}
if (skb->dev->flags & IFF_LOOPBACK)
return ret;
if (CTINFO2DIR(ctinfo) != IP_CT_DIR_ORIGINAL) {
goto out;
}
automac = nf_ct_ext_find(ct, NF_CT_EXT_NAT);
if (automac == NULL) {
automac = nf_ct_ext_add(ct, NF_CT_EXT_NAT, GFP_ATOMIC);
if (automac == NULL) {
goto out;
}
struct ethhdr *eth = (struct ethhdr*)(skb->data - ETH_HLEN);
memcpy(automac->reply_gw_mac, eth->h_source, ETH_ALEN);
memcpy(automac->orig_gw_mac, eth->h_dest, ETH_ALEN);
automac->dev = skb->dev;
}
} out:
return ret;;
} static unsigned int ipv4_confirm(unsigned int hooknum,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
.............
out:
/* We've seen it coming out the other side: confirm it */
ret = nf_conntrack_confirm(skb);
if (ret == NF_ACCEPT) {
struct nf_conn_nat *automac = NULL;
struct net_device *dev = NULL;
unsigned int hh_len = 0;
if (skb->dev->flags & IFF_LOOPBACK)
return ret;
if (CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL)
return ret;
if (hooknum != NF_INET_POST_ROUTING)
return ret;
automac = nf_ct_ext_find(ct, NF_CT_EXT_NAT);
if (automac == NULL)
return ret;
dev = automac->dev;
skb->dev = dev;
hh_len = LL_RESERVED_SPACE(dev);
if (unlikely(skb_headroom(skb) < hh_len && dev->header_ops)) {
struct sk_buff *skb2; skb2 = skb_realloc_headroom(skb, LL_RESERVED_SPACE(dev));
if (skb2 == NULL) {
kfree_skb(skb);
return -ENOMEM;
}
if (skb->sk)
skb_set_owner_w(skb2, skb->sk);
kfree_skb(skb);
skb = skb2;
}
//填充以太帧头,注意偏移!
char *header = skb->data - hh_len;
header[0] = 0x0;
header[1] = 0x0;
struct ethhdr *eth = (struct ethhdr*)(header+2);
memcpy(eth->h_dest, automac->reply_gw_mac, ETH_ALEN);
memcpy(eth->h_source, automac->orig_gw_mac, ETH_ALEN);
eth->h_proto = htons(ETH_P_IP);
skb_push(skb, ETH_HLEN);
skb->_skb_dst = NULL;
dev_queue_xmit(skb);
return NF_STOLEN;
}
return ret;
}

精妙之处在于,只要有一个包到达该BOX,其源MAC地址就能被conntrack记住,当反方向的包到来并要发回去的时候,就能取出该源MAC地址作为目标MAC地址封装到以太帧中,然后无需经由ARP层直接发送出去。现在的问题是,两个相同IP地址的主机可以同时发包过来吗?答案无疑是肯定的,因为它们俩发包前解析BOX的MAC地址,该BOX无疑全部回复,它们总有个先来后到,唯一的副作用时后到的那个ARP请求会更新BOX的ARP表,将相同的那个IP的MAC地址覆盖成自己的MAC地址,然而这无所谓了,因为既然两台主机都得到了BOX的MAC地址,数据包肯定能发出去到达BOX,返回包在寻址这两台机器的时候,并不通过ARP来寻址,而是通过这两台机器自己携带过去的源MAC地址来作为目标MAC的,故而覆盖也无所谓了!

        只要保证没有五元组的冲突即可,而这种冲突的可能性是极其小的。

        严格来讲ARP属于IP的下层,是和链路层接口的协议,它是”下一跳解析协议“的一种,适用于IPv4到以太网的适配,ARP只是动态的帮你完成了映射,如果你知道该如何封装以太帧,完全可以不用ARP,要么手工设置static的映射,要么就是从某处得到一个映射,关键点不在ARP或者映射,关键点在于你能得到一个MAC地址!只要你能得到目标MAC地址,那么封装以太帧即可,此时是不关心源MAC的!

        我真的走火入魔了,不管是用MAC桥微端口搞定了OpenVPN的源地址选择,还是构造假网关在配置Windows路由时指定源地址,还是本文说的IP地址冲突情景下的数据通信,都不是标准的做法,希望较真的同学注意。如果想靠这个理解网络的行为,那是再好不过了...

关键在封装并发出了帧-IP冲突也无所谓的更多相关文章

  1. 局域网内IP冲突怎么办

      对于在Internet和Intranet网络上,使用TCP/IP协议时每台主机必须具有独立的IP地址,有了IP地址的主机才能与网络上的其它主机进行通讯.但IP地址冲突会造成网络客户不能正常工作,只 ...

  2. 如何在linux下检测(自身)IP冲突

    最近遇到一个需求,或者说是一个用户现场问题. 我们设备先安装,设置dhcp模式获取ip进行联网,后来又安装了其他设备,但该设备是手动设置的静态ip地址,正好与我们设备冲突,造成网络故障. 那我们就需要 ...

  3. Alwayson的IP冲突

    Alwayson的IP冲突 https://social.technet.microsoft.com/Forums/office/en-US/4d50cb1c-eef7-4dcc-b937-3c8eb ...

  4. FFmpeg4.0笔记:封装ffmpeg的视频帧转换功能类CSws

    Github https://github.com/gongluck/FFmpeg4.0-study/tree/master/Cff CSws.h /************************* ...

  5. 今天连续几次被其他电脑客户端踢下线,也不知是否是ip冲突

    可能是利用ip然后我的网络也没有关掉共享,其次就是远程开启了一个叫做"学生端"的应用,好在我改密码连续两次改的快

  6. 解决局域网IP冲突

    进入cmd ipconfig -all 查看现有IP,发现IP不是192.168.1.*的形式,而是192.168.0.*等异常 ipconfig -release  释放现有IP ipconfig ...

  7. IP冲突解决方案

    客人在我所供职的酒店上网的时候,经常会弹出一个对话框,显示一些提示,如上网的注意事项和消费标准等信息;并且有自己的电影和歌曲服务器,DHCP-server也是其中的一台服务器,宾馆.酒店就是用这台机器 ...

  8. IP冲突如何把冲突的IP挤下去

    把冲突IP挤下去的方法: ①进入网络和共享中心>本地连接>禁用. ②进入网络和共享中心>更改适配器设置>双击被禁用的连接,自动重新连接即可.

  9. 昨天晚上也弄不清楚是自己密码被盗了还是由于ip冲突

    所以还是尽量要相信自己所见到的,今天上午是安卓课程,说实话,昨天晚上都是2:30睡的,现在硬是要把时间待这么晚才回去睡,是因为我想尽快入睡,昨天晚上就是眼睛都有点睁不开了,所以就睡得很快,但是早上也是 ...

随机推荐

  1. AJAX技术的核心

    //创建一个XMLHttpRequest对象 ,利用此对象与服务器进行通信 是AJAX技术的核心 /** * 获取XmlHttpRequest对象 */ function getXMLHttpRequ ...

  2. nodejs基础安装

    安装Nodejs需要从官网上下载一个最新的安装包,运行.我这里是win764位系统. 下载版本6.5.0 由于去外国的镜像上下载东西比较慢,淘宝为我们准备了国内的镜像.我们需要安装国内镜像的使用工具. ...

  3. Asp.Net MVC ajax调用 .net 类库问题

    如果你还在为 ajax 调用 .net 类库还束手无策的话,相信这篇博客将帮助你解决这个世纪问题! 因为Visual Studio 内置了asp.net mvc ,不过当你添加asp.net mvc项 ...

  4. chkconfig用法

    有时候为了方便管理,我们常常喜欢在Linux中将之安装为服务,然后就可以使用服务来管理. 但是当我们运行安装服务的命令时候,假设服务名为myservice #chkconfig --add myser ...

  5. zoj 3761

    很简单但很虐心的一道题: 我感觉自己的算法没错,但是老是过不了:= = 但是还是把代码贴出来: 我的方法,用并查集的方式做一课树,然后对树进行遍历: 有多少棵树就有多少个点剩余: #include&l ...

  6. sql 中 case when 语法(转)

    sql语言中有没有类似C语言中的switch case的语句?? 没有,用case   when   来代替就行了.            例如,下面的语句显示中文年月         select ...

  7. 玩玩EXPRESSJS

    呵呵,反正有的是学习时间哈哈, 这个EXPRESSJS,看看实现的大约. 看的是http://www.expressjs.com.cn/ 代码A: var express = require('exp ...

  8. php 获取图片、swf的尺寸大小

    PHP获取图片大小函数.  getimagesize() 能够得到图片及flash(swf)的大小. 语法 1 list($width, $height, $type, $attr) = getima ...

  9. 如何查看Linux的磁盘空间还有多少

    无论是Linux还是unix,都提供了很多工具来查看磁盘空间的大小. 1. df命令,df命令,可以查看当前文件系统占用的空间的大小,以及剩余的大小,如下例子: Filesystem     1K-b ...

  10. SPRING IN ACTION 第4版笔记-第七章Advanced Spring MVC-001- DispatcherServlet的高级配置(ServletRegistration.Dynamic、WebApplicationInitializer)

    一. 1.如想在DispatcherServlet在Servlet容器中注册后自定义一些操作,如开启文件上传功能,则可重写通过AbstractAnnotationConfigDispatcherSer ...