解决某些主机商的VPS分配了IPv6段但是额外添加的IPv6地址不通的问题

事件背景

最近折腾IPv6的小鸡,一些主机商给小鸡分配了一个IPv6段,一般是/64,意味着有数不尽的公网IPv6地址
我心想着不折腾利用一下浪费了。于是看了一下手中的小鸡,除了刚卖出去的Dmit,剩下的去掉搬瓦工这样的隧道地址,不少廉价小鸡也有一个段的IPv6。一个网站绑定一个公网IPv6?一个容器绑定一个公网IPv6?远程组网给客户端分配IPv6?或者搞多IP节点?
先不管怎么玩,首先得保证添加的IPv6能通。但就这个前提就一波三折。

相对开放的IPv6环境

RareCloud这家的VPS,给的/64段,它家的IPv6网关收到外网的包,会主动发起NS请求,
只要是这个段上的IPv6地址,随时添加随时能用,刚添加外网就能ping通,一切顺利。

如果你的VPS属于RareCloud这种情况,就不必往下看了,下面是处理添加了IPv6,只有主地址通,其它地址不通的情况。

有限制的IPv6环境

但是myprepaidserver和veloxmedia就不行了,新添加的IPv6,外面ping不通。
起初我以为我搞错了,服务商其实只是“刚好”给我分配了::1这个地址,并不是给了我整个段。
直到我不甘心,把myprepaidserver的IPv6的地址
xxxx:xxxx:xxxx:xxxx::1修改为了xxxx:xxxx:xxxx:xxxx::2,发现xxxx:xxxx:xxxx:xxxx::2能通了。
这说明,这个小鸡确实能用这个段上的其它地址,为什么绑定多个就不行呢?
不甘心,我又添加了::1,::3,::4,::5,::6,::7,::8,::9 ,然后没管它,过段时间发现::9也通了原来的::1::2也是通的,
但是前面的3456却不通。这就勾起我好奇心了,同样添加的IPv6地址,是什么决定哪些能通哪些不能通的呢?

问题分析

我的直觉告诉我,这个问题涉及到IPv6的ND协议,它的作用和IPv4的ARP协议差不多,就是根据IP地址查物理地址,以及判断使用这个IP地址的主机是否存在。
直接上tcpdump,然后抓取IPv6的ICMP包,因为IPv6的ND协议是通过ICMP来的,
我新添加了一个IP:xxxx:xxxx:xxxx:xxxx::6666
结果发现当外部网络ping它的时候,没有收到任何NS(Neighbor Solicitation)包,反倒收到其它乱七八糟的邻居发来的NS包。这就有点奇怪了,
按理说,如果外部网络的ping包到达了VPS的网关,网关要把这个包发到我的VPS上,得先通过NS判断这个IPv6地址是存在以及查询对应的物理地址,但是我却没收到这个NS请求,那网关根本就不知道我的存在啊,那自然就不通了。
但为什么有些地址是通的呢?
然后我就尝试使用xxxx:xxxx:xxxx:xxxx::6666这个地址往外ping,

ping -I xxxx:xxxx:xxxx:xxxx::6666 www.google.com

结果依然卡住没有任何回复(-I 参数是使用指定的接口或者IP作为源地址,以免它使用默认IP或者主IP)
但是!当我准备ctrl+C中断的时候,突然通了!与此同时,外网也能ping通xxxx:xxxx:xxxx:xxxx::6666了!
然后再去看tcpdump的抓包,发现前面的ping都是发出去了,没有任何回复,
直到几十秒后,VPS发出了NS包,看了一下,是查询网关地址xxxx:xxxx:xxxx::1,然后网关回复了NA(Neighbor Advertisement),接着就收到ping的回复了,所以这个VPS发起的NS是很关键的事情。现在一切都解释得通了,为什么有时候绑定的IPv6能通有时候不能,有时候一开始不通,过一段时间又通了。

复盘过程

一开始,VPS默认绑定了一个IP也就是xxxx:xxxx:xxxx:xxxx::1
因为只有这1个地址,所以这个VPS对外所有的IPv6通信都通过这个地址,
那就意味着它在把对外的数据包发给网关的时候,会发起NS先查询网关的物理地址,而这个NS请求的源地址一定是xxxx:xxxx:xxxx:xxxx::1
虽然这个时候网关是被动回应NA的一方,但它也同时就知道了xxxx:xxxx:xxxx:xxxx::1的存在,在下次有数据包目的地址为xxxx:xxxx:xxxx:xxxx::1时,就知道转发给谁了。

后来,我给VPS添加了多个地址:

xxxx:xxxx:xxxx:xxxx::3
xxxx:xxxx:xxxx:xxxx::4
xxxx:xxxx:xxxx:xxxx::5
xxxx:xxxx:xxxx:xxxx::6
xxxx:xxxx:xxxx:xxxx::7
xxxx:xxxx:xxxx:xxxx::8
xxxx:xxxx:xxxx:xxxx::9

但是网关并不知道这些地址存在,按照正常情况,网关接收到数据包,但是不确定里面的目标地址是否存在自己的网络里时,应该主动发起NS,试探性询问网络里有没有主机拥有这个地址,但是某些服务商为了安全因素或者其它什么原因,禁用了主动发起NS。所以当这种类型的商家的IPv6网关接收到外部的包,发现里面的地址没见过的时候,就直接丢弃了。这就是新添加的IPv6,外面ping不通的原因。
那为什么偶尔,新添加的IPv6又能通呢,是因为系统在对外请求的时候,并不一定固定使用某一个IP地址作为源地址,可能会轮询。
比如上面添加了::1::9这9个地址,可能对外访问的时候,会选择其中一个,某些IP运气好刚好选中了,就有了在网关那“露面”的机会。

通俗解释

上面看不懂无所谓,下面通俗解释一下:

一个小区(网络)里面,住着很多户,
每一户(每一个VPS)里面可能住着多个人(绑定了多个IPv6)。

有个名叫RareCloud的小区,门卫(网关)比较外向。
有一天,外面寄来了一个包裹,收件人是张三(目标IPv6),
门卫一看:“张三?我好像认识又好像不认识,先喊一嗓子试试!”
于是他拿着大喇叭往小区里一喊:“张三张三!谁是张三!有没有人是张三!”(NS请求)
然后1栋1单元5楼2号(MAC地址)的张三探出脑袋回应:“我在这!”(NA回复)
然后保安就知道张三住在1单元5楼2号,于是就把包裹送到他家了,同时它把张三和他的单元楼层记下来(邻居表),下次来了张三的包裹就直到送哪里去了。
后来张三家里多了张四张五(添加多个IPv6),但是好在门卫每次收到没见过的人的包裹,都会像之前一样“喊一声”,
所以张四张五的包裹也能顺利收到。

========================================

但是另一个名叫Veloxmedia的小区,门卫王麻子很“内向”,
有一天,外面寄来了一个包裹,收件人是李四
这个门卫不知道李四是谁,也不知道他到底在不在小区里面,又不好意思喊(不主动发起NS请求)
所以就干脆把包裹扔了。
直到过了一会,李四要出门了(对外请求),但是出门之前要先去门卫那
李四只知道要出门必须要先去王麻子那(网关),但是不知道他在哪,
于是李四喊了一声:“我是李四王麻子你在哪里!?”(NS请求)
王麻子回应:“哦哦哦,我在大门口坐着的”(NA回应)
并且王麻子看了一眼声音的来源,发现李四住在2单元2楼2号(MAC地址)。于是偷偷记下来(邻居表)
于是李四才知道门卫和大门在哪,高兴出门去了。
后来,王麻子从外面收到了收件人是李四的包裹,查了一下以前的记录,知道他在2单元2楼2号,
于是就顺利把包裹送到了李四家里。

后来李四家里凭空多出来了李五李六李七李八(绑定了多个IPv6)。
有一天门卫王麻子收到了收件人是李五的包裹,他只知道李四在2单元2楼2号,
但是李五是谁又不认识了,于是又把包裹丢了。
偏偏李五李六是死宅,从来没对外露面(作为源地址对外发送数据)
所以很长一段时间,门卫都不知道李五李六等人的存在。
直到有一天,李四家李七出门了
(可能是系统自动选择的,在多IP场景下,系统出站使用的IP是哪一个我不太了解具体规则,但是可以手动指定,例如使用ping-I参数,curl--interface参数等)
门卫终于认识了李七,才知道原来李七李四是一家的,下次收到李七的包裹就知道送哪里了。

问题解决

知道问题原因了,那解决思路很简单,
就是让VPS主动以绑定的每个IPv6地址作为源地址,分别发送一次NS请求,让网关“记住”它们的存在,
对于上面的Veloxmedia小区的故事,就是让李四的全家人,都去门卫那露个面。

我在网上找了很久,都没有找到合适的工具主动发送NS,找到一个nssend,但是只有旧版的debian才有对应的软件包。
所以就干脆自己手戳吧。

项目以及使用方法

项目地址:https://github.com/kkqy-go/nser
使用golang写的,写起来比C语言快,让AI补充了一下注释和README,本来是一个小功能,怎么快怎么来。
可以自己编译,或者下载release里编译好的程序,编译好的程序名字会带有系统和架构,下面的使用说明默认以nser作为程序名。

以Veloxmedia为例,它的IPv6就属于必须要主动向网关发送NS以后网关才转发这个地址的包的类型
我使用的debian 13。
假设Veloxmedia给我分配的IPv6段是aaaa:bbbb:cccc:dddd::1/64
顺便普及一下IPv6的一个基础知识,以免有的人搞不明白为什么有::,::是简写,如果地址里有一段全是0,就可以缩成::
aaaa:bbbb:cccc:dddd::1aaaa:bbbb:cccc:dddd:0000:0000:0000:0001是等价的

注意:不同系统环境有差异,你的网卡可能不是eth0,要自己用ifconfig查一下,要把下面的aaaa:bbbb:cccc:dddd换成你的实际前缀

临时添加IP:

ip addr add aaaa:bbbb:cccc:dddd::2/64 dev eth0
ip addr add aaaa:bbbb:cccc:dddd::3/64 dev eth0
ip addr add aaaa:bbbb:cccc:dddd::4/64 dev eth0
ip addr add aaaa:bbbb:cccc:dddd::5/64 dev eth0
ip addr add aaaa:bbbb:cccc:dddd::6/64 dev eth0
ip addr add aaaa:bbbb:cccc:dddd::7/64 dev eth0
ip addr add aaaa:bbbb:cccc:dddd::8/64 dev eth0
ip addr add aaaa:bbbb:cccc:dddd::9/64 dev eth0

永久添加IPv6得修改/etc/network/interfaces

auto lo
iface lo inet loopback

# The primary network interface
allow-hotplug eth0
iface eth0 inet static
        ...
iface eth0 inet6 static
        accept_ra 0
        address aaaa:bbbb:cccc:dddd::1/64
        gateway aaaa:bbbb:cccc::1
        # 添加到这里
        up ip addr add aaaa:bbbb:cccc:dddd::2/64 dev eth0 
        up ip addr add aaaa:bbbb:cccc:dddd::3/64 dev eth0
        up ip addr add aaaa:bbbb:cccc:dddd::4/64 dev eth0
        up ip addr add aaaa:bbbb:cccc:dddd::5/64 dev eth0
        up ip addr add aaaa:bbbb:cccc:dddd::6/64 dev eth0
        up ip addr add aaaa:bbbb:cccc:dddd::7/64 dev eth0
        up ip addr add aaaa:bbbb:cccc:dddd::8/64 dev eth0
        up ip addr add aaaa:bbbb:cccc:dddd::9/64 dev eth0

你想添加更多也可以,就/64而言可是从aaaa:bbbb:cccc:dddd:0000:0000:0000:0000aaaa:bbbb:cccc:dddd:ffff:ffff:ffff:ffff足足2^64次方个地址可以用!
我这里举例,只添加了8个IPv6地址,
这个时候从外网ping,大概率很多是不通的(可能个别是通的,原因上面已经讲过了)。
这个时候使用以下参数运行一下nser:

./nser -gateway -iface eth0

做完这一步,上面添加的IPv6全都能通了。

这个是nser最简单的用法,就是让nser遍历eth0上的所有IPv6地址,然后以每个IPv6地址分别发送一次NS请求到网关地址,以让网关加入邻居表。
当然你也可以指定-src-dst手动发送,但对于一般情况,直接用上述参数就行了。

只要网关记住你了,应该就是一直有效的,
实测网关会周期性对邻居表里的条目发起NS请求确认主机是否还存在(这个时候它又会发送NS了,说明它只对有缓存的记录才会主动发起NS),
如果存在,会继续延长时间。所以不必担心会不会突然又不通了。
但是如果后面,你的VPS因为故障或者其它原因离线时间比较长,可能会网关会“忘记”你,所以下次启动还得使用nser给网关“露个面”。

其它说明

也许这个帖子并不能解决你的问题,因为你的IPv6 ping不通也许是其它问题:
1.比如防火墙
2.比如网关、路由配置错误
3.比如环境压根没有IPv6

  1. ...
    并不一定对症下药,但也许能帮到某些遇到一样问题的人。

后话

折腾是乐趣,折腾的结果也许并没有太多用处,但是体会的是学习过程,通过解决这个问题,我把IPv6的ND协议搞明白了。
肯定很多朋友看到这里觉得一个一个添加IPv6太麻烦了,能不能把整个段绑定上去,或者有没有办法给docker里的容器分配不同的IPv6,
这次打了这么多字累了,这个留到下一次讲了。
(主要是debian 13软件源里的ndppd有bug,所以需要自己编译和替换,关于ndppd,这两天也积累了不少槽点)

Nginx 反向代理流媒体文件出现的加载缓慢问题

事情起因

有个Jellyfin的服务器,前端使用nginx进行反代,正常情况下浏览器访问流媒体文件,会自动分块请求,不需要加载整个文件即可开始播放,但是在前端套一层nginx反向代理以后,要加载很久才显示画面。

问题排查

首先nginx服务器和后端之间并没有网络连接速度慢的问题,排除掉了网络问题,
就首先怀疑是nginx的配置文件,
尝试关闭缓冲:

proxy_buffering off;  
proxy_request_buffering off;

无济于事,仍然播放缓慢。
而且发现一个很奇怪的问题,当浏览器关闭以后,前端已经断开连接了,
但nginx仍然持续不断从后端服务器上读取文件,而且流量巨大,似乎是试图读取整个媒体文件。

最后逐一排查配置项,
最后发现是nginx默认配置了一个缓存区

proxy_temp_path /www/common/proxy/proxy_temp_dir; 
proxy_cache_path /www/common/proxy/proxy_cache_dir levels=1:2 keys_zone=proxy_cache_panel:20m 
proxy_cache proxy_cache_panel; 

此配置导致nginx接收到请求以后,首先查询是否有缓存,有缓存则直接返回,没有缓存会从后端抓取完整文件,然后再返回给客户端。
问题根源也就在这里了。

问题解决

在反向代理配置里,关闭缓存:

proxy_cache off;

使用自签证书给自己的程序签名

前因

由于Golang编写的程序总是在Windows平台下报毒,网上查阅以后,发现通过签名可以缓解此类问题(并没有根治,总之Windows Defender很迷就是了)。

过程

需要先安装VisualStudio,因为签名相关工具被包含在VisualStudio的开发工具里。

  1. 创建CA:

    makecert -r -pe -n "CN=My CA" -ss CA -sr CurrentUser -a sha256 -cy authority -sky signature -sv MyCA.pvk MyCA.cer

  2. 导入CA(也可以忽略):

    certutil -user -addstore Root MyCA.cer

  3. 创建代码签名证书:

    makecert -pe -n "CN=My SPC" -a sha256 -cy end -sky signature -ic MyCA.cer -iv MyCA.pvk -sv MySPC.pvk MySPC.cer

  4. 转为PFX文件:

    pvk2pfx -pvk MySPC.pvk -spc MySPC.cer -pfx MySPC.pfx

如果需要加密码,则增加 -po fess 参数

  1. 给可执行文件签名:

    signtool sign /v /f MySPC.pfx /t http://timestamp.digicert.com /fd SHA256 MyExecutable.exe

其中http://timestamp.digicert.com可替换为以下值:

记一次整理旧文件

前言

我是一个念旧的人,以前的一些开心的事情让我无法释怀,
有时候回忆是一种享受,
有时候回忆是一种痛苦,
即便是开心的事情,
回忆它,让我享受它过去给我留下的余晖,
回忆它,让我感慨它过去再也不来的遗憾,

多年以前,
那时候互联网很纯粹,
我遇到的都是友善的人,
没有争吵,没有对立。
因为某个共同爱好,我在网上和一些人有过一段共同时光,
一起玩游戏,一起谈理想,
但是突然有一天失去了音讯,
我和TA都未曾见过彼此的样子,也不知道彼此的姓名。
我们好像认识,但又互不认识。
TA就像记忆里的一个符号,永远存在记忆里,
我在现实里找不到这个符号对应的人。
人海茫茫,
也许有一天,有渺茫的概率在现实里擦肩而过,
可我不认识TA,TA不认识我。
现在,
我知道我还记得那个我不认识的TA,
但我不知道TA是否还记得TA不认识的我。

小时候,我记忆力很好,
很多人和事我都记得很清楚,
所以记忆里有很多人,
现实认识的人,网络认识的人,
可后来突然就没了联系。
所以,
有些老文件我舍不得删除,
有些老物品我舍不得丢弃,
因为它们是我记忆中那些人,
或者那些符号存在的证据。

我那些记忆里的人和现实里的人或许已经没了联系,
就如同记忆里的“白月光”,和现实里对应的人已经分化成独立的“人”,
一个活在现实里,一个活在记忆里。
我想起以前的一部电影
《寻梦环游记》
只要还有人记得,就不会真正死去。
这个电影除了表达缅怀逝去亲人,
也同样能表达我刚刚的这个主题。

整理文件

今天,我从一堆老旧的文件夹里找到了不少10年甚至15年以前的文件。
有些文件,我都忘记我从什么地方得到的了。
其中有以前的各种备份。

一个旧网站的备份

在这些备份里,我无意中翻到一个网站的备份,
这个网站名字叫“梦幻岛经典游戏站”,
是一个“怀旧”的网站,里面是一些老旧游戏的收集。
我高中大学的时候有一种仓鼠一样的收集癖好,
担心这个网站以后打不开了,于是我就全站下载并且做了一个备份,
然后做了一个镜像站点。
没想到后来果然打不开了,考虑到可能还有不少网友需要
于是2014年我就在贴吧发了一个帖子:
https://tieba.baidu.com/p/3267057694
当时发了贴我就没管了,也不知道后来还有人访问,
于是后面一次数据迁移,我忘了迁移镜像站点,导致我的镜像站打不开了。
今天我因为无意中翻到了这个备份,
于是又把那个镜像站建起来了,然后又去发了个帖子:
https://tieba.baidu.com/p/9229037837
一看时间,刚好10年过去。
不知道原网站的站长andy去了哪里,自从他把站群解散以后就没了消息。
我相信有很多像我一样怀旧的人。我希望互联网有记忆,还能记住这个怀旧游戏站。

一堆老照片的备份

有些事情可能一直记着,但是平时不会去回忆,
当看到老照片的时候,就会想起一连串的事情,
就像突然把一个铺满灰的箱子打开,
一下子冒出来一堆陌生又熟悉的东西。
我一下想起了“忒修斯之船”,
故事描述了一艘船在长期航行中,
所有的零件都因为损耗而逐渐被替换,直到没有一块原来的木板。
这艘船是否还是原来的那艘船?如果不是,那么它在什么时候变成了另一艘船?
我就想啊,我身上的细胞也在不断迭代,
网上搜了一下,人体的细胞大概6-7年会完全迭代更换一次,
某个角度来说,
照片里的“我”并不是“我”,
“我”只是继承了“他”的记忆?

结语

说来也是幽默,
我当初备份那些文件是为了“怀旧”,
而我现在又在“怀旧”那个时候的“怀旧”,
套娃了属于是。

说来也是幽默,
在10年以前,大概我高中的时候还是大一的时候,
我还持有几个比特币,那个时候比特币还不值钱,
还没有主流的交易所,还只是少数虚拟币玩家之间互相通过任务转账赠与,
只是后来密钥不知道丢哪里去了,
当时也不知道比特币以后会这么值钱,现在1个比特币价值几十万人民币了。
我备份了各种过去的东西,却没有备份“未来的黄金”。
要是那几个比特币的密钥我有备份,我现在也就多出来几百万了吧😀~

apt-get 发生 sub-process - apt-listchanges error 解决办法

起因

将VPS重装为debian 12以后,apt直接无法使用,不论是安装还是升级都提示"Segment Fault"
详情里出现:

sub-process - apt-listchanges error

尝试 :

  1. apt purge -f apt-listchanges
    apt install -f apt-listchanges
  2. dpkg --remove apt-listchanges
    apt install -f apt-listchanges
  3. mv /etc/apt/apt.conf.d/20listchanges /etc/apt/apt.conf.d/20listchanges.bak
    apt update && apt upgrade