主機(jī)信息

由圖可以看出overlay跨主機(jī)通信的細(xì)節(jié)還是很復(fù)雜,經(jīng)過了多個(gè)網(wǎng)卡后才經(jīng)由物理網(wǎng)卡發(fā)出。podEth0 -> cali39d -> vxlan.calico -> ens33。本文詳細(xì)解析了數(shù)據(jù)包是如何在內(nèi)核協(xié)議棧流轉(zhuǎn)的,vxlan設(shè)備封裝報(bào)文的具體過程。
本篇包含以下內(nèi)容 :
- pod跨命名空間通信
- calixx網(wǎng)卡到vxlan設(shè)備通信
- vxlan設(shè)備到物理設(shè)備通信
- 添加路由內(nèi)核代碼分析
- 路由條目中的onlink參數(shù)
pod內(nèi)的eth0 -> calixx網(wǎng)卡
參見鏈接 http://www.itdecent.cn/p/75392d686c59
calixxx -> vxlan.calico
calixxx 為容器命名空間內(nèi)網(wǎng)卡veth對(duì)的另一端,存在于主機(jī)的命名空間內(nèi)。
calixx收到二層包之后,向上進(jìn)行傳遞。


1、 calixx驅(qū)動(dòng)收到報(bào)文后,經(jīng)過二層處理,經(jīng)過ip_rcv進(jìn)入ip層處理。
2、 通過查找路由,確定了該包需要經(jīng)過vxlan.calico發(fā)出去,下一跳的ip地址是對(duì)端的vtep的地址,對(duì)端vtep的mac地址已經(jīng)由felix寫入到主機(jī)的鄰居表項(xiàng)中。這里要重點(diǎn)理解下一跳的含義。
3、 由于已經(jīng)知道了對(duì)端vtep設(shè)備的mac地址,因?yàn)檫@里不再需要進(jìn)行mac地址查詢,直接由neigh_output交由下層vxlan設(shè)備處理。
路由條目中的onlink
這里先寫個(gè)neilink處理消息的大概邏輯,后續(xù)補(bǔ)充個(gè)流程圖
下一跳是否可達(dá),就是判斷下一跳的地址是否在已有的路由中
執(zhí)行perf record -e probe:fib_check_nh -e probe:netlink_recvmsg -agR ip r add 5.5.6.0/24 via 4.4.10.2 dev ens192 onlink

// netlink消息的協(xié)議處理函數(shù)
static const struct proto_ops netlink_ops = {
.family = PF_NETLINK,
.owner = THIS_MODULE,
.release = netlink_release,
.bind = netlink_bind,
.connect = netlink_connect,
.socketpair = sock_no_socketpair,
.accept = sock_no_accept,
.getname = netlink_getname,
.poll = datagram_poll,
.ioctl = netlink_ioctl,
.listen = sock_no_listen,
.shutdown = sock_no_shutdown,
.setsockopt = netlink_setsockopt,
.getsockopt = netlink_getsockopt,
// 內(nèi)核收到用戶態(tài)的消息后的處理函數(shù)
.sendmsg = netlink_sendmsg,
// 用戶讀取netlink消息的處理函數(shù)
.recvmsg = netlink_recvmsg,
.mmap = sock_no_mmap,
.sendpage = sock_no_sendpage,
};
// 注冊(cè)增加路由條目的處理函數(shù)
rtnl_register(PF_INET, RTM_NEWROUTE, inet_rtm_newroute, NULL, 0);
struct netlink_kernel_cfg cfg = {
.groups = RTNLGRP_MAX,
.input = rtnetlink_rcv,
.cb_mutex = &rtnl_mutex,
.flags = NL_CFG_F_NONROOT_RECV,
.bind = rtnetlink_bind,
};
struct sock *
__netlink_kernel_create(struct net *net, int unit, struct module *module,
struct netlink_kernel_cfg *cfg)
{
if (cfg && cfg->input)
// 修改netlink_rcv 為 rtnetlink_rcv函數(shù)
nlk_sk(sk)->netlink_rcv = cfg->input;
}
// 根據(jù)perf抓取到的調(diào)用棧,繼續(xù)向下分析
static int netlink_unicast_kernel(struct sock *sk, struct sk_buff *skb,
struct sock *ssk)
{
int ret;
struct netlink_sock *nlk = nlk_sk(sk);
ret = -ECONNREFUSED;
if (nlk->netlink_rcv != NULL) {
ret = skb->len;
netlink_skb_set_owner_r(skb, sk);
NETLINK_CB(skb).sk = ssk;
netlink_deliver_tap_kernel(sk, ssk, skb);
// 這里就調(diào)用到了上面的cfg的input函數(shù),即rtnetlink_rcv函數(shù)
nlk->netlink_rcv(skb);
consume_skb(skb);
} else {
kfree_skb(skb);
}
sock_put(sk);
return ret;
}
// 處理子模塊的消息
static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh,
struct netlink_ext_ack *extack)
{
link = rtnl_get_link(family, type);
if (flags & RTNL_FLAG_DOIT_UNLOCKED) {
doit = link->doit;
rcu_read_unlock();
// doit就是注冊(cè)的新增路由表的處理函數(shù)inet_rtm_newroute
if (doit)
err = doit(skb, nlh, extack);
module_put(owner);
return err;
}
}
// 對(duì)于下一跳的檢查函數(shù),內(nèi)核注釋中寫明了下一跳很復(fù)雜,是由于歷史原因,,,
static int fib_check_nh(struct fib_config *cfg, struct fib_nh *nh,
struct netlink_ext_ack *extack)
{
if (nh->nh_gw) {
struct fib_result res;
// 有onlink參數(shù),單獨(dú)處理
if (nh->nh_flags & RTNH_F_ONLINK) {
dev = __dev_get_by_index(net, nh->nh_oif);
addr_type = inet_addr_type_dev_table(net, dev, nh->nh_gw);
nh->nh_dev = dev;
dev_hold(dev);
nh->nh_scope = RT_SCOPE_LINK;
return 0;
}
// 沒有onlink,需要查找下一跳nh是否可達(dá)。
{
struct fib_table *tbl = NULL;
struct flowi4 fl4 = {
.daddr = nh->nh_gw,
.flowi4_scope = cfg->fc_scope + 1,
.flowi4_oif = nh->nh_oif,
.flowi4_iif = LOOPBACK_IFINDEX,
};
if (cfg->fc_table)
tbl = fib_get_table(net, cfg->fc_table);
if (tbl)
err = fib_table_lookup(tbl, &fl4, &res,
FIB_LOOKUP_IGNORE_LINKSTATE |
FIB_LOOKUP_NOREF);
if (!tbl || err) {
err = fib_lookup(net, &fl4, &res,
FIB_LOOKUP_IGNORE_LINKSTATE);
}
if (err) {
NL_SET_ERR_MSG(extack,
"Nexthop has invalid gateway");
rcu_read_unlock();
return err;
}
}
}
vxlan設(shè)備到物理網(wǎng)卡
static const struct net_device_ops vxlan_netdev_ether_ops = {
.ndo_init = vxlan_init,
.ndo_uninit = vxlan_uninit,
.ndo_open = vxlan_open,
.ndo_stop = vxlan_stop,
.ndo_start_xmit = vxlan_xmit,
.ndo_get_stats64 = ip_tunnel_get_stats64,
};

這里這個(gè)箭頭從下面指到上面并不是畫圖空間不足,而是為了表現(xiàn)數(shù)據(jù)包是從二層又回到了三層的處理中。
1、vxlan設(shè)備的轉(zhuǎn)發(fā)表中記錄了對(duì)端vtep的mac地址和remoteip的對(duì)應(yīng)關(guān)系。
2、 封裝udp,和ip頭后,經(jīng)由ip_local_out重新進(jìn)入ip層處理,經(jīng)過output和postrouting后,由ip_finish_output2出ip層。這里要重點(diǎn)理解,vxlan驅(qū)動(dòng)將上層包偽裝成一個(gè)要出本機(jī)的正常的數(shù)據(jù)包,由ip_local_out進(jìn)入ip層處理,因?yàn)閕p_local_out是一個(gè)正常的出本機(jī)udp,tcp包在ip層處理的入口函數(shù)。要正確理解vxlan設(shè)備的封裝過程以及vxlan設(shè)備在這個(gè)過程中起到的作用。
3、 經(jīng)過查詢路由,與本機(jī)處于同網(wǎng)段,通過mac地址查詢獲取到對(duì)端物理網(wǎng)卡的mac地址,經(jīng)由物理網(wǎng)卡發(fā)送。如果與本機(jī)不在同一網(wǎng)段,則將包交由網(wǎng)關(guān)處理。
物理網(wǎng)卡
static const struct net_device_ops ixgb_netdev_ops = {
.ndo_start_xmit = ixgb_xmit_frame,
};
wireshark分析

perf抓取內(nèi)核調(diào)用棧
在pod內(nèi)ping另一臺(tái)主機(jī)上的pod的ip。使用perf抓取指定函數(shù)的調(diào)用堆棧。
perf probe --add iptunnel_xmit
perf probe --add ip_output
perf record -e probe:iptunnel_xmit -e probe:ip_output -ag -F max sleep 10

使用ftrace抓取內(nèi)核調(diào)用棧
在pod內(nèi)ping另一臺(tái)主機(jī)上的pod的ip。使用ftrace抓取指定函數(shù)的調(diào)用堆棧。
echo iptunnel_xmit > /sys/kernel/debug/tracing/set_ftrace_filter
echo function > /sys/kernel/debug/tracing/current_tracer
echo 1 > /sys/kernel/debug/tracing/options/func_stack_trace
cat /sys/kernel/debug/tracing/trace
<...>-527743 [001] ..s1 98518.998187: iptunnel_xmit <-vxlan_xmit_one
<...>-527743 [001] ..s1 98518.998199: <stack trace>
=> iptunnel_xmit
=> vxlan_xmit_one
=> vxlan_xmit
=> dev_hard_start_xmit
=> __dev_queue_xmit
=> ip_finish_output2
=> ip_output
=> ip_forward
=> ip_rcv
=> __netif_receive_skb_one_core
=> process_backlog
=> net_rx_action
=> __do_softirq
=> do_softirq_own_stack
=> do_softirq
=> __local_bh_enable_ip
=> ip_finish_output2
=> ip_output
=> ip_send_skb
=> raw_sendmsg
=> sock_sendmsg
=> __sys_sendto
=> __x64_sys_sendto
=> do_syscall_64
=> entry_SYSCALL_64_after_hwframe