周末的时候写了一篇关于Docker底层支撑技术的文章:
以firejail sandbox解析Docker核心原理依赖的四件套:https://blog.csdn.net/dog250/article/details/81025071
获得了一些反响,总结下来就三点:
1. 确实这四件套支撑了很多的容器技术;
2. 唱衰Docker以及OverlayFS,Cgroup,NS这些,以为它们违背了某种原则;
3. 关于firejail网络方面的某些细节。

上述的1和2显得过于形而上,保留个人观点不讨论,或者等哪天闲了再说。关于第三点,我觉得有必要梳理一下,本文先说一个典型的。


有网友问到,firejail的Macvlan配置细节是怎样的,firejail容器内的Macvlan虚拟网卡如何访问容器外宿主机的IP地址呢??(先说答案吧,默认情况下,不能访问)

我并没有深入的了解过firejail,本身接触它也不久,所以只能从其文档代码中去一窥究竟了。firejail的github源在这里:https://github.com/netblue30/firejail

  1. 从以下的链接可以看到一个典型的Demo样例,我和作者描述问题的思路是一致的:
    https://github.com/netblue30/firejail/blob/master/src/firejail/network.txt
  2. 从下面的代码中你可以看到firejail关于macvlan的配置细节:
    https://github.com/netblue30/firejail/blob/master/src/firejail/network_main.c

总而言之,firejail使用网络的方式有两种:

  • Bridge veth的方式:与Docker Bridge模式一致,如果–net参数指定一个Linux Bridge的话。
  • Macvlan bridge模式:如果–net参数指定一块物理网卡,即会产生一个bridge模式的macvlan虚拟网卡。

这些在其manual中也有提到:

–net=bridge_interface
                Enable  a  new  network  namespace  and  connect it to this bridge interface.  Unless specified with option –ip and –defaultgw, an IP address and a
                default gateway will be assigned automatically to the sandbox. The IP address is verified using ARP before  assignment.  The  address  configured  as
                default gateway is the bridge device IP address. Up to four –net bridge devices can be defined. Mixing bridge and macvlan devices is allowed.
  .
                Example:
                sudobrctladdbrbr0 sudobrctladdbrbr0 firejail –net=eth0 –ip=192.168.1.80 –dns=8.8.8.8 firefox

现在我们关注第二点,即Macvlan的方式。我使用下面的命令启动firejail:

# 我为容器指派192.168.44.55这个IP地址# 我的宿主机enp0s17这块网卡的IP地址是192.168.44.138root@debian:/home/zhaoya# firejail --net=enp0s17 --ip=192.168.44.55/24 Reading profile /etc/firejail/server.profileReading profile /etc/firejail/disable-common.incReading profile /etc/firejail/disable-programs.incReading profile /etc/firejail/disable-passwdmgr.inc** Note: you can use --noprofile to disable server.profile **Parent pid 870, child pid 871The new log directory is /proc/871/root/var/logInterface        MAC                IP               Mask             Statuslo                                  127.0.0.1        255.0.0.0        UP    eth0-870         1e:01:c7:ff:4b:39  192.168.44.55    255.255.255.0    UP    Default gateway 192.168.44.2Child process initializedroot@debian:~# root@debian:~# ping 192.168.44.138PING 192.168.44.138 (192.168.44.138) 56(84) bytes of data.From 192.168.44.55 icmp_seq=1 Destination Host UnreachableFrom 192.168.44.55 icmp_seq=2 Destination Host UnreachableFrom 192.168.44.55 icmp_seq=3 Destination Host Unreachable
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

在容器外的宿主机抓包:

root@debian:/home/zhaoya# tcpdump -i enp0s17 arp or icmp -ntcpdump: verbose output suppressed, use -v or -vv for full protocol decodelistening on enp0s17, link-type EN10MB (Ethernet), capture size 262144 bytes19:31:50.029168 ARP, Request who-has 192.168.44.138 tell 192.168.44.55, length 2819:31:51.053176 ARP, Request who-has 192.168.44.138 tell 192.168.44.55, length 2819:31:52.077292 ARP, Request who-has 192.168.44.138 tell 192.168.44.55, length 2819:31:53.101540 ARP, Request who-has 192.168.44.138 tell 192.168.44.55, length 28...
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

显然,ARP请求没有得到回应。

如果此时另外启动一个新的firejail容器,指派另一个同网段IP地址,你会发现,两个容器之间是可以互通的,但是容器和宿主机之间无法互通,这是怎么回事?

这就涉及到Macvlan的几种工作模式的原理了,我简单将其总结成下面的图示:

我们专门看看左上角第一幅图,即Bridge模式的Macvlan。显然,没有从虚拟网卡到物理网卡的通路(确实有点像裤衩…)…有了这个图示几乎便不用去看代码了。

值得注意的是,VEPA是一个特殊的模式,它不仅仅关乎Macvlan本身,还涉及到宿主机外部的交换机是否支持Hairpin(发夹弯模式,即交换机端口并不过滤入口的模式,可以实现原路echo),所以说,Bridge模式情况下,Macvlan虚拟网卡不能访问宿主网卡,有关限定条件,即虚拟网卡在主机内部不能访问宿主网卡,虚拟网卡发出的数据包经由物理宿主网卡一旦发到外部,还是有可能通过Hairpin给echo回来的


问题的解释就是这样。

下面是见招拆招了。如果你非要实现Macvlan的虚拟网卡和物理网卡之间的互通,怎么办?其实简单,只需要构造下面的图即可:


这下不看代码也得看了!


主要看Macvlan的xmit回调:

static int macvlan_queue_xmit(struct sk_buff *skb, struct net_device *dev){    const struct macvlan_dev *vlan = netdev_priv(dev);    const struct macvlan_port *port = vlan->port;    const struct macvlan_dev *dest;    if (vlan->mode == MACVLAN_MODE_BRIDGE) {        const struct ethhdr *eth = (void *)skb->data;        /* send to other bridge ports directly */        if (is_multicast_ether_addr(eth->h_dest)) {            macvlan_broadcast(skb, port, dev, MACVLAN_MODE_BRIDGE);            // 显然在ARP广播注入所有的虚拟网卡接收路径后,便从物理网卡直接发到外部去了。            goto xmit_world;        }        dest = macvlan_hash_lookup(port, eth->h_dest);        // 如果不是广播,也只有路由到虚拟网卡的数据包从能被注入。        if (dest && dest->mode == MACVLAN_MODE_BRIDGE) {            /* send to lowerdev first for its network taps */            dev_forward_skb(vlan->lowerdev, skb);            return NET_XMIT_SUCCESS;        }    }xmit_world:    skb->dev = vlan->lowerdev;    // 直接经由物理网卡连接的线缆发出。    return dev_queue_xmit(skb);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

对付这个非常简单,只需要改成下面的这个就行:

static int macvlan_queue_xmit_v2(struct sk_buff *skb, struct net_device *dev){        const struct macvlan_dev *vlan = netdev_priv(dev);        const struct macvlan_port *port = vlan->port;        const struct macvlan_dev *dest;        if (vlan->mode == MACVLAN_MODE_BRIDGE) {                const struct ethhdr *eth = (void *)skb->data;                /* send to other bridge ports directly */                if (is_multicast_ether_addr(eth->h_dest)) {                        struct sk_buff *nskb;                        macvlan_broadcast(skb, port, dev, MACVLAN_MODE_BRIDGE);                        nskb = skb_clone(skb, GFP_ATOMIC);                        if (likely(nskb)) {                                nskb->dev = vlan->lowerdev;                                // 往物理网卡也注入一份。                                dev_forward_skb(vlan->lowerdev, nskb);                        }                        goto xmit_world;                }                dest = macvlan_hash_lookup(port, eth->h_dest);                if (dest && dest->mode == MACVLAN_MODE_BRIDGE) {                        /* send to lowerdev first for its network taps */                        dev_forward_skb(vlan->lowerdev, skb);                        return NET_XMIT_SUCCESS;                }                // 如果目标MAC是物理网卡的,则注入到宿主物理网卡                else /*if (!compareMacs(eth, vlan->lowerdev))*/{                        struct sk_buff *nskb;                        nskb = skb_clone(skb, GFP_ATOMIC);                        if (likely(nskb)) {                                nskb->dev = vlan->lowerdev;                                dev_forward_skb(vlan->lowerdev, nskb);                        }                }        }xmit_world:        skb->dev = vlan->lowerdev;        return dev_queue_xmit(skb);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46

Macvlan虚拟网卡的发送路径就是这么轻松搞定的,那么接下来看一个比较棘手的,即物理网卡的发送路径,因为显然我们需要宿主机也能通过物理网卡和容器通信,比如SSH登录它。

这个比较棘手是因为我们不得不改物理网卡的驱动的hard_start_xmit回调函数,目前也没有在dev_queue_xmit中看到有什么比较好的HOOK点,而修改物理网卡驱动就为了支持Macvlan这么一个并非通用的机制,也有点怪异…

为了尽快跑起来看到效果,不去想那么多,直接使用ptype_all机制。

在数据包将要发送到物理网卡前,会有一个抓包的HOOK点,即dev_queue_xmit_nit函数。我们只需要注册一个ETH_P_ALL类型的packet_type即可将数据包拉到一个func回调中,之后便可以在这个回调中做任何想做的事了:

int xmit_handle(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev){        const struct macvlan_port *port = macvlan_port_get_rcu(skb->dev);        const struct ethhdr *eth = eth_hdr(skb);        const struct macvlan_dev *vlan;        if (skb->pkt_type != PACKET_OUTGOING)  {            // 仅仅在发送路径使能                goto drop_out;        }        if (port) {                vlan = macvlan_hash_lookup(port, eth->h_dest);                if (vlan) // 如果找到了虚拟网卡就单播注入。                        dev_forward_skb(vlan->dev, skb);                else // 如果没有对应到,就广播注入。                        macvlan_broadcast(skb, port, dev, MACVLAN_MODE_BRIDGE);                return 0;        }drop_out:        consume_skb(skb);        return再分享一下我老师大神的人工智能教程吧。零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!https://blog.csdn.net/jiangjunshow

Linux Macvlan的虚拟网卡与宿主物理网卡之间的Bridge通信问题相关推荐

  1. linux 虚拟网卡与物理网卡关系,Linux Macvlan的虚拟网卡与宿主物理网卡之间的Bridge通信问题...

    周末的时候写了一篇关于Docker底层支撑技术的文章: 以firejail sandbox解析Docker核心原理依赖的四件套 : https://blog.csdn.net/dog250/artic ...

  2. linux vmware 安装后无法桥接到物理网卡的解决办法

    在linux下安装完vmware后,因为需要必须使用桥接网络,交接物理网卡总是显示could not connect /dev/vmnet0, 网上搜索了很多办法都无法解决,下面这个据说能解决,但还是 ...

  3. linux查看网卡物理编号_关于如何查看多网卡物理机中网卡序号与物理网卡的对应该关系...

    做软件开发的人都知道:接触真正底层系统驱动的的机会很少(做网络驱动的除外哦),上周和同事一起在一块物理机的办卡上安装centerOS6.5(至于为啥安装6.5版本的,只能说厂商要求的),遇到了一个持续 ...

  4. linux查看网卡物理编号_Linux下多网卡时,如何快速辨别网卡ID与物理网卡的对应关系(即ethtool命令)...

    一般购买的服务器都有4个网卡,这个时候在安装好服务器后,配置IP的时候就郁闷了 如是一个浪潮8560M2服务器安装Redhat后的网卡显示: [root@DBSERVER51 ~]# ifconfig ...

  5. 判断是本地网卡是物理网卡还是虚拟网卡

    GetAdaptersInfo 可以获得本机所有网卡的信息,然而这些网卡中可能包括虚拟网卡.例如,若安装了 VMWare 或者某些 VPN 客户端软件,则会出现若干虚拟网卡.它们在形式上与物理网卡几乎 ...

  6. 删除Windows中隐藏的物理网卡和网络虚拟化失败后的虚拟网卡

    Windows环境下,在更换硬件服务器主板和网卡等硬件.恢复操作系统或者网络虚拟化失败后,可能会出现网卡方面的问题.例如,设备管理器中多了不应该存在的网卡:因命名冲突无法重命名当前网络连接:IP地址冲 ...

  7. MAC地址获取,有线网卡与无线网卡、物理网卡与虚拟网卡的区分

    获取当前活跃状态的网卡MAC地址.物理地址 Wmic命令:Win32_NetworkAdapter和Win32_NetworkAdapterConfiguration. 其中cmd命令行执行: 1. ...

  8. Linux 配置网卡、主机名(基础配置、网卡会话配置、网卡绑定配置)

    目录 配置网卡基本信息 通过nmcli命令配置网卡 通过配置网卡文件配置网卡 通过nmtui命令配置网卡 通过nm-connection-editor命令配置网卡 网卡高级配置 配置网络会话 配置网卡 ...

  9. CentOS系列的绑定MAC(物理网卡地址)

    CentOS系列的绑定MAC 1.临时绑定MAC 1-2.查看IP的MAC 1-3.临时修改MAC 1-3-1.临时修改MAC是系统重启后失效的 2.永久绑定MAC 2-1.编辑网卡 2-2.系统重启 ...

最新文章

  1. 特征提取,转换和选择
  2. android rfid 读写sdk,Android-SDK-1.0.0-STD android手机调用RFID模块读取电子标签Demo - 下载 - 搜珍网...
  3. C++实现通过UDP传输文件
  4. 数据库和数据挖掘领域的会议和期刊
  5. 安川最小巧机器人_2020工博会,安川展品前瞻(机器人篇)
  6. wpf绑定之格式化日期
  7. 表单嵌套问题的解决方法
  8. 作战军事环境仿真系统软件解决方案
  9. Web前端开发实验(导航栏、购物页面)
  10. linux下增加宋体 仿宋 字体
  11. 黑页网站html源码,仿360网站卫士拦截页面黑页源码
  12. Android Service之bindService
  13. 小米5s plus 刷机 国际版
  14. steps函数--参数意思和用法
  15. jiffies和jiffies_64
  16. unity入门精要之第6 章 Unity 中的基础光照概述-1
  17. mysql更新等差数列求和公式_shell学习笔记(6)
  18. 工业机器人三点工具定位法图文_手把手教你工业机器人三点示教法
  19. 《数据结构 C++ 语言描述》电子书下载 -(百度网盘 高清版PDF格式)
  20. ZDJW浙大经纬纹织CAD5.0

热门文章

  1. r语言员工离职_r语言、 weka代写从决策树模型看员工为什么离职?
  2. 支付+电商双系统项目笔记(七)支付系统:支付宝支付开发
  3. 百度AI进行人脸识别
  4. 线性代数矩阵相关内容总结
  5. LCP 16. 游乐园的游览计划
  6. 朴素贝叶斯实战应用---单词拼写检查器(结合html直观展示)
  7. 京东便利店拿什么保障百万便利店的服务品质,刘强东是这样说的
  8. W5500连接正常却PING不通,怎么办?
  9. vue 手机号(334分隔)
  10. ios中的通知和推送