在所有高端型号,大多数中端型号以及部分低端型号的交换机/路由器上,都可以配置一个或者多个镜像端口,它是流量分析的利器。然而,Linux上没有现成的技术可以实现镜像端口,当然,我指的不是Linux 3.x(x是几,忘了)以上的内核,这些内核已经支持了镜像,但不够好。起码2.6.35的内核是不能支持的,那么Linux实现的软交换机属于哪个档次呢?关键是,很多高端的网络产品也是基于Linux实现的,没有镜像口怎么能行,即使在不使用Linux bridge的情况下,也希望能有一个技术实现镜像端口。
      我相信,并且确信,很多产品都已经实现了这个技术,它事实上很简单,多年以前,我自己在学习华为网络技术的时候,也曾在Linux写了一个支持镜像的内核模块,虽然那是在网上找的人家实现的半片子代码改的。现如今,在我可以很不谦虚地说自己已经很精通Netfilter以及Linux IP路由的时候,决定给出一个基于Netfilter的实现,Netfilter就在那,它几乎可以扩展任何协议栈的东西,甚至重写整个协议栈...多年以来,关于这个Linux如何实现镜像端口的讨论很多很多,也催生了不少爱美之士的不断尝试和修正,对我个人来讲,第一次涉足这个话题是在2009年,虽然在学习Cisco技术的时候也搞过,但毕竟不是任务化的,只是说我对Cisco技术是学而不考-太贵,因此可以有大把本应该用于考试准备的时间用来学习Linux,特别是把Cisco的特性实现在Linux上,说句题外话,我之所以对Cisco和Linux的网络技术能同时掌握,和学而不考有很大的关系,然而对于求职,那就是另外一回事了...
      在给出代码之前,我先给出一个只依靠配置就可以完成的实现,然后说一下它的缺点。事实上,仅仅依靠brctl命令或者sysfs,echo就可以实现一个镜像端口,具体做法就是:
1.确定你的镜像端口,比如eth5;
2.将实际数据通过的端口,比如eth0和镜像端口绑成一个bridge;
3.调用brctl的setageing命令将老化时间设置为0,这就模拟了一个2端口的hub;
4.所有数据端口eth0发出的包都会发往eth5
...

但是!但是每一个物理接口只能属于一个bridge,这就意味着你只能通过上述的方式捕获一个方向的数据,不得不使用另外的一个镜像口使用相同的办法捕获另外一个方向的数据,然后再把这两个镜像口接在一个switch上,在此switch上合二为一,这种方式,还是,太硬了!
      那么,软件做法有没有呢?有的,我多年前实现的那个就是,大体想法就是注册一个ETH_P_ALL类型的packet_type,类似tcpdump抓包那样捕获数据包,然后在内核模块中调用dev_queue_xmit将其发送到你定义的镜像端口,具体定义方式需要通过字符设备的ioctl,procfs等方式来定义。这种方式比较常规,工作地比较好,并且可以从诸如tap等虚拟网卡将流量镜像给进程而不是线缆那头的审计设备。然而,还是太硬了,在你通过BPF语法过滤数据包之前,流量已经被ETH_P_ALL截取了...事实上,并不是所有的流量都需要被镜像!BPF虽然强大,但是依靠中间层进行解析翻译,门槛太高,我相信,一条iptables规则和一条等价的BPF规则放在那,能看懂前者的占绝大多数,看不懂后者占绝大多数,过于灵活就是不灵活,给你一本新华字典,所有字都在里面,你读十遍也不如读一遍《古文观止》...这个可以从香农的信息论中得到证明。

xt_TEE的实现

在xtables-addons中,已经有了一个xt_TEE的实现,在其manual中,有一个一目了然的配置:
-t mangle -A PREROUTING -i eth0 -j TEE --gateway 2001:db8::1
即将数据包克隆一份,然后发往一个IP地址,该IP地址可以配置。我为何觉得它不好呢?第一,我认为依基于IP而不是基于端口来镜像数据包可能需要额外太多的配置,比如你事先要有一个接收端的明确IP地址;第二我觉得它的实现不是很好,它的实现阻碍了原始数据包的快速通过,而我比较倾向于用“下半部”的思想解决克隆包的发送问题,即先将其排入一个队列,然后让系统调度其发送,而不是强制在代码中调用发送代码。除了这两点,TEE的实现真的不错。看了TEE的实现之后,我在想,为何:
-j TEE --dev ethX,ethY,ethZ
这种设置就不行呢?当然,肯定不行,因为TEE target没有--dev参数,可是为何没有人实现呢?...难道仅仅是内核缺少由dev自动封装以太头的接口?也许是吧,毕竟,所有的dev_queue_xmit调用都是从路由层一路下来的...
      现在该给出我最新的实现了。这个实现很简单,和TEE一样,写了一个新的iptables target,即CLONE。克隆一个数据包并且打上标签,然后如何处理该数据包呢?很显然是根据标签来查找策略路由表了,你可以在策略路由表中将所有克隆的数据包发到任何一个网卡中,这不就是镜像口的含义么?
      要说明的是,虽然你可以通过reroute的方式将带有标签的克隆数据包发往一个网卡,但是由于网卡在发包前需要对目标或者对下一跳进行ARP,那么可能导致由于ARP没有回应而发包失败,幸运的是,ifconfig命令可以禁用网卡的ARP,这不正是为镜像端口准备的么??

前传

起初,写这个模块的目标并不是为了做镜像端口,而是为了将一个数据包复制两份,仅此而已,其实本意就是一个Netfilter实现的抓包模块,和使用pcap抓包相比,它的优势在于可以去除很多不相关数据包的干扰,它只能抓取确实是发往本机的数据包,虽然这也许违背的抓包的原本的意义,但是那只是一个词汇而已!我以及很多人大多数情况下抓包并不是为了嗅探别人的数据,而是为了解决和自己相关的问题,这就需要过滤掉那些不小心到来的由于交换机MAC映射到期导致的发往所有端口的数据,而这需要写一大堆tcpdump规则。
      使用Netfilter配合iptables来做这件事,优势在于不需要把全部的规则写在一条命令里面,你完全可以在PREROUTING的mangle表用mark过滤掉那些你不感兴趣的包,然后在FORWARD的filter上对感兴趣的包实施包克隆,然后将克隆到的数据包通过策略路由发往任何你希望它到达的地方。你可以再写一个模块,用以决定是对包进行完全的记录呢,还是对仅仅像LOG target那样只记录协议元数据-这很重要,大多数时候,我们并不关心载荷内容,除非你做深度分析。
      总之,我不喜欢那种包揽一切的程序,抓包也是如此,一个ETH_P_ALL将所有数据不问青红皂白全部截取,这是不合适的,当然它更加符合抓包的原本含义,但是谁在乎呢?也许是UNIX哲学在作崇,但也只是也许而已。实证主义并不在任何地方都有效。
      在我的实现中,和TEE的实现不同,我只是克隆数据包,然后为其打上一个标签,至于说接下来怎么做,后续的HOOK来决定,你甚至都可以用我的CLONE target和TEE target结合在一起,形成一个packet fork×××。
      下面给出实现,注意,该实现不能做到包嗅探!

实现

本实现由4部分,其中包含一个内核模块文件,一个用户态的iptables库文件,一个结构体定义头文件,一套Makefile。代码完全按照xtables-addons的规范制作。
结构体定义头文件:xt_CLONE.h

#ifndef _LINUX_NETFILTER_XT_CLONEMARK_H
#define _LINUX_NETFILTER_XT_CLONEMARK_H 1struct xt_clonemark_tginfo {__u32 mark;
};#endif /* _LINUX_NETFILTER_XT_CLONEMARK_H */

内核模块:xt_CLONE.c

/**      This program is free software; you can redistribute it and/or*      modify it under the terms of the GNU General Public License; either*      version 2 of the License, or any later version, as published by the*      Free Software Foundation.*/#include <linux/module.h>
#include <linux/skbuff.h>
#include <linux/netfilter/x_tables.h>
#include <net/ip6_route.h>
#include "xt_CLONE.h"
#include <net/ip.h>
#include "compat_xtables.h"struct sk_buff_head clq;
static struct tasklet_struct clone_xmit_tasklet;static void clone_xmit_work(unsigned long data)
{struct sk_buff_head *pclq = (struct sk_buff_head *)data;struct net_device *old_dev = NULL;struct net_device *new_dev = NULL;do {struct sk_buff * skb = skb_dequeue_tail(pclq);old_dev = skb_dst(skb)->dev;if (ip_route_me_harder(&skb, RTN_UNSPEC)) {kfree_skb(skb);}new_dev = skb_dst(skb)->dev;if (old_dev != new_dev) {ip_local_out(skb);} else {kfree_skb(skb);}} while (!skb_queue_empty(pclq));
}static unsigned int
clone_tg6(struct sk_buff **poldskb, const struct xt_action_param *par)
{// TODOreturn XT_CONTINUE;;
}static unsigned int
clone_tg4(struct sk_buff **poldskb, const struct xt_action_param *par)
{const struct xt_clonemark_tginfo *markinfo = par->targinfo;struct sk_buff *newskb;__u32 mark;__u32 qlen;qlen = skb_queue_len (&clq);// 控制总量!if (qlen > 1000/*sysctl参数控制*/) {return XT_CONTINUE;}mark = markinfo->mark;newskb = pskb_copy(*poldskb, GFP_ATOMIC);if (newskb == NULL)return XT_CONTINUE;// 在FORWARD链上做的目的是可以放心reroute,关键在re前缀
//      skb_dst_drop(newskb);// 丢弃连接跟踪,但是要为之初始化一个notrack的伪连接跟踪
#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
#include <net/netfilter/nf_conntrack.h>nf_conntrack_put(newskb->nfct);newskb->nfct = &nf_conntrack_untracked.ct_general;newskb->nfctinfo = IP_CT_NEW;nf_conntrack_get(newskb->nfct);
#endifnewskb->mark = mark;skb_queue_head(&clq, newskb);tasklet_schedule(&clone_xmit_tasklet);return XT_CONTINUE;
}static struct xt_target clone_tg_reg[] __read_mostly = {{.name       = "CLONE",.revision   = 0,.family     = NFPROTO_IPV6,.table      = "filter",.target     = clone_tg6,.targetsize = sizeof(struct xt_clonemark_tginfo),.me         = THIS_MODULE,},{.name       = "CLONE",.revision   = 0,.family     = NFPROTO_IPV4,.table      = "filter",.target     = clone_tg4,.targetsize = sizeof(struct xt_clonemark_tginfo),.me         = THIS_MODULE,},
};static int __init clone_tg_init(void)
{skb_queue_head_init(&clq);tasklet_init(&clone_xmit_tasklet, clone_xmit_work, (unsigned long)&clq);return xt_register_targets(clone_tg_reg, ARRAY_SIZE(clone_tg_reg));
}static void __exit clone_tg_exit(void)
{tasklet_kill(&clone_xmit_tasklet);return xt_unregister_targets(clone_tg_reg, ARRAY_SIZE(clone_tg_reg));
}module_init(clone_tg_init);
module_exit(clone_tg_exit);
MODULE_AUTHOR("Wangran <marywangran@126.com>");
MODULE_DESCRIPTION("Xtables: CLONE packet target");
MODULE_LICENSE("GPL");
MODULE_ALIAS("ip6t_CLONE");
MODULE_ALIAS("ipt_CLONE");

iptables模块:libxt_CLONE.c

/**      This program is free software; you can redistribute it and/or*      modify it under the terms of the GNU General Public License; either*      version 2 of the License, or any later version, as published by the*      Free Software Foundation.*/
#include <stdio.h>
#include <getopt.h>
#include <xtables.h>
#include "xt_CLONE.h"
#include "compat_user.h"enum {FL_MARK_USED     = 1 << 0,
};static const struct option clonemark_tg_opts[] = {{.name = "mark",     .has_arg = true, .val = '1'},{NULL},
};static void clonemark_tg_init(struct xt_entry_target *t)
{struct xt_clonemark_tginfo *info = (void *)t->data;info->mark = ~0U;
}static void clone_tg_help(void)
{printf("CLONE --mark mark\n\n");
}static int clone_tg_parse(int c, char **argv, int invert, unsigned int *flags,const void *entry, struct xt_entry_target **target)
{struct xt_clonemark_tginfo *info = (void *)(*target)->data;unsigned int n;switch (c) {case '1':xtables_param_act(XTF_ONLY_ONCE, "CLONE", "--mark", *flags & FL_MARK_USED);xtables_param_act(XTF_NO_INVERT, "CLONE", "--mark", invert);if (!xtables_strtoui(optarg, NULL, &n, 0, ~0U))xtables_param_act(XTF_BAD_VALUE, "CLONE", "--mark", optarg);info->mark = n;*flags |= FL_MARK_USED;return true;}return false;
}static void clone_tg_check(unsigned int flags)
{//TODO
}static void
clonemark_tg_save(const void *entry, const struct xt_entry_target *target)
{const struct xt_clonemark_tginfo *info = (const void *)target->data;printf(" --mark 0x%x ", (__u32)info->mark);
}static struct xtables_target clone_tg_reg = {.version       = XTABLES_VERSION,.name          = "CLONE",.family        = NFPROTO_UNSPEC,.size          = XT_ALIGN(sizeof(struct xt_clonemark_tginfo)),.userspacesize = XT_ALIGN(sizeof(struct xt_clonemark_tginfo)),.init          = clonemark_tg_init,.save          = clonemark_tg_save,.help          = clone_tg_help,.parse         = clone_tg_parse,.final_check   = clone_tg_check,.extra_opts    = clonemark_tg_opts,
};static __attribute__((constructor)) void clone_tg_ldr(void)
{xtables_register_target(&clone_tg_reg);
}

编译
建议编译时将c代码全部放入xtables-addons的extensions目录,然后修改该目录下的Kbuild文件,加入以下一行:
obj-${build_CLONE}      += xt_CLONE.o
修改该目录下的Mbuild文件,加入下面一行:
obj-${build_CLONE}      += libxt_CLONE.so
修改该目录上级目录的mconfig文件,加入下面一行:
build_CLONE=m
在extensions目录下执行make && make install即可,

说明

为何要在filter表做呢?因为filter表都在路由之后执行,这是为了调用reroute接口函数ip_route_me_harder的方便,该函数导出为一个内核接口,可以直接调用。在这么做之前,我尝试过直接调用ip_queue_xmit函数,然而发现只有在本机出发的包才会经过该路径,因此需要为skb绑定一个socket才可以,而这无疑是工作量加大了;后来,我想到了直接调用ip_rcv_finish函数,可以该函数并未导出,需要在加载模块前先去procfs里面查一下该函数的地址,然后传入模块,这种做法并不标准;再往后,自然而然就是调用ip_route_me_harder接口函数了,然而该函数需要skb已经有了一个dst_entry(这很正常,reroute中的re前缀表明skb已经被路由过一次了),因此必然要在路由之后调用,那么显然处理位置就落到了Netfilter的HOOK点和路由构成的马鞍面的中间位置了,只能在filter表来做,重新路由之后,直接调用ip_local_out从第三层发出即可。
      此时又有问题了,既然已经重路由了,为了不直接从第二层路由结果的dev中发出呢,也就是调用dev_queue_xmit函数。实际上是完全可以的,然而工作量也会加大,比如你要自行增加MAC头封装等。在一个成型的实现中,所有的封装都必须由协议栈本身来完成,即调用协议栈的函数,因为协议栈本身就是干这个的,决不要在自己的代码中实现,如果你觉得自己可以实现一个更妙的,那就直接改掉协议栈。

局限

该实现还是有一定局限的,毕竟该实现的做法太高层,它会改变数据包的MAC头,但是这对于针对应用层内容的深度解析,无所谓了。另外需要注意的是,需要在本机做三件工作,第一就是设置CLONE规则及确定mark,第二是根据mark设置策略路由,第三就是将策略路由指向的出口设备的arp禁用掉。除了本机做的工作之外,还要在接收镜像数据的机器的接收接口上开启混杂模式。
      毕竟这只是一个试验,并非成型的解决方案,能做到这一点我已经很满足了。

转载于:https://blog.51cto.com/dog250/1343714

Linux如何实现镜像端口相关推荐

  1. linux下端口镜像,Linux如何实现镜像端口

    在所有高端型号,大多数中端型号以及部分低端型号的交换机/路由器上,都可以配置一个或者多个镜像端口,它是流量分析的利器.然而,Linux上没有现成的技术可以实现镜像端口,当然,我指的不是Linux 3. ...

  2. linux 端口 镜像吗,Linux如何实现镜像端口

    在所有高端型号,大多数中端型号以及部分低端型号的交换机/路由器上,都可以配置一个或者多个镜像端口,它是流量分析的利器.然而,Linux上没有现成的技术可以实现镜像端口,当然,我指的不是Linux 3. ...

  3. linux 端口镜像软件,Linux Bridge的镜像端口实现

    很多种交换机上都可以配置镜像端口,也就是说所有的流量都要顺便发一份到镜像端口,一般都是在镜像端口上接一个主机,上面开启抓包或者审计程序,保证时刻监控网络流量.镜像端口解决了学习型交换机无法抓包的问题. ...

  4. linux nat span端口镜像,端口镜像span、rspan实现数据检测

    端口镜像span.rspan(华为) RSPAN(Remote Switched Port Analyzer,远程交换端口分析),即远程端口镜像,远程端口镜像(RSPAN)是本地端口镜像(SPAN)的 ...

  5. CentOS下KVM网卡设置成网桥时获取镜像端口的流量

    首先,网桥配置好之后就能实现一个简单的交换机,而交换机的特点就是MAC地址学习,那么KVM的网卡设置成网桥之后,也就是相当于连接到了交换机上. 此时如果要实现在二层交换机或三层交换机做端口镜像,并把这 ...

  6. 构建小型Linux跟文件系统镜像(Ext2 或 Ext3)

    1,构建镜像文件(100M) mkdir work dd if=/dev/zero of=/work/linux_root.img bs=1k count=100000 100000+0 record ...

  7. linux防火墙开放所有端口命令,linux centos7 防火墙及端口开放相关命令

    一.防火墙相关命令 1.查看防火墙状态 : systemctl status firewalld.service 注:active是绿的running表示防火墙开启 2.关闭防火墙 :systemct ...

  8. Linux 修改SSH 默认端口 22,防止被破解密码

    2019独角兽企业重金招聘Python工程师标准>>> Linux/Unix 系统,很多人使用SSH + 密码来登陆服务器,默认 22端口,这样会有被暴力破解密码的危险(除非密码足够 ...

  9. 日常工作问题解决:centos/linux系统如何检测端口是否打开

    日常工作问题解决:centos/linux系统如何检测端口是否打开 参考文章: (1)日常工作问题解决:centos/linux系统如何检测端口是否打开 (2)https://www.cnblogs. ...

最新文章

  1. The application could not be installed: INSTALL_FAILED_NO_MATCHING_ABIS
  2. 【Linux】在VirtualBox-6.0中安装Manjaro18.0
  3. 写学生管理系统后的一些感想
  4. 针对maven install 报错:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.1 解决方案...
  5. 【PC工具】微软OneNote使用笔记,onenote无法连接网络无法同步解决方法
  6. 冷静处理因为一时疏忽产生的错误。是提升自己的重要方法
  7. 使用SQLite3存储和读取数据
  8. 虫师python appium自动化测试书_基于python的Appium自动化测试的坑
  9. 如何优雅地实现判断一个值是否在一个集合中?
  10. 工业交换机厂家,工业交换机品牌前十大排名
  11. [原创]软件质量管理培训
  12. unity之制作二维码扫描
  13. 【SQL基础】SQLzoo练习
  14. 2019/12/9 K60单片机学习
  15. android开发日历,Android日历开发详解
  16. VB中的界面设计原则和编程技巧
  17. 【备忘】2018年最新尚硅谷全套Java、Android、HTML5前端视频教程下载
  18. win10系统如何清理c盘垃圾
  19. 关于迅盘Turbo Memory的ReadyDrive功能被禁用的思考
  20. win10微软输入法不显示选字框?

热门文章

  1. 发布一个验证码生成组件
  2. 【转】python包导入细节
  3. Oracle DBA课程系列笔记(19)
  4. 全球都面临网络安全“人才荒” 院士建议我国高校开“少年班”
  5. ORACLE学习笔记--性能优化2
  6. 达人眼中的WINCE网络驱动
  7. Effective C# 原则13:用静态构造函数初始化类的静态成员(译)
  8. 会话技术CookieSession
  9. 基于Cobbler实现多版本系统批量部署
  10. splunk的bucket组织目录——时间序列,按照时间来组织目录