目前内核支持的封装类型由枚举类型lwtunnel_encap_types定义,如下所示支持MPLS、IP、ILA、IP6、SEG6、BPF和SEG6_LOCAL等7种类型。函数lwtunnel_valid_encap_type负责检验用户配置(通过netlink接口)的封装类型是否合法,合法的封装类型必须大于LWTUNNEL_ENCAP_NONE,并且小于__LWTUNNEL_ENCAP_MAX;而且内核必须已经注册了相应封装类型的操作函数集(由结构体lwtunnel_encap_add_ops表示),其通过函数lwtunnel_encap_add_ops进行注册。

enum lwtunnel_encap_types {

LWTUNNEL_ENCAP_NONE,

LWTUNNEL_ENCAP_MPLS,

LWTUNNEL_ENCAP_IP,

LWTUNNEL_ENCAP_ILA,

LWTUNNEL_ENCAP_IP6,

LWTUNNEL_ENCAP_SEG6,

LWTUNNEL_ENCAP_BPF,

LWTUNNEL_ENCAP_SEG6_LOCAL,

__LWTUNNEL_ENCAP_MAX,

};

内核使用结构体lwtunnel_encap_ops表示特定的封装类型操作函数集,系统中所有注册的封装类型操作函数集保存在全局数组lwtun_encaps中。

static const struct lwtunnel_encap_ops __rcu *lwtun_encaps[LWTUNNEL_ENCAP_MAX + 1] __read_mostly;

下表列出了内核中封装类型和操作函数集的对应信息:

+---------------------------+-------------------+--------------------+

| LWTUNNEL_ENCAP_MPLS | mpls_iptun_ops | mpls_iptunnel.c |

+---------------------------+-------------------+--------------------+

| LWTUNNEL_ENCAP_IP | ip_tun_lwt_ops | ip_tunnel_core.c |

+---------------------------+-------------------+--------------------+

| LWTUNNEL_ENCAP_ILA | ila_encap_ops | ila_lwt.c |

+---------------------------+-------------------+--------------------+

| LWTUNNEL_ENCAP_IP6 | ip6_tun_lwt_ops | ip_tunnel_core.c |

+---------------------------+-------------------+--------------------+

| LWTUNNEL_ENCAP_SEG6 | seg6_iptun_ops | seg6_iptunnel.c |

+---------------------------+-------------------+--------------------+

| LWTUNNEL_ENCAP_BPF | bpf_encap_ops | lwt_bpf.c |

+---------------------------+-------------------+--------------------+

| LWTUNNEL_ENCAP_SEG6_LOCAL | seg6_local_ops | seg6_local.c |

+---------------------------+-------------------+--------------------+

路由系统隧道配置

LWTUNNEL系统的功能实现绑定在内核路由系统上。如下IP命令用于配置封装类型为IP或者GENEVE的轻量级隧道。

使用external关键字配置IP隧道与GENEVE隧道:

ip link add vxlan1 type vxlan dstport 4789 external

ip link set dev vxlan1 up

ip addr add 20.1.1.1/24 dev vxlan1

ip route add 10.1.1.1 encap ip id 30001 dst 20.1.1.2 dev vxlan1

ip link add gnv0 type geneve external

ip link set dev gnv0 up

ip route add 172.16.20.1/32 encap ip id 1234 dst 192.168.100.3 dev gnv0

不使用external配置vxlan与geneve隧道:

ip link add dev vxlan1 type vxlan id 30001 remote 20.1.1.2 dstport 4789

ip route add 10.1.1.1 dev vxlan1

ip link add dev gnv0 type geneve remote 192.168.100.3 vni 1234 dstport 5678

ip route add 172.16.20.1 dev gnv0

与路由系统结合在一起的LWTUNNEL轻量级隧道,将隧道信息保存在路由系统中。内核中函数inet_rtm_newroute负责处理以上的ip route命令新添加的路由表项:其首先调用函数rtm_to_fib_config将netlink消息中的数据转换保存到内核的fib_config结构体中,成员fc_encap_type保存封装类型,成员fc_encap保存整个封装数据:

static int rtm_to_fib_config(struct net *net, struct sk_buff *skb, struct nlmsghdr *nlh, struct fib_config *cfg, ...)

{

nlmsg_for_each_attr(attr, nlh, sizeof(struct rtmsg), remaining) {

switch (nla_type(attr)) {

case RTA_ENCAP:

cfg->fc_encap = attr;

break;

case RTA_ENCAP_TYPE:

cfg->fc_encap_type = nla_get_u16(attr);

err = lwtunnel_valid_encap_type(cfg->fc_encap_type, extack);

break;

}

}

}

其次调用fib_table_insert函数将路由表项插入到内核FIB表中,fib_create_info负责在此之前创建要插入的fib_info结构信息。其检查fib_config结构的成员fc_encap是否有值,有值表明配置了隧道封装信息。随即创建特定隧道封装的lwtunnel_state信息,并且保存在fib_info的成员fib_nh下一跳数据结构中,以供路由匹配到相应流量时使用。

struct fib_info *fib_create_info(struct fib_config *cfg, struct netlink_ext_ack *extack)

{

struct fib_nh *nh = fi->fib_nh;

if (cfg->fc_encap) {

struct lwtunnel_state *lwtstate;

err = lwtunnel_build_state(cfg->fc_encap_type, cfg->fc_encap, AF_INET, cfg, &lwtstate, extack);

nh->nh_lwtstate = lwtstate_get(lwtstate);

}

}

以IP封装类型为例,其build_state函数为ip_tun_build_state,除了要分配通用的lwtunnel_state数据结构外,还分配了特定于IP封装的ip_tunnel_info结构体(紧随lwtunnel_state结构体空间之后),特定于IP封装类型的信息保存在ip_tunnel_info私有结构体中,如封装的id、dst等信息。每种特定的封装如有必要都会分配私有的数据结构,如MPLS封装,在mpls_build_state函数中,分配mpls_iptunnel_encap数据结构以及label所需空间。

static int ip_tun_build_state(...)

{

struct ip_tunnel_info *tun_info;

struct lwtunnel_state *new_state;

new_state = lwtunnel_state_alloc(sizeof(*tun_info));

new_state->type = LWTUNNEL_ENCAP_IP;

tun_info = lwt_tun_info(new_state);

if (tb[LWTUNNEL_IP_ID])

tun_info->key.tun_id = nla_get_be64(tb[LWTUNNEL_IP_ID]);

}

轻量级隧道路由入口

进入本机的数据包,在经过NF_INET_PRE_ROUTING hook点之后,交由内核函数ip_rcv_finish处理,其通过查找路由表决定数据包的流向,即ip_route_input_noref函数查询入口路由,决定是送往本地应用还是转发。在查询到路由表项后使用__mkroute_input函数创建路由缓存结构rtable。

static int ip_rcv_finish(struct net *net, struct sock *sk, struct sk_buff *skb)

{

if (!skb_valid_dst(skb)) {

err = ip_route_input_noref(skb, iph->daddr, iph->saddr, iph->tos, dev);

}

return dst_input(skb);

}

static int __mkroute_input(struct sk_buff *skb, const struct fib_result *res, struct in_device *in_dev, __be32 daddr, __be32 saddr, u32 tos)

{

rth->dst.input = ip_forward;

rt_set_nexthop(rth, daddr, res, fnhe, res->fi, res->type, itag, do_cache);

set_lwt_redirect(rth);

skb_dst_set(skb, &rth->dst);

}

其中rt_set_nexthop函数,根据路由的下一跳信息,找到在上一节中使用ip route命令配置封装路由时,创建的lwtunnel_state结构。其保存有指定的封装类型相关的信息,赋值给路由缓存结构中的lwtstate成员。

static void rt_set_nexthop(...)

{

if (fi) {

struct fib_nh *nh = &FIB_RES_NH(*res);

rt->dst.lwtstate = lwtstate_get(nh->nh_lwtstate);

}

}

如果配置了路由相关的轻量级隧道,路由的结果出了本地上送和转发之后,第三条路就是重定向到轻量级隧道中,即函数set_lwt_redirect,如果此关联隧道为输入方向(设置了LWTUNNEL_STATE_INPUT_REDIRECT标志),将其input函数指针修改为隧道自身的输入函数lwtunnel_input,并且保留原有的input指针函数到orig_input成员中,对于像BPF类型的隧道,在其进行完bpf相关处理后,还会调用orig_input指针函数继续被重定向之前的接收流程。

static void set_lwt_redirect(struct rtable *rth)

{

if (lwtunnel_output_redirect(rth->dst.lwtstate)) {

rth->dst.lwtstate->orig_output = rth->dst.output;

rth->dst.output = lwtunnel_output;

}

if (lwtunnel_input_redirect(rth->dst.lwtstate)) {

rth->dst.lwtstate->orig_input = rth->dst.input;

rth->dst.input = lwtunnel_input;

}

}

最终,在路由查询完成之后,ip_rcv_finish函数调用路由指定的input函数dst_input,此处调用的为lwtunnel_input函数。

轻量级隧道路由出口

出口函数的设置位于出口路由的生成函数__mkroute_output中。即在查找到出口路由之后,生成rtable路由缓存项时,检查rtable下一跳中是否关联有轻量级隧道状态信息lwtunnel_state,如果存在并且此隧道为出口方向(置位LWTUNNEL_STATE_OUTPUT_REDIRECT),路由缓存结构的output指针指向隧道的lwtunnel_output函数。此过程与LWTUNNEL路由入口的处理方式一致,仅是方向上的差别。

static struct rtable *__mkroute_output(const struct fib_result *res, const struct flowi4 *fl4, ...)

{

struct fib_info *fi = res->fi;

struct fib_nh_exception *fnhe;

rt_set_nexthop(rth, fl4->daddr, res, fnhe, fi, type, 0, do_cache);

set_lwt_redirect(rth);

}

rth->dst.output函数由dst_output进行封装调用,对于源自本机的数据包,调用位置在NF_INET_LOCAL_OUT hook点之后,参见函数__ip_local_out,遍历完此hook点挂载的函数之后,调用dst_output,实际为调用lwtunnel_output函数。

int __ip_local_out(struct net *net, struct sock *sk, struct sk_buff *skb)

{

return nf_hook(NFPROTO_IPV4, NF_INET_LOCAL_OUT,

net, sk, skb, NULL, skb_dst(skb)->dev, dst_output);

}

对于转发的数据包,其调用位置位于函数ip_forward_finish中,在hook点NF_INET_FORWARD遍历完成之后调用。

int ip_forward(struct sk_buff *skb)

{

return NF_HOOK(NFPROTO_IPV4, NF_INET_FORWARD,

net, NULL, skb, skb->dev, rt->dst.dev, ip_forward_finish);

}

static int ip_forward_finish(struct net *net, struct sock *sk, struct sk_buff *skb)

{

return dst_output(net, sk, skb);

}

传输类型XMIT

之前阐述的通过set_lwt_redirect函数重定向的轻量级隧道为INPUT或者OUTPUT类型,通过dst_input或者dst_output函数调用嫁接在内核网络协议栈中。此处的XMIT传输类型,直接编码在ip_finish_output2函数中,此处理位于NF_INET_POST_ROUTING hook点之后,目前仅有MPLS隧道在使用,其通过mpls_xmit函数为数据包增加MPLS标签,之后将数据包发送到网络中。

static int ip_finish_output2(struct net *net, struct sock *sk, struct sk_buff *skb)

{

if (lwtunnel_xmit_redirect(dst->lwtstate)) {

int res = lwtunnel_xmit(skb);

if (res < 0 || res == LWTUNNEL_XMIT_DONE)

return res;

}

}

内核版本

Linux-4.15

linux内核支持我vxlan,Linux内核轻量级隧道相关推荐

  1. 如何修改CentOS6、CentOS7内核支持安装锐速的内核

    如何修改CentOS6.CentOS7内核支持安装锐速的内核 摘要: 在锐速ServerSpeeder无限带宽破解版Linux一键自动安装包文章中介绍了如何使用一键包安装锐速,优化服务器网络速度.其实 ...

  2. 4.19.90内核支持pci=reorder这个内核启动参数吗?

    是的,4.19.90内核支持pci=reorder这个内核启动参数.这个参数是用来重新排列PCI设备的初始化顺序的.它可以在启动内核时使用,方法是在内核命令行中加入pci=reorder参数.这个参数 ...

  3. windows命令行下访问linux,Windows支持直接访问Linux子系统文件:你的下一台Linux何必是Linux...

    原标题:Windows支持直接访问Linux子系统文件:你的下一台Linux何必是Linux 晓查 发自 凹非寺 量子位 报道 | 公众号 QbitAI 微软,致力于做最好的Linux发行版. 今天, ...

  4. windows兼容Linux php,支持windows与linux的php计划任务的实现方法

    本文实例讲述了支持windows与linux的php计划任务的实现方法.包括了在winows下利用winodows计划任务来操作,还有在linux中利用linux的方法来实现.分享给大家供大家参考.具 ...

  5. linux内核支持的加密算法,Linux Kernel(Android) 加密算法总结(三)-应用程序调用内核加密算法接口...

    本文将主要介绍,如何在应用程序空间中(user space) 调用内核空间(kernel space)加密模块提供的加密算法API. 方法一:通过调用crypto: af_alg - User-spa ...

  6. linux内核支持浮动ip,Linux配置浮动IP

    在高可用集群环境中,一般都需要使用浮动IP来实现web高可用(High Availability). 本篇文章主要讲实际操作步骤: 可以是双机,也可以是多机,主服务器为10.61.1.22,从服务器为 ...

  7. linux内核支持浮动ip,Linux配置浮动IP实现WEB高可用

    在高可用集群环境中,一般都需要使用浮动IP来实现web高可用(High Availability). 浮动IP的概念以及为什么需要浮动IP请参考:浮动IP(FLOAT IP) 本篇文章主要讲实际操作步 ...

  8. linux系统支持ntfs吗,linux支持NTFS

    我的linux系统是redhat的enterprise5,缺省的情况下是不支持NTFS系统的.需要安装相应的模块.我的安装步骤是: 1,到href="" target=_blank ...

  9. linux不支持modprobe命令,Linux中modprobe命令起什么作用呢?

    摘要: 下文讲述Linux中modprobe的功能说明,如下所示: modprobe命令功能: 用于采用智能方式向内核中加载模块 或从内核中移除模块 modprobe命令的原理: modprobe命令 ...

最新文章

  1. 爬虫笔记|r.text-r.request.headers|修改,头
  2. Android 学习历程摘要(一)
  3. LF将由git中的CRLF替换-那是什么,它很重要吗? [重复]
  4. Python实现网页截图
  5. mysql8.0编译安装
  6. exp-imp实现oracle不同表空间的迁移
  7. ResourceID(frameworks/base/libs/utils/README)
  8. JAVA程序运行原理分析
  9. 注意!这些行为均涉嫌科研不端!
  10. golang CI: Use result of type assertion to simplify cases SCC-S1034
  11. 冲刺周期一--站立会议05
  12. 另类方法激活你的Winodws 2008
  13. Android中实现Activity的透明背景效果
  14. Oracle表连接方式总结
  15. LaTex问题解决集[2]:解决插入Visio图片有多余边框和白边的问题
  16. Linux 操作系统镜像下载
  17. STM32个人笔记-电源管理
  18. Garbled Circuits介绍 - 56 Yao协议的实现 总结
  19. uniapp如何引入全局js
  20. origin如何绘制双y轴曲线_如何在origin图中,做出双Y轴?

热门文章

  1. 转发:只用最适合的! 全面对比主流 .NET 报表控件:水晶报表、FastReport、ActiveReports 和 Stimulsoft
  2. DAT与MPG文件之间有什么区别(转)
  3. 大众迈腾/途观/CC/途安卡尔福车载导航评测
  4. 小程序自定义菜单Navbar
  5. html课程表斜杠,Word功课表斜线怎么做
  6. Ubuntu18.04安装ROS Melodic+turtlebot3仿真配置
  7. 小觅双目摄像头固件升级失败的解决办法
  8. (附源码)apringboot计算机专业大学生就业指南 毕业设计061355
  9. 关于德国商标的注册概要
  10. Android壁纸机制(Android Q与老版本对比)