macvlan分析
在 Macvlan 出现之前,一块以太网卡添加多个 IP 地址,却不能添加多个 MAC 地址,即使使用了创建 ethx:y 这样的方式,这些“网卡”的 MAC 地址和 ethx 都是一样的,本质上,它们还是一块网卡,这将限制很多二层的操作。
Macvlan 允许你在主机的一个网络接口上配置多个虚拟的网络接口,这些网络 interface 有自己独立的 MAC 地址,也可以配置上 IP 地址进行通信。Macvlan 下的虚拟机或者容器网络和主机在同一个网段中,共享同一个广播域。Macvlan 和 Bridge 比较相似,但因为它省去了 Bridge 的存在,所以配置和调试起来比较简单,而且效率也相对高。除此之外,Macvlan 自身也完美支持 VLAN
Macvlan vs Bridge
| 维度 | Bridge | Macvlan |
|---|---|---|
| 网络层级 | 工作于二层(数据链路层),需通过主机协议栈转发。 | 工作于二层,虚拟接口直接关联物理网卡,绕过主机协议栈(默认)。 |
| IP 分配 | 虚拟设备共享主机的 IP 地址,或通过 Bridge 分配独立 IP。 | 每个 Macvlan 接口可配置独立 IP,直接属于物理网络子网。 |
| MAC 地址 | 虚拟设备共享主机物理网卡的 MAC 地址(或由 Bridge 统一管理)。 | 每个 Macvlan 接口有独立 MAC 地址,可被远程设备视为独立节点。 |
| 通信方式 | - 虚拟设备之间通过 Bridge 转发数据。 - 与外部通信需通过主机的物理网卡路由或 NAT。 |
- 虚拟设备直接通过物理网卡与外部通信,无需经过主机协议栈(类似物理网卡直连)。容器流量直接通过物理网卡发送到物理网络,完全绕过宿主机的网络协议栈。 - 支持直接路由到外部网络。 |
| 性能 | 数据需经主机协议栈处理,存在一定性能损耗。 | 绕过主机协议栈,直接在二层传输,性能更接近物理网卡(尤其适合高吞吐量场景)。 |
| 隔离性 | 虚拟设备属于同一广播域,隔离性依赖三层路由或防火墙。 | 虚拟设备可通过不同子接口(如不同 Macvlan 类型)实现二层隔离(如 private 模式)。 |
| 适用场景 | - 容器 / 虚拟机共享主机网络(如 Docker bridge)。 - 需要主机参与数据包处理(如 NAT、过滤)。 |
- 容器 / 虚拟机需要独立 MAC 地址,直接接入物理网络。 - 高性能网络场景(如裸金属容器、低延迟需求)。 |
| 配置复杂度 | 配置相对简单,支持图形化工具(如 nmcli、brctl)。 |
需手动指定 MAC 地址或模式,配置更依赖命令行(如 ip link add type macvlan)。 |
选 Bridge 的情况:
选 Macvlan 的情况:
- 需要高性能、独立 MAC 地址,且物理网络支持时(如云服务器、物理机部署)。
macvlan 模式
macvlan发包逻辑
static int macvlan_queue_xmit(struct sk_buff *skb, struct net_device *dev) { const struct macvlan_dev *vlan = netdev_priv(dev); //作用:指向关联的 macvlan_port 结构体,表示当前 macvlan 设备所属的“端口”。 //macvlan_port 是管理多个 macvlan 设备的逻辑实体,负责协调它们共享同一个底层物理设备 const struct macvlan_port *port = vlan->port; const struct macvlan_dev *dest; if (vlan->mode == MACVLAN_MODE_BRIDGE) {//如果是bridge模式, const struct ethhdr *eth = skb_eth_hdr(skb); /* send to other bridge ports directly */ if (is_multicast_ether_addr(eth->h_dest)) { skb_reset_mac_header(skb); //如果目的地址是组播广播则, //将port上所有macvlan接口,遍历一遍然后每个vlan接口转发 macvlan_broadcast(skb, port, dev, MACVLAN_MODE_BRIDGE); goto xmit_world;// 同时 将报文通过port 宿主parent接口发送出去 } //单播数据 根据目的mac查找目标macvlan设备 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); // 如果找到了,且目标也是bridge模式; 说明是两个子接口macvlan1 和macvlan2 通信 //macvlan1 转发到macvlan2接口实际上是发给底层eth0中转, //也就是eth0收到此包,然后再次macvlan_handle_frame然后转发到macvlan2 接口上 return NET_XMIT_SUCCESS; } //否者转发到 底层物理设置eth0 } //上述bridge说明了:bridge模式想子接口可以互通! 应为arp 令居表项能建立起来,其余的模式,arp报文直接转发到底层eth0 接口。 xmit_world: skb->dev = vlan->lowerdev;//指向底层物理网络设备(如 eth0)的 net_device 结构体 return dev_queue_xmit_accel(skb, netdev_get_sb_channel(dev) ? dev : NULL); }
macvlan_queue_xmit() ├── 如果目标是同一个 port 上的其他 macvlan(macvlan2): │ ├── dev_forward_skb() 把包交给 eth0;包的设备是 eth0,将通过 eth0 的接收路径进入内核。// eth0 接收到 macvlan1 发给 macvlan2 的包 │ └── return NET_XMIT_SUCCESS └── 否则 skb->dev = lowerdev (eth0), dev_queue_xmit()
也就是:在 VEPA /PRIVATE/PASSTHRU模式下,所有从 Macvlan 接口发出的流量,不管目的地全部都发送给父接口,即使流量的目的地是共享同一个父接口的其它 Macvlan 接口。
eth0 接收路径:转给 macvlan 接口
数据从内核中的 eth0 驱动层接收到,然后交给协议栈:
netif_receive_skb()
└── __netif_receive_skb()
└── __netif_receive_skb_core()
└── rx_handler
/* called under rcu_read_lock() from netif_receive_skb */ static rx_handler_result_t macvlan_handle_frame(struct sk_buff **pskb) { struct macvlan_port *port; struct sk_buff *skb = *pskb; const struct ethhdr *eth = eth_hdr(skb); const struct macvlan_dev *vlan; const struct macvlan_dev *src; struct net_device *dev; unsigned int len = 0; int ret; rx_handler_result_t handle_res; /* Packets from dev_loopback_xmit() do not have L2 header, bail out 即本机自发自收的包, 自己发给eth0的报文,也属于loopback,报文转到eth0 此时应该直接到协议栈; 不应到macvlan上来,所以返回pass到协议栈处理*/ if (unlikely(skb->pkt_type == PACKET_LOOPBACK)) return RX_HANDLER_PASS; port = macvlan_port_get_rcu(skb->dev); // 找到 eth0 上注册的 macvlan port if (is_multicast_ether_addr(eth->h_dest)) {//组播广播 unsigned int hash; //对收到的 IP 数据报进行重组(如果被分片)。 //如果重组失败,释放 skb 并结束处理。 skb = ip_check_defrag(dev_net(skb->dev), skb, IP_DEFRAG_MACVLAN); if (!skb) return RX_HANDLER_CONSUMED; *pskb = skb;//重新获取 eth(因为 skb 地址可能变了,报文重组的skb)。 eth = eth_hdr(skb); //更新源 MAC 的 macvlan 映射(用于反向查找) macvlan_forward_source(skb, port, eth->h_source); //查找源 MAC 地址绑定的 macvlan_dev(发出者) src = macvlan_hash_lookup(port, eth->h_source); if (src && src->mode != MACVLAN_MODE_VEPA && src->mode != MACVLAN_MODE_BRIDGE) { //如果发出者的模式不是 VEPA/BRIDGE,说明不能互通(如 PRIVATE),则只转发给自己 /* forward to original port. */ vlan = src;//设置的是 MACVLAN_MODE_PRIVATE,macvlan 之间是不能互相通信的(即使接收到也会过滤掉 arp 会失效 ret = macvlan_broadcast_one(skb, vlan, eth, 0) ?: netif_rx(skb); //macvlan_broadcast_one 向 vlan 投递 skb。此时参数local=0,返回0;则是调用netif_rx //如果它处理失败,则交给内核默认路径 netif_rx。 /*其完全阻止共享同一父接口的 Macvlan 虚拟网卡之间的通讯, 即使配置了 Hairpin 让从父接口发出的流量返回到宿主机,相应的通讯流量依然被丢弃。 具体实现方式是丢弃广播/多播数据,这就意味着以太网地址解析 arp 将不可运行, 除非手工探测 MAC 地址,否则通信将无法在同一宿主机下的多个 Macvlan 网卡间展开。 */ handle_res = RX_HANDLER_CONSUMED; goto out; }//也就是private模式子接口虚拟网卡肯定不能通信, //但是MACVLAN_MODE_VEPA模式,发包到eth0 然后走到此处调用 hash = mc_hash(NULL, eth->h_dest); if (test_bit(hash, port->mc_filter))//如果广播目标在监听列表中(使用 hash 位图快速判断): macvlan_broadcast_enqueue(port, src, skb); //调用 macvlan_process_broadcast 广播出去, //也就是vepa模式下,子接口arp报文是从eth0发送出去了!一般情况下是收不到的 return RX_HANDLER_PASS; } //如果是单播报文, macvlan_forward_source(skb, port, eth->h_source); //如果是 PASSTHRU 模式,直接取出第一个虚拟接口(只能绑定一个容器); if (macvlan_passthru(port)) vlan = list_first_or_null_rcu(&port->vlans, struct macvlan_dev, list); else { // 查找目的 MAC 地址对应的 macvlan 接口 vlan = macvlan_hash_lookup(port, eth->h_dest); } //如果没找到目标 macvlan,或者目标接口处于 SOURCE 模式(只发不收), //则交由其他 handler 内核协议栈 处理。 if (!vlan || vlan->mode == MACVLAN_MODE_SOURCE) return RX_HANDLER_PASS; //也就是单播 VEPA模式下: //macvlan 发往外部,则macvlan1--->eth0--->netif_rcv---rc_hander-->hander_pass-->router--->外网 //macvlan发往macvlan2,则macvlan1--->eth0--->netif_rcv---rc_hander-->RX_HANDLER_ANOTHER-->maclan2 dev = vlan->dev; if (unlikely(!(dev->flags & IFF_UP))) { kfree_skb(skb); return RX_HANDLER_CONSUMED; } len = skb->len + ETH_HLEN; skb = skb_share_check(skb, GFP_ATOMIC); if (!skb) { ret = NET_RX_DROP; handle_res = RX_HANDLER_CONSUMED; goto out; } *pskb = skb; skb->dev = dev; skb->pkt_type = PACKET_HOST; ret = NET_RX_SUCCESS; handle_res = RX_HANDLER_ANOTHER; out: macvlan_count_rx(vlan, len, ret == NET_RX_SUCCESS, false); return handle_res; }
http代理服务器(3-4-7层代理)-网络事件库公共组件、内核kernel驱动 摄像头驱动 tcpip网络协议栈、netfilter、bridge 好像看过!!!!
但行好事 莫问前程
--身高体重180的胖子

浙公网安备 33010602011771号