本节内容参考 链接1、链接2 和 链接3
1.1 IPVLAN虚拟网卡技术
IPVLAN与MACVLAN类似。IPVLAN和MACVLAN的区别在于它在IP层进行流量分离而不是基于MAC地址,因此,你可以看到,同属于一块宿主以太网卡的所有IPVLAN虚拟网卡的MAC地址都是一样的,因为宿主以太网卡根本不是用MAC地址来分流IPVLAN虚拟网卡的流量的。具体的流程如下图所示:
由于所有的虚拟接口共享同个mac地址,因此有些地方需要注意:当使用 DHCP 协议分配 ip 时,一般会用 mac 地址作为机器的标识,因此需要配置唯一的 ClientID 字段作为机器的标识, DHCP server 配置 ip 时需使用该字段作为机器标识,而不是使用 mac 地址。
1.2 三种模式(mode)
ipvlan 有三种不同的工作模式:L2 、L3 和 L3s。一个父接口只能选择其中一种模式(不能采用混用模式),依附于它的所有虚拟接口都会运行在这个模式下。
1、L2 模式
Ipvlan 的 L2 模式和 macvlan 的 bridge 模式工作原理很相似,父接口作为交换机来转发子接口的数据。同一个网络的子接口可以通过父接口来转发数据,而如果想发送到其他网络,报文则会通过父接口的路由转发出去。
2、L3 模式
L3 模式下,ipvlan 有点像路由器的功能,它在各个虚拟网络和主机网络之间进行不同网络报文的路由转发工作。只要父接口相同,即使虚拟机/容器不在同一个网络,也可以互相 ping 通对方,因为 ipvlan 会在中间做报文的转发工作。该模式把父接口当成一个路由器,完全不支持广播,这个模式下的接口也比l2模式下的ipvlan接口多了一个 NOARP属性,也不会发送广播报文
2、L3s 模式
L3s 模式下与L3 模式类似,区别在于启用了iptables (conn-tracking)
上述三种MODE不能混用。
4.1 L2 mode:
In this mode TX processing happens on the stack instance attached to the
slave device and packets are switched and queued to the master device to send
out. In this mode the slaves will RX/TX multicast and broadcast (if applicable)
as well.
4.2 L3 mode:
In this mode TX processing up to L3 happens on the stack instance attached
to the slave device and packets are switched to the stack instance of the
master device for the L2 processing and routing from that instance will be
used before packets are queued on the outbound device. In this mode the slaves
will not receive nor can send multicast / broadcast traffic.
4.3 L3S mode:
This is very similar to the L3 mode except that iptables (conn-tracking)
works in this mode and hence it is L3-symmetric (L3s). This will have slightly less
performance but that shouldn't matter since you are choosing this mode over plain-L3
mode to make conn-tracking work.
1.3 三种FLAGS
ipvlan 有三种不同的flag:bridge 、private 和 vepa。bridge允许同一父接口下的子接口直接通讯;private禁止子接口之间通讯;vepa禁止子接口之间直接通讯,需要外部交换机开启hairpin(802.1q)转发通讯。
上述三种FLAG不能混用。
5. Mode flags:
At this time following mode flags are available
5.1 bridge:
This is the default option. To configure the IPvlan port in this mode,
user can choose to either add this option on the command-line or don't specify
anything. This is the traditional mode where slaves can cross-talk among
themselves apart from talking through the master device.
5.2 private:
If this option is added to the command-line, the port is set in private
mode. i.e. port won't allow cross communication between slaves.
5.3 vepa:
If this is added to the command-line, the port is set in VEPA mode.
i.e. port will offload switching functionality to the external entity as
described in 802.1Qbg
Note: VEPA mode in IPvlan has limitations. IPvlan uses the mac-address of the
master-device, so the packets which are emitted in this mode for the adjacent
neighbor will have source and destination mac same. This will make the switch /
router send the redirect message.
说明:IPVlan 和 MACVlan 会有各种模式(mode)和 flag,比如 bridge,VEPA(virtual ethernet port aggregator),private,passthrough。形象举例,假如父接口是一个聊天群,所有的群成员都可以向外发消息,那么可以这样理解:
- bridge 模式,所有群成员可以在群内发言。
- private 模式,所有群成员禁言的,既不能在群内发言,也不能在群外发言。
- vepa 模式,所有群成员在群内禁言,但是可以在群外互相私聊(需要外部交换支持802.1q)。
- passthrough 模式,只有群主可以发言,其他人禁言。
1.4 收发包流程
本节内容参考 链接2、链接5
1.4.1 收包流程
IPVlan 子设备的三种 mode 分别有不同的收包处理流程,在内核的流程如下:
首先会经过__netif_receive_skb_core 进入到创建时注册的 ipvlan_handle_frame 的处理流程,此时数据包依然是主设备所拥有。
rx_handler_result_t ipvlan_handle_frame(struct sk_buff **pskb)
{
struct sk_buff *skb = *pskb;
struct ipvl_port *port = ipvlan_port_get_rcu(skb->dev);
if (!port)
return RX_HANDLER_PASS;
switch (port->mode) {
case IPVLAN_MODE_L2:
return ipvlan_handle_mode_l2(pskb, port);
case IPVLAN_MODE_L3:
return ipvlan_handle_mode_l3(pskb, port);
#ifdef CONFIG_IPVLAN_L3S
case IPVLAN_MODE_L3S:
return RX_HANDLER_PASS;
#endif
}
/* Should not reach here */
WARN_ONCE(true, "%s called for mode = [%x]\n", __func__, port->mode);
kfree_skb(skb);
return RX_HANDLER_CONSUMED;
}
-
ipvlan l2
对于 mode l2 模式的报文处理,只处理多播的报文,将报文放进前面创建子设备时初始化的多播处理的队列;对于单播报文,会直接交给 ipvlan_handle_mode_l3 进行处理!
static rx_handler_result_t ipvlan_handle_mode_l2(struct sk_buff **pskb,
struct ipvl_port *port)
{
struct sk_buff *skb = *pskb;
struct ethhdr *eth = eth_hdr(skb);
rx_handler_result_t ret = RX_HANDLER_PASS;
// 多播
if (is_multicast_ether_addr(eth->h_dest)) {
if (ipvlan_external_frame(skb, port)) {
struct sk_buff *nskb = skb_clone(skb, GFP_ATOMIC);
/* External frames are queued for device local
* distribution, but a copy is given to master
* straight away to avoid sending duplicates later
* when work-queue processes this frame. This is
* achieved by returning RX_HANDLER_PASS.
*/
if (nskb) {
ipvlan_skb_crossing_ns(nskb, NULL);
ipvlan_multicast_enqueue(port, nskb, false);
}
}
// 单播
} else {
/* Perform like l3 mode for non-multicast packet */
ret = ipvlan_handle_mode_l3(pskb, port);
}
return ret;
}
-
ipvlan l3
对于 mode l3 或者单播的 mode l2 报文,进入 ipvlan_handle_mode_l3 处理流程,首先通过 ipvlan_get_L3_hdr 获取到网络层的头信息,然后根据 ip 地址去查找到对应的子设备,最后调用 ipvlan_rcv_frame,将报文的 dev 设置为 IPVlan 子设备并返回 RX_HANDLER_ANOTHER,进行下一次收包。
static rx_handler_result_t ipvlan_handle_mode_l3(struct sk_buff **pskb,
struct ipvl_port *port)
{
void *lyr3h;
int addr_type;
struct ipvl_addr *addr;
struct sk_buff *skb = *pskb;
rx_handler_result_t ret = RX_HANDLER_PASS;
lyr3h = ipvlan_get_L3_hdr(port, skb, &addr_type);
if (!lyr3h)
goto out;
// 寻找目标地址子设备
addr = ipvlan_addr_lookup(port, lyr3h, addr_type, true);
if (addr)
// 对应子设备收包
ret = ipvlan_rcv_frame(addr, pskb, false);
out:
// 走主设备路由
return ret;
}
-
ipvlan l3s
对于 mode l3s,在 ipvlan_handle_frame 中会直接返回 RX_HANDLER_PASS,也就是说,mode l3s 的报文会在主设备就进入到网络层的处理阶段,对于 mode l3s 来说,预先注册的 nf_hook 会在 NF_INET_LOCAL_IN 时触发,执行 ipvlan_l3_rcv 操作,通过 addr 找到子设备,更换报文的网络层目的地址,然后直接进入 ip_local_deliver 进行网络层余下的操作。
case IPVLAN_MODE_L3S:
return RX_HANDLER_PASS;
具体请参考:ipvlan-l3s模式
1.4.2 发包流程
ipvlan_queue_xmit 根据子设备的模式选择不同的发送方法,mode l2 通过 ipvlan_xmit_mode_l2 发送,mode l3 和 mode l3s 进行 ipvlan_xmit_mode_l3 发送。
int ipvlan_queue_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct ipvl_dev *ipvlan = netdev_priv(dev);
struct ipvl_port *port = ipvlan_port_get_rcu_bh(ipvlan->phy_dev);
if (!port)
goto out;
if (unlikely(!pskb_may_pull(skb, sizeof(struct ethhdr))))
goto out;
switch(port->mode) {
// l2
case IPVLAN_MODE_L2:
return ipvlan_xmit_mode_l2(skb, dev);
// l3
case IPVLAN_MODE_L3:
#ifdef CONFIG_IPVLAN_L3S
// ls3
case IPVLAN_MODE_L3S:
#endif
return ipvlan_xmit_mode_l3(skb, dev);
}
/* Should not reach here */
WARN_ONCE(true, "%s called for mode = [%x]\n", __func__, port->mode);
out:
kfree_skb(skb);
return NET_XMIT_DROP;
}
-
ipvlan_xmit_mode_l2
1. 对于 ipvlan_xmit_mode_l2,首先判断是否是本地地址或者 VEPA 模式,如果不是 VEPA 模式的本地报文,则首先通过 ipvlan_addr_lookup 查找是否是相同主设备下的 IPVlan 子设备,如果是相同主设备下的 IPVlan 子设备,则通过 ipvlan_rcv_frame 让对应子设备进行收包处理;如果不是,则通过 dev_forward_skb 让主设备进行处理。
2. 接下来 ipvlan_xmit_mode_l2 会对多播报文进行处理,在处理之前,通过 ipvlan_skb_crossing_ns 清理掉数据包的 netns 相关的信息,包括 priority 等,最后将数据包放到 ipvlan_multicast_enqueue,触发上述的多播处理流程。
3. 对于非本地的数据包,通过主设备的 dev_queue_xmit 进行发送。
static int ipvlan_xmit_mode_l2(struct sk_buff *skb, struct net_device *dev)
{
const struct ipvl_dev *ipvlan = netdev_priv(dev);
struct ethhdr *eth = skb_eth_hdr(skb);
struct ipvl_addr *addr;
void *lyr3h;
int addr_type;
// ipvlan模式不是vepa 且 源mac 与 目标mac 相同
if (!ipvlan_is_vepa(ipvlan->port) &&
ether_addr_equal(eth->h_dest, eth->h_source)) {
lyr3h = ipvlan_get_L3_hdr(ipvlan->port, skb, &addr_type);
if (lyr3h) {
// 寻找目标地址
addr = ipvlan_addr_lookup(ipvlan->port, lyr3h, addr_type, true);
if (addr) {
// ipvlan模式是 private ,那么 drop
if (ipvlan_is_private(ipvlan->port)) {
consume_skb(skb);
return NET_XMIT_DROP;
}
// 调用该目标地址的子设备收包
ipvlan_rcv_frame(addr, &skb, true);
return NET_XMIT_SUCCESS;
}
}
skb = skb_share_check(skb, GFP_ATOMIC);
if (!skb)
return NET_XMIT_DROP;
/* Packet definitely does not belong to any of the
* virtual devices, but the dest is local. So forward
* the skb for the main-dev. At the RX side we just return
* RX_PASS for it to be processed further on the stack.
*/
// 如果不属于子设备,直接送主设备处理
dev_forward_skb(ipvlan->phy_dev, skb);
return NET_XMIT_SUCCESS;
// 处理多播包
} else if (is_multicast_ether_addr(eth->h_dest)) {
skb_reset_mac_header(skb);
ipvlan_skb_crossing_ns(skb, NULL);
ipvlan_multicast_enqueue(ipvlan->port, skb, true);
return NET_XMIT_SUCCESS;
}
// 是vepa 或 源mac与目标mac不同,且不是多播包,直接送主设备发出
skb->dev = ipvlan->phy_dev;
return dev_queue_xmit(skb);
}
-
ipvlan_xmit_mode_l3
1. ipvlan_xmit_mode_l3 的处理首先也是对 VEPA 进行判断,对于非 VEPA 模式的数据包,通过ipvlan_addr_lookup 查找是否是其他子设备,如果是其他子设备则调用 ipvlan_rcv_frame 触发对应子设备进行收包处理。
2. 对于 VEPA 模式或目标地址不是本地的数据包,首先进行 ipvlan_skb_crossing_ns 的处理,然后进行 ipvlan_process_outbound的操作,此时根据数据包的网络层协议,选择 ipvlan_process_v4_outbound 或者 ipvlan_process_v6_outbound 进行处理。
3. 以 ipvlan_process_v4_outbound 为例,首先会通过 ip_route_output_flow 进行路由的查找,然后直接通过网络层的 ip_local_out,在主设备的网络层继续进行发包操作。
static int ipvlan_xmit_mode_l3(struct sk_buff *skb, struct net_device *dev)
{
const struct ipvl_dev *ipvlan = netdev_priv(dev);
void *lyr3h;
struct ipvl_addr *addr;
int addr_type;
lyr3h = ipvlan_get_L3_hdr(ipvlan->port, skb, &addr_type);
if (!lyr3h)
goto out;
// 如果ipvlan模式不是vepa
if (!ipvlan_is_vepa(ipvlan->port)) {
// 寻找目标地址
addr = ipvlan_addr_lookup(ipvlan->port, lyr3h, addr_type, true);
// 如果找到了目标地址
if (addr) {
// 如果ipvlan模式是 private ,那么丢弃
if (ipvlan_is_private(ipvlan->port)) {
consume_skb(skb);
return NET_XMIT_DROP;
}
// 只剩下bridge模式,调用该目标地址的子设备收包
ipvlan_rcv_frame(addr, &skb, true);
return NET_XMIT_SUCCESS;
}
}
out:
// 是vepa 或 目标地址不是本子设备,交给主设备,ip_route_output_flow查找路由表,走三层转发
ipvlan_skb_crossing_ns(skb, ipvlan->phy_dev);
return ipvlan_process_outbound(skb);
}