2023年7月23日发(作者:)
LinuxMPLS功能详解使⽤mpls功能,⾸先需要加载mpls相关的模块:$ sudo modprobe mpls_gso$ sudo modprobe mpls_iptunnel$ sudo modprobe mpls_router
使能mpls的接收和设置labels表项的数量,默认情况下内核不接收mpls报⽂,如果不使能此项,在如下使⽤ip命令配置本机环回lo接⼝接收mpls数据包时就会失败。labels转发表项的数量初始为0,将会导致不能配置label转发表项。sysctl -w =1sysctl -w rm_labels=1048575配置1:到⽹络10.1.1.0/30的数据包增加200和300两个label$ sudo ip route add 10.1.1.0/30 encap mpls 200/300 via 192.168.1.1 dev ens33
$
$ ip route
10.1.1.0/30 encap mpls 200/300 via 192.168.1.1 dev ens33
$
使⽤ping测试,tcpdump抓包内容可见,增加了200和300两个label,标签300设置了标签栈底标志[S]。$ ping 10.1.1.1PING 10.1.1.1 (10.1.1.1) 56(84) bytes of data.$$ sudo tcpdump mpls -vvetcpdump: listening on ens33, link-type EN10MB (Ethernet), capture size 262144 bytes23:36:30.813147 00:0c:29:74:7f:04 (oui Unknown) > 00:90:27:fe:c9:34 (oui Unknown), ethertype MPLS unicast (0x8847),length 106:
MPLS (label 200, exp 0, ttl 64) (label 300, exp 0, [S], ttl 64) (tos 0x0, ttl 64, id 36751, offset 0, flags [DF], proto ICMP (1), length 84) localhost > localhost: ICMP echo request, id 6907, seq 1, length 64配置2:标签为500的mpls数据包送到本机的环回接⼝lo:$ sudo ip -f mpls route add 500 dev lo$$ip -f mpls route show500 dev lo
$配置3:标签为100的数据包替换为标签600,发往192.168.1.2。$ sudo ip -f mpls route add 100 as to 600 via inet 192.168.1.2$$ip -f mpls route show100 as to 600 via inet 192.168.1.2 dev ens33
500 dev lo
$配置4:指定多个下⼀跳地址,为每个下⼀跳指定不同的(90/91)标签:$ sudo ip -f mpls route add 80 nexthop as to 90 via inet 192.168.1.2 nexthop as to 91 via inet 192.168.1.3
$
$ ip -d -f mpls route showunicast 80 proto boot scope global
nexthop as to 90 via inet 192.168.1.2 dev ens33 nexthop as to 91 via inet 192.168.1.3 dev ens33$
MPLS的路由结构内核函数mpls_route_add处理MPLS路由的添加⼯作。所有的MPLS路由全部组织在⽹络命名空间的成员platform_label指针数组(指针的指针)中,通过标签值可找到对应的MPLS路由。全部路由不超过platform_labels指定的最⼤数量。struct netns_mpls { size_t platform_labels; struct mpls_route __rcu * __rcu *platform_label;}⼀个mpls_route路由表项所占空间与其所有的下⼀跳mpls_nh的空间,以及每个下⼀跳的所有label空间之总和不超过4K字节⼤⼩。mpls_route路由表项结构图如下:另外,内核注册了netdev通知链函数mpls_dev_notify处理mpls路由的失效和活动状态。static struct notifier_block mpls_dev_notifier = { .notifier_call = mpls_dev_notify,};当接收到设备关闭NETDEV_DOWN事件时,调⽤mpls_ifdown遍历所有的mpls路由表项,将出⼝设备为此设备的表项标记为不可⽤(RTNH_F_DEAD)。如果接收到设备NETDEV_UP的事件,同样的遍历所有的表项,经出⼝设备为此设备的表项标记为可⽤,即去掉RTNH_F_DEAD标志。static int mpls_dev_notify(struct notifier_block *this, unsigned long event, void *ptr){ struct net_device *dev = netdev_notifier_info_to_dev(ptr); switch (event) { case NETDEV_DOWN: mpls_ifdown(dev, event); break; case NETDEV_UP: mpls_ifup(dev, RTNH_F_DEAD | RTNH_F_LINKDOWN); break; }}MPLS数据帧处理Linux内核协议栈注册了⼀个ETH_P_MPLS_UC(0x8847)类型的数据包处理函数(mpls_packet_type)处理MPLS报⽂。
static struct packet_type mpls_packet_type __read_mostly = { .type = cpu_to_be16(ETH_P_MPLS_UC), .func = mpls_forward,};⾸先通过MPLS标签解析mpls_entry_decode函数,得到数据包中MPLS标签的4个字段:标签值、TTL、流量类别和标签栈底标志位。标签值字段占⽤20个bit位,其最⼤值为2**20 -1,由于mpls协议存在预留值,合法的标签值从16开始(MPLS_LABEL_FIRST_UNRESERVED)。static inline struct mpls_entry_decoded mpls_entry_decode(struct mpls_shim_hdr *hdr){
struct mpls_entry_decoded result; unsigned entry = be32_to_cpu(hdr->label_stack_entry); = (entry & MPLS_LS_LABEL_MASK) >> MPLS_LS_LABEL_SHIFT; = (entry & MPLS_LS_TTL_MASK) >> MPLS_LS_TTL_SHIFT; = (entry & MPLS_LS_TC_MASK) >> MPLS_LS_TC_SHIFT; = (entry & MPLS_LS_S_MASK) >> MPLS_LS_S_SHIFT; return result;}其次通过得到的MPLS标签值,查询mpls路由表。有之前介绍的mpls路由表结构可知,以标签值做索引即可找到对应的路由项:static struct mpls_route *mpls_route_input_rcu(struct net *net, unsigned index){ struct mpls_route __rcu **platform_label = rcu_dereference(net->rm_label); rt = rcu_dereference(platform_label[index]); return rt;}对于仅有⼀个下⼀跳的路由表项,直接使⽤。对于多个下⼀跳地址,需要在找到的路由项中,选择⼀个下⼀跳地址,函数(mpls_select_multipath)完成此功能。检查如果活动的下⼀跳的个数为0,返回空。否则根据数据包中的以下字段计算hash值:最多4个标签值、IP头的源IP地址、⽬的IP地址和协议号。得到的hash值与当前活动的下⼀跳数量(alive)取余得到下⼀跳地址的索引值。mpls_get_nexthop函数取出mpls路由的下⼀跳。如果活动的下⼀跳数量与下⼀跳总数相等,说明根据下⼀跳索引nh_index可以直接取到下⼀跳结构。否则,需要遍历所有活动的下⼀跳列表,找到此下⼀跳。 alive = READ_ONCE(rt->rt_nhn_alive); if (alive == 0) return NULL; hash = mpls_multipath_hash(rt, skb); nh_index = hash % alive; if (alive == rt->rt_nhn) goto out; for_nexthops(rt) { unsigned int nh_flags = READ_ONCE(nh->nh_flags); if (nh_flags & (RTNH_F_DEAD | RTNH_F_LINKDOWN)) continue; if (n == nh_index) return nh; n++; } endfor_nexthops(rt);out: return mpls_get_nexthop(rt, nh_index);如果配置了标签替换,仅替换数据包中的顶层标签。以下代码将mpls路由表项中下⼀跳对应的标签栈写⼊到数据包头中。 hdr = mpls_hdr(skb); bos = ; for (i = nh->nh_labels - 1; i >= 0; i--) { hdr[i] = mpls_entry_encode(nh->nh_label[i], , 0, bos); bos = false; }环境iproute2版本:4.17.0内核版本: Linux-4.15
发布者:admin,转转请注明出处:http://www.yc00.com/news/1690105766a306264.html
评论列表(0条)