写点什么

园区网中 IPv6 地址的终端 mac 地址追溯

用户头像
冯骐
关注
发布于: 2021 年 03 月 04 日

前言


国家开始大力推 IPv6 了,作为大教育网的高校而言,我们自然早已全网双栈, v6 启用多年了。提供互联网接入服务当然要保存用户的上网认证日志以供查证之需,自然也包括终端的 mac 地址追溯。


对于 IPv4 而言,这个事情很简单,dhcp 日志就好。然而在 IPv6 中情况就发生了一些变化。且不说无状态地址分配的问题,即便做了 dhcpv6,就能和 v4 一样解决问题吗?不是这样的。


dhcpv6


dhcpv4 使用 mac 地址来标识用户 —— 事实上是标识用户系统的接口(网卡)。而在 dhcpv6 中,则采取 DUID + IAID 的模式,DUID 标识系统,IAID 标识接口。


根据 RFC3315 的规范,DUID 有三种模式


DUID Based on Link-layer Address Plus Time [DUID-LLT]

由 2 字节的类型码 —— 这里是 1,2 字节的硬件类型码,4 字节的时间码,后面加链路层地址组成。


     0                   1                   2                   3     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+    |               1               |    hardware type (16 bits)    |    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+    |                        time (32 bits)                         |    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+    .                                                               .    .             link-layer address (variable length)              .    .                                                               .    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
复制代码

DUID Assigned by Vendor Based on Enterprise Number [DUID-EN]

由 2 字节的类型码 —— 这里是 2,2 字节的企业注册号,2 字节的企业标识符,后面加企业自己定义的 Identifier。


     0                   1                   2                   3     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+    |               2               |       enterprise-number       |    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+    |   enterprise-number (contd)   |                               |    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               |    .                           identifier                          .    .                       (variable length)                       .    .                                                               .    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
复制代码

DUID Based on Link-layer Address [DUID-LL]

由 2 字节的类型码 —— 这里是 3,2 字节的硬件类型码,后面加链路层地址组成。


     0                   1                   2                   3     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+    |               3               |    hardware type (16 bits)    |    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+    .                                                               .    .             link-layer address (variable length)              .    .                                                               .    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
复制代码

无论是哪一种模式,DUID 都直接与用户系统关联,而与实际的网络接口无关。DUID-EN 自然不必多说,DUID-LL 中,选取的 link-layer address 可以是任意的网络接口,无论哪个网络接口接入网络,DUID 都将保持不变。


The choice of network interface can be completely arbitrary, as long as that interface rovides a unique link-layer address and is permanently attached to the device on which the DUID-LL is being generated. The same DUID-LL SHOULD be used in configuring all network interfaces connected to the device, regardless of which interface's link-layer address was used to generate the DUID.


而 DUID-LL 是推荐用于没有永久性存储的设备的,比如交换机等。对于有永久性存储的设备,比如我们的电脑,手机,更常见的模式是 DUID-LLT。此模式下,DUID 一经生成,即便你换掉了网卡,系统的 DUID 也不会变更。


Clients and servers using this type of DUID MUST store the DUID-LLT in stable storage, and MUST continue to use this DUID-LLT even if the network interface used to generate the DUID-LLT is removed.


想一想为什么 DUID-LLT 要加上时间?因为这样才能保证把一张网卡拔下来换掉另外一台电脑上,生成的 DUID 不会产生冲突嘛~


在 windows 下我们可以通过 ipconfig /all 来看一下自己的 DUID,比如看下我这块无线网卡的 DUID:


Wireless LAN adapter WLAN:
Connection-specific DNS Suffix . : Description . . . . . . . . . . . : Intel(R) Wireless-N 7265 Physical Address. . . . . . . . . : 34-02-86-70-5A-6C ……中间省略了…… DHCPv6 Client DUID. . . . . . . . : 00-01-00-01-22-1B-61-34-68-F7-28-C1-3E-27
复制代码

可以看到末尾的 68-F7-28-C1-3E-27 和无线网卡的物理地址 34-02-86-70-5A-6C 全然不同。这个 mac 其实是属于有线网卡的。


Ethernet adapter 以太网:
Media State . . . . . . . . . . . : Media disconnected Connection-specific DNS Suffix . : Description . . . . . . . . . . . : Intel(R) Ethernet Connection (3) I218-LM Physical Address. . . . . . . . . : 68-F7-28-C1-3E-27
复制代码

这种 DUID 和网卡解耦,只绑定系统的做法,其实是更利于用户终端的定位的 —— 如果环境是纯 v6 的话。。。

然而现实是大家都是双栈,这样 v6 中的 DUID 就和 v4 中的 mac 地址关联就成为了麻烦的事情,我们总是希望统一化的来处理对吧。


况且,还有一堆客户端不支持 dhcpv6 呢,根据 2017 IPv6 支持度报告 描述,仍只有 65% 的操作系统支持 dhcpv6,其中包括很可能永远不会支持 dhcpv6 的安卓。



所以还是无状态分配吧


ipv6 neighbors


那么方案还是回到无状态分配上,获取 mac 地址的办法自然就是取三层点上的 ipv6 neighbors 表了。技术上要么 snmp,要么 cli 执行名再分析输出。我们选择走 cli—— 因为 snmp 太慢了。。。


我们知道 snmp 是有 index 的,并且在响应 snmp 请求时,返回的数据会经过排序,根据 index 的序号来按序返回。这就导致数据量很大的时候,snmp 的响应速度其实是相当慢的。并且,当交换机上的 snmp agent 持续时间很长在响应 snmp 请求时,他会显著的占用交换机的 cpu 资源,可能会导致交换机的 cpu 使用率破表。


而通过 cli 执行命令来返回 ipv6 neighbors 表时,数据是无序的,速度就自然会快的多。这个差异在 ipv6 neighbors 量级增加时会非常显著。举个极端点的例子:


实验设备是一台思科的 C6880-X-LE,上面大概有 4 万多条 arp 表项和 8 万多条 ipv6 邻居表项。


#show arp | count Vl           Number of lines which match regexp = 41207
#show ipv6 neighbors | count VlNumber of lines which match regexp = 85116
复制代码

我们先写个脚本来模拟下 ssh 执行命令,作为对比的是 snmp 采集 ipNetToPhysicalTable,这个指标由 RFC4293 定义, oid 为 1.3.6.1.2.1.4.35.1.4。由于 ipNetToPhysicalTable 同时包含了 ipv4 地址和 ipv6 地址对应的 mac 地址表,公平起见,ssh 执行的命令中同时包含 show arp 和 show ipv6 neighbors


一个很简单的 expect 脚本:


#!/usr/bin/expect
spawn ssh admin@192.168.9.1expect "Password:"send "123456\r"expect "*#"send "terminal length 0\r"send "show ipv6 neighbors\r"send "show arp\r"send "quit\r"interact
复制代码

然后我们使用 time 命令来对比下使用 ssh 执行命令和 snmp 请求的时间差异


# time ./ssh_test.sh>/dev/null 2>&1
real 0m13.783suser 0m0.061ssys 0m0.134s
# time snmpwalk -v 2c -c community 192.168.9.1 1.3.6.1.2.1.4.35.1.4>/dev/null 2>&1
real 44m28.033suser 0m10.151ssys 0m5.002s
复制代码

13.7 秒 和 44 分半,差距将近 190 倍!而这样体量的三层节点并不罕见,尤其是无线网络的网关,规模很可能还要更大。


采集和记录


所以方案就很清晰了


  • 首先并发的通过 ssh 采集全网所有三层节点上的 ipv6 邻居表

  • 然后分析执行结果,根据不同类型的交换机来做匹配处理,提取 ipv6 邻居表项上的各字段

  • 对提取的字段进行封装,然后推送进我们的日志服务器,比如 ELK 或者 Splunk


并发 ssh 的执行可以使用 multissh 来做,我在 用 Go 写一个轻量级的 ssh 批量操作工具 中介绍过这个工具。我们先使用 multissh 去各个交换机上执行命令,把运行的结果先存下来,然后再通过 python 脚本去分析提取数据。考虑到交换机的类型和口令各异,我们编写一个 v6_neighbors.json 文件来保存不同交换机的口令和命令模板。


{           "SshHosts": [        {               "Host": "192.168.10.1",            "Port": 22,             "Username": "admin",            "Password": “password",            "cmdFile":"/opt/swbackup/Ciscov6Ndcmd.txt"        },        {               "Host": "192.168.31.1",            "Port": 22,             "Username": "admin",            "Password": "password",            "CmdFile": "/opt/swbackup/CiscoEnv6NdCmd.txt"        },        {            "Host": "192.168.92.1",            "Port": 22,            "Username": "admin",            "Password": "password",            "CmdFile": "/opt/swbackup/Huaweiv6NdCmd.txt"        }       …………
复制代码

例如上面所用调用 cmdFile,对于不存在 en 密码的思科交换机,他的 cmdFile 是:


terminal length 0show ipv6 neighbors
复制代码

而对于存在 en 密码的思科`,则为:


enableenablepasswordterminal length 0show ipv6 neighbors
复制代码

对于华为,则是:


display ipv6 neighborsquit
复制代码

等等,我们都提前定义好,然后通过 multissh 调用即可。


/opt/swbackup/multissh -c /opt/swbackup/v6_neighbors.json -outTxt -f /opt/swbackup/v6nd_out/
2018/10/17 11:45:01 Multissh start2018/10/17 11:45:12 Multissh finished. Process time 10.98903995s. Number of active ip is 34
复制代码

对于 34 台交换机的并行执行,总计的耗时大约是 11 秒。运行的结果会以交换机的 host 作为文件名保存在 v6nd_out/ 目录内。


 v6nd_out]# ls192.168.10.1.txt 192.168.31.1.txt 192.168.92.1.txt .....
复制代码

然后我们根据不同的交换机类型来分析这些文本就好了。执行的输出大体可以分为三个部分:ipv6 neighbors 表以上,ipv6 neighbors 表正文,ipv6 neighbors 表以下,显然除了 ipv6 neighbors 表正文,其他部分我们在处理的时候都可以直接抛弃掉了。 举几个例子:

Cisco Catalyst 的交换机


cisco_switch#terminal length 0cisco_switch#show ipv6 neighbors   IPv6 Address                              Age Link-layer Addr State Interface# ipv6 neighbors 表以上部分,抛弃# ipv6 neighbors 表正文2001:DA8:8005:1328:FCF5:53FA:666E:6CE5      2 3417.eb9b.ed9c  STALE Vl28FE80::5908:8EEF:73F3:F295                   0 2089.843b.a63f  STALE Vl28FE80::F0:84CE:8CFF:2543                     0 000e.c6c9.59e2  REACH Vl28FE80::B44D:C7F9:30B8:48F                    0 0050.56a1.4dd3  DELAY Vl44…………FE80::250:56FF:FEBC:556D                    3 0050.56bc.556d  STALE Vl44FE80::89B2:4851:559E:A102                   1 94c6.9174.74a1  STALE Vl28# ipv6 neighbors 表正文# ipv6 neighbors 表以下部分,抛弃cisco_switch#exit
复制代码

华为的交换机


Info: The max number of VTY users is 10, and the number      of current VTY users on line is 1.      The current login time is 2018-10-17 09:58:13+00:00.<henkou-core>display ipv6 neighbors-----------------------------------------------------------------------------# ipv6 neighbors 表以上部分,抛弃# ipv6 neighbors 表正文IPv6 Address : 2001:DA8:8005:7100:1EB:112D:BE4F:8CE3Link-layer   : 8cec-4b91-f3e8                     State : STALEInterface    : GE4/0/26                           Age   : 00h18m20sVLAN         : 903                                CEVLAN: -VPN name     :                                    Is Router: FALSE…………IPv6 Address : FE80::4D02:139A:3ADA:D038Link-layer   : 0023-5461-231f                     State : STALEInterface    : GE4/0/44                           Age   : 00h08m55sVLAN         : 903                                CEVLAN: -VPN name     :                                    Is Router: FALSE# ipv6 neighbors 表正文# ipv6 neighbors 表以下部分,抛弃-----------------------------------------------------------------------------Total: 15      Dynamic: 15      Static: 0
<henkou-core>quit Info: The max number of VTY users is 10, and the number of current VTY users on line is 0.
复制代码

Cisco Nexus 的交换机


terminal length 0show ipv6 neighborCisco Nexus Operating System (NX-OS) SoftwareTAC support: http://www.cisco.com/tacCopyright (C) 2002-2016, Cisco and/or its affiliates.All rights reserved.The copyrights to certain works contained in this software areowned by other third parties and used and distributed under their ownlicenses, such as open source.  This software is provided "as is," and unlessotherwise stated, there is no warranty, express or implied, including but notlimited to warranties of merchantability and fitness for a particular purpose.Certain components of this software are licensed underthe GNU General Public License (GPL) version 2.0 orGNU General Public License (GPL) version 3.0  or the GNULesser General Public License (LGPL) Version 2.1 orLesser General Public License (LGPL) Version 2.0.A copy of each such license is available athttp://www.opensource.org/licenses/gpl-2.0.php andhttp://opensource.org/licenses/gpl-3.0.html andhttp://www.opensource.org/licenses/lgpl-2.1.php andhttp://www.gnu.org/licenses/old-licenses/library.txt.exitganxunlou# terminal length 0ganxunlou# show ipv6 neighbor 
Flags: # - Adjacencies Throttled for Glean G - Adjacencies of vPC peer with G/W bit R - Adjacencies learnt remotely
IPv6 Adjacency Table for VRF defaultTotal number of entries: 290Address Age MAC Address Pref Source Interface# ipv6 neighbors 表以上部分,抛弃# ipv6 neighbors 表正文2001:da8:8005:1542:1118:8f01:d54d:a9d2 2d02h fc4d.d43e.aca5 50 icmpv6 Vlan5002001:da8:8005:1542:15e6:6514:2169:47fc 6d02h fc4d.d43e.aca5 50 icmpv6 Vlan500…………fe80::f2c8:50ff:fe7f:1563 00:09:46 f0c8.507f.1563 50 icmpv6 Vlan23fe80::f970:4f2a:7c35:1e90 00:04:14 94c6.9120.5b83 50 icmpv6 Vlan23# ipv6 neighbors 表正文# ipv6 neighbors 表以上部分,抛弃ganxunlou# exit
复制代码

然后根据 ipv6 neighbors 表的差异,我们去做适配来提取字段就可以了。首先定义下结构。由于各品牌的 ipv6 neighbors 表所提供的字段并不全部相同,我们尽量定义他们的全集。没有的字段留空就好了,比如思科的交换机 interface 提供的是 vlan 号,而华为则区分为了具体的物理接口和 vlan 两部分,所以这里我们两个字段都定义,思科在 vlan 中留空即可。


class ipv6_neighbors:        def  __init__(self):                self.ipv6_address = ""                self.age = ""                self.mac_address = ""                self.state = ""                self.interface = ""                self.vlan = ""                self.switch_type = ""
复制代码

我们针对不同的交换机来行,来编写对应的处理函数,输入文本行的数组,输出分析好的 ipv6_neighbors 数组,举些例子:

对于华为的交换机,可以这么处理:


def huawei_swos_v6_nds(lines):        v6_nds = []        i = 0        for l in lines:                if "-------------------------" in l:                        break                i = i + 1        num = 0        for l in lines[(i+1):]:                if "-------------------------" in l:                        break                if num == 0:                        v6_nd = ipv6_neighbors()                        v6_nd.ipv6_address = l.split(":", 1)[1].strip()                elif num == 1:                        splitstr = l.split(":")                        v6_nd.mac_address = splitstr[1].replace("State", "").strip()                        v6_nd.state = splitstr[2].strip()                elif num == 2:                        splitstr = l.split(":")                        v6_nd.interface = splitstr[1].replace("Age", "").strip()                        v6_nd.age = splitstr[2].strip()                elif num == 3:                        splitstr = l.split(":")                        v6_nd.vlan = splitstr[1].replace("CEVLAN", "").strip()                elif num == 4:                        num = num + 1                        continue                else:                        v6_nd.switch_type = "huawei_swos"                        v6_nds.append(v6_nd)                        num = 0                        continue                num = num + 1        return v6_nds
复制代码

做法就是逐行扫描,直至碰到 ----------- 之前都抛弃。

然后开始处理 ipv6 neighbors 表。每个 ipv6 neighbor 数据可以分为 5 行:

第一行是 IPv6 Address

第二行是 Link-layer,也就是 Mac Address 和 State

第三行是 Interface 和 Age

第四行是 VLAN 和 CEVLAN

第五行是 VPN name 和 Is Router

分别提取字段追加到 v6_nds 数组中。 然后经过一个空行进入到下一个 ipv6 neighbor 数据。

直至再次碰到 ----------- ,退出,返回数据。


类似的,我们可以处理其他的交换机数据,然后把他们封装成 json


{"switch_type": "huawei_swos", "switch": "192.168.92.1", "ipv6_address": "2001:DA8:8005:7100:4D02:139A:3ADA:D038", "mac_address": "0023-5461-231f", "interface": "GE4/0/44", "age": "00h04m18s", "vlan": "903", "state": "STALE"}
复制代码

然后我们再把这些 json 数据推到我们的日志服务器就好啦,我们可以直接通过 API 推送进 ELK 或者 Splunk。或者更简单的直接把他们作为日志打下来,然后通过 packetbeat/splunkforwarder 去采集就行了。


数据分析


既然数据进了日志服务器,而且已经以 json 方式封装好了字段,那么再做数据分析就是水到渠成的事情了。随便举几个例子:

根据 mac_address 来计数,可以统计全网的上线终端数量。


结合 switch,可以统计不同三层点下的用户规模。


或者看一下 mac_address 相同的 v6 地址情况


根据实际的情况,可以任意组合来做,总之只要进了日志服务器后面一切就都好办。


参考文献


RFC3315

2017 IPv6 支持度报告

RFC4293


以上

原文于 2018 年 10 月首发于简书,搬家存档。

行文有微调。


发布于: 2021 年 03 月 04 日阅读数: 14
用户头像

冯骐

关注

教育行业码农 2020.06.19 加入

一个教育行业的码农

评论

发布
暂无评论
园区网中 IPv6 地址的终端 mac 地址追溯