calico vxlan ipv4 overlay组网跨主机通信分析

主机信息

演示环境.png

由图可以看出overlay跨主机通信的细节还是很复杂,经过了多个网卡后才经由物理网卡发出。podEth0 -> cali39d -> vxlan.calico -> ens33。本文详细解析了数据包是如何在内核协议栈流转的,vxlan设备封装报文的具体过程。
本篇包含以下内容 :

  • pod跨命名空间通信
  • calixx网卡到vxlan设备通信
  • vxlan设备到物理设备通信
  • 添加路由内核代码分析
  • 路由条目中的onlink参数

pod内的eth0 -> calixx网卡

参见链接 https://www.jianshu.com/p/75392d686c59

calixxx -> vxlan.calico

calixxx 为容器命名空间内网卡veth对的另一端,存在于主机的命名空间内。
calixx收到二层包之后,向上进行传递。

netfilter.png
协议栈处理.png

1、 calixx驱动收到报文后,经过二层处理,经过ip_rcv进入ip层处理。
2、 通过查找路由,确定了该包需要经过vxlan.calico发出去,下一跳的ip地址是对端的vtep的地址,对端vtep的mac地址已经由felix写入到主机的邻居表项中。这里要重点理解下一跳的含义。
3、 由于已经知道了对端vtep设备的mac地址,因为这里不再需要进行mac地址查询,直接由neigh_output交由下层vxlan设备处理。

路由条目中的onlink

这里先写个neilink处理消息的大概逻辑,后续补充个流程图
下一跳是否可达,就是判断下一跳的地址是否在已有的路由中
执行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

添加路由.png
// netlink消息的协议处理函数
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,
    // 内核收到用户态的消息后的处理函数
    .sendmsg =  netlink_sendmsg,
    // 用户读取netlink消息的处理函数
    .recvmsg =  netlink_recvmsg,
    .mmap =     sock_no_mmap,
    .sendpage = sock_no_sendpage,
};

// 注册增加路由条目的处理函数
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函数
        nlk_sk(sk)->netlink_rcv = cfg->input;
}

// 根据perf抓取到的调用栈,继续向下分析
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);
        // 这里就调用到了上面的cfg的input函数,即rtnetlink_rcv函数
        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就是注册的新增路由表的处理函数inet_rtm_newroute
        if (doit)
            err = doit(skb, nlh, extack);
        module_put(owner);
        return err;
    }
}

// 对于下一跳的检查函数,内核注释中写明了下一跳很复杂,是由于历史原因,,,
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参数,单独处理
        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是否可达。
        {
            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设备到物理网卡

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,
};
vxlan.png

这里这个箭头从下面指到上面并不是画图空间不足,而是为了表现数据包是从二层又回到了三层的处理中。
1、vxlan设备的转发表中记录了对端vtep的mac地址和remoteip的对应关系。
2、 封装udp,和ip头后,经由ip_local_out重新进入ip层处理,经过output和postrouting后,由ip_finish_output2出ip层。这里要重点理解,vxlan驱动将上层包伪装成一个要出本机的正常的数据包,由ip_local_out进入ip层处理,因为ip_local_out是一个正常的出本机udp,tcp包在ip层处理的入口函数。要正确理解vxlan设备的封装过程以及vxlan设备在这个过程中起到的作用。
3、 经过查询路由,与本机处于同网段,通过mac地址查询获取到对端物理网卡的mac地址,经由物理网卡发送。如果与本机不在同一网段,则将包交由网关处理。

物理网卡

static const struct net_device_ops ixgb_netdev_ops = {
    .ndo_start_xmit     = ixgb_xmit_frame,
};

wireshark分析

抓包.png

perf抓取内核调用栈

在pod内ping另一台主机上的pod的ip。使用perf抓取指定函数的调用堆栈。

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
perf使用.png

使用ftrace抓取内核调用栈

在pod内ping另一台主机上的pod的ip。使用ftrace抓取指定函数的调用堆栈。

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,463评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,868评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,213评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,666评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,759评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,725评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,716评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,484评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,928评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,233评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,393评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,073评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,718评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,308评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,538评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,338评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,260评论 2 352

推荐阅读更多精彩内容