闲谈IPv6-没有选项胜有选项的TLV

皮鞋湿了,只要是温州的,虽湿而不胖。


IPv6协议头固定,并且非常简单,去掉了IPv6的选项字段,把变长头部统改成了定长。这有什么益处就不多说了,肯定百分百地是提升了处理效率。然而,这里我要说的是,在获得这些收益的同时,其实并没有付出任何代价!

这也就是说,IPv4的设计在某种意义上是错误的设计!不过呢,也不必因为我这句话而大跌眼镜,进化嘛,缺陷总是有的。


我们乍看IPv6,好像是缺失了options选项,这是不是意味着它和IPv4相比,功能弱化了很多呢?非也!

相反,IPv6强化了很多。看似矛盾的两个IP版本,其实并不矛盾!

IPv6并不是简单地说一句 “不支持选项了” ,它的意思实际上是在说, “IPv6可以无限支持选项!” 它之所以把IP选项从IP头里面剥离开来,其实是想更好地去处理选项,不然,最多40字节的选项实在是太鸡肋了,脱离IP头部而自立门户显然是一个更好的选择。


链式协议头

为了处理这些少到无,多到无穷尽的 选项 ,IPv6采用了链式处理。如果可能,则将每一个选项逻辑都单列为一个 上层协议 ,以next header来标识,这便为IPv6的扩展提供了无限的可能。比如下面的链式处理:
在这里插入图片描述

我们看一下IPv4的情况:
在这里插入图片描述

TLV结构

也许是我接触TLV之前没有接触过其它什么其它的表示型语法,但我就是喜欢TLV,相反,我讨厌json,XML,HTML这些。

我并不怎么懂编程,也不懂什么编程语言,加上我感兴趣的最初的网络协议也都不是用于表示内容的,而是仅仅规定格式的,所以,我第一次接触TLV是很晚了,大概是在2010年左右,我当时在做X.509证书的相关工作,涉及到ASN.1编码。

第一次接触TLV,我感觉这太棒了!它像是一个俄罗斯套娃,竟然可以上下无限扩展,使我可以开心颜!简直太棒了!

说了这么多,其实我想说,IPv6的诸多options扩展头,都是采用了TLV的编码格式。


我们看看 HAO扩展,大概就是Home Address Option的缩写,它其实就是一个IPv6地址,代表在移动IP中的 家乡IPv6地址 的概念。它就是一个TLV结构表示的结构体:

/*
 *  home address option in destination options header
 */

struct ipv6_destopt_hao {
    __u8            type;
    __u8            length;
    struct in6_addr     addr;
} __attribute__((packed));

它被封装在了一个叫做ipv6_destopt_hdr的扩展头里:

struct ipv6_opt_hdr {
    __u8        nexthdr;
    __u8        hdrlen;
    /*
     * TLV encoded option data follows.
     */
} __attribute__((packed));  /* required for some archs */

#define ipv6_destopt_hdr ipv6_opt_hdr

在IPv6里,ipv6_destopt_hdr这个扩展选项头的协议号(***记住,在IP看来,它是一个“上层协议”***)是60:

#define IPPROTO_DSTOPTS     60  /* IPv6 destination options */

因此,假设一个节点的Home地址是240e:111::123,其转交地址care-of为2007:111::123,那么它发往2009:111::123的TCP封包就应该是:
在这里插入图片描述

hop-by-hop 与TLV

事实上,你可以在任何支持TLV的扩展头里去拼装任何的TLV,关于该TLV的解释,你只需要自行实现一个回调函数即可。

这里要提到的就是 逐跳头

IPv6的逐跳头,意思是说, 每一个经由的路由器都要去解析这个头部所包含的每一个选项。 这个是协议层面的实现决定的,如果你的实现中没有显式解析这个头部的选项,那就意味着你没有完备实现IPv6协议,看起来很死板,不是吗?但是逐跳头对其包含的选项的解释确实灵活多变的。

逐跳头包含一系列的TLV选项,一个或者多个。每一个TLV选项都有一个特定的处理方式, 包括但不限于:

  • 直接处理,更新协议栈数据结构。
  • 将数据交由应用层去处理

无论如何,协议栈必须对逐跳头有所反应,也就是说,如果这个逐跳头是错误的,有问题的,那么就必须要返回错误。

你想特殊处理逐跳头吗?

你想添加新的逐跳选项么?简单注册一个TLV处理函数即可!


我们来看一下Linux内核是如何处理逐跳头的,首先在ipv6_rcv中,显式处理逐跳头:

    if (hdr->nexthdr == NEXTHDR_HOP) {
        if (ipv6_parse_hopopts(skb) < 0) {
            IP6_INC_STATS_BH(net, idev, IPSTATS_MIB_INHDRERRORS);
            rcu_read_unlock();
            return NET_RX_DROP;
        }
    }

这就是显式调用。

其中ipv6_parse_hopopts完成了一切。在该函数中,内部调用ip6_parse_tlv函数:

在一个循环解析中,调用可能的func回调函数!

反正就是说,只要数据包到达,并且其中有逐跳头,那你就必须去处理它,至于说怎么处理,标准就不管了。虽然标准不管,我们还是可以理解一个应用,那就是资源预留,即RSVP运行的场景。


年华如沙在指隙间悄然流逝,点点散落在未明的尘世。看不透的世事,道不明的情愁,缘梦恋尘尽成灰,浮华如斯东逝水。 ​​​

浙江温州皮鞋湿,下雨进水不会胖。

原文链接: https://blog.csdn.net/dog250/article/details/88430403

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍;

也有高质量的技术群,里面有嵌入式、搜广推等BAT大佬

    闲谈IPv6-没有选项胜有选项的TLV

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/406637

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年4月26日 上午9:58
下一篇 2023年4月26日 上午9:59

相关推荐