ipfs autonat

hostAddr机制

hostAddr查询与运用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
ipfs id
{
"ID": "bafzm3jqbeczaadu3v52hvjplcll7zsydxibzipg5yaxij3hmdhskysc52sdxg",
"PublicKey": "CAQSIQMXKhfeoAepfrHLesbR+6QXV5EbBDoz/e1+g9DvUw7ABw==",
"Addresses": [
"/ip4/127.0.0.1/tcp/4001/p2p/bafzm3jqbeczaadu3v52hvjplcll7zsydxibzipg5yaxij3hmdhskysc52sdxg",
"/ip4/192.168.100.242/tcp/4001/p2p/bafzm3jqbeczaadu3v52hvjplcll7zsydxibzipg5yaxij3hmdhskysc52sdxg",
"/ip6/::1/tcp/4001/p2p/bafzm3jqbeczaadu3v52hvjplcll7zsydxibzipg5yaxij3hmdhskysc52sdxg",
"/ip6/fe80::7adc:71f6:25a4:40e0/tcp/4001/p2p/bafzm3jqbeczaadu3v52hvjplcll7zsydxibzipg5yaxij3hmdhskysc52sdxg",
"/dns4/xxx.baasze.com/tcp/4001/p2p/bafzm3jqbec7ulhfmm7s7ydt2mf32nbsjy4237mvzj5skzbkxrfxz7axghsyum/p2p-circuit/p2p/bafzm3jqbeczaadu3v52hvjplcll7zsydxibzipg5yaxij3hmdhskysc52sdxg",
"/ip4/xx.xx.xx.xx/tcp/4001/p2p/bafzm3jqbec7ulhfmm7s7ydt2mf32nbsjy4237mvzj5skzbkxrfxz7axghsyum/p2p-circuit/p2p/bafzm3jqbeczaadu3v52hvjplcll7zsydxibzipg5yaxij3hmdhskysc52sdxg"
],
"AgentVersion": "go-ipfs/0.3.4/183988a",
"ProtocolVersion": "ipfs/0.1.0"
}

ipfs id 命令可显示ipfs节点信息,其中Addresses为服务地址即hostAddr, ipfs 会将hostAddr广播出去, 其余节点会利用hostAddr的地址进行连接, 因此通过hostAddr能否连接节点是 节点互联的关键.

hostAddr的初始化

ipfs 启动后默认监听 “/ip4/0.0.0.0/tcp/0” “/ip6/::/tcp/0”, 此时 ipfs 查询本机各网卡地址并进行监听, 复杂网络环境下有如下几种情况:

  • 云主机通常采用经典网络模式分配的ECS固定公网IP, 在本机网卡上可见公网ip, 因此hostAddr的可查询到此公网ip

  • 云主机通常采用弹性公网ip EIP(Elastic IP Address)访问, 在主机上网卡上不可见公网ip,因此hostAddr的 无法查询到此公网ip

hostAddr的更新

对于部署在无外网网卡ip主机的ipfs, 若依靠广播初始化后的hostAddr, 无法被其余节点主动连接, 即 Inbound 无法走通, 因此 libp2p 设计了autonat、autorelay等机制

注: fix host can be dialed by autonat public addr, but lost the public addr to announce 此pr修复了hostAddr更新缺失EIP的情况,已被合并.

autonat机制

autonat基本原理

go-libp2p-autonat 运用remotePeer(对端节点)DialBack(回拨)机制 来获取自身服务的外网IP及端口, 其过程如下:

  1. 全网中开启autonat service的某节点A, 该节点在p2p网络中支持 autonat协议(”/libp2p/autonat/1.0.0”)

  2. 节点B主动连接上支持 autonat service的节点, 此连接为后续协议交互的连接, 包括 identify协议autonat协议

  3. 节点B在与A完成身份认证后判断节点A是否支持autonat协议, 若支持则由节点B的 autonat client 发起公网地址拨号请求(DialBack)

  4. 节点A的autonat service 收到DialBack请求后对节点B进行DialBack (回拨的地址为节点B的hostAddr以及已有连接的节点B地址), 并将结果observations(包含Multiaddr及Reachability)发送给节点B

  5. 节点B根据DialBack的结果判断自身是 ReachabilityPublic 还是 ReachabilityPrivate, 并补充更新 hostAddr

autonat优化

除了上述基本功能外, autonat还做了一些额外的优化

  1. 节点B启动动态定时器主动对Peerstore的节点进行autonat协议探测以便获取自身最新的nat状态

  2. 对于网络状态的翻转(flipping)设定一个confidence, confidence的增加代表对当前状态的自信心([0,3]), 根据是否翻转以及confidence的值决定是否触发网络状态的改变, 修改hostAddr

  3. confidence小于最大值时, 1.的探测定时器为固定时长(15 min), 否则对探测定时器做如下调整

  4. 当为外网可访问状态时,若最新的外网节点主动连接(Inbound Conn)成功且比最新的探测时间晚, 则延长1.的探测定时器时长, 段时间虚无继续探测

5.当为外网不可访问状态时,若最新的外网节点主动连接(Inbound Conn)成功且比最新的探测时间晚, 减小1.的探测定时器时长以变获取最新的外网地址

总结: 目前ipfs中默认开启了autonat service及autonat探测

ipfs cluster

ipfs-cluster 私有网络集群搭建

192.168.100.4 192.168.100.5 192.168.100.241 组成私有网络, 选取 192.168.100.241 为管理节点采用
ipfs-cluster-ctl 管理文件

  • 管理节点部署 ipfs-cluster-service

记录如下 secret 和 cluster的管理节点信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
cd ~
mkdir ipfs-cluster
cd ipfs-cluster
wget https://dist.ipfs.io/ipfs-cluster-service/v0.12.1/ipfs-cluster-service_v0.12.1_linux-amd64.tar.gz
...

tar -xvzf ipfs-cluster-service_v0.12.1_linux-amd64.tar.gz
...

./ipfs-cluster-service/ipfs-cluster-service init
...

./ipfs-cluster-service/ipfs-cluster-service daemon &
[2] 88693
16:41:34.958 INFO service: Initializing. For verbose output run with "-l debug". Please wait... daemon.go:46
➜ ipfs-cluster 16:41:35.449 INFO cluster: IPFS Cluster v0.12.1 listening on:
/ip4/127.0.0.1/tcp/9096/p2p/12D3KooWEdwm9Q7MXvZrYeMujrQ5fCzJwfJQX75gtSdTyYMpRozf
/ip4/192.168.100.241/tcp/9096/p2p/12D3KooWEdwm9Q7MXvZrYeMujrQ5fCzJwfJQX75gtSdTyYMpRozf
/ip4/127.0.0.1/udp/9096/quic/p2p/12D3KooWEdwm9Q7MXvZrYeMujrQ5fCzJwfJQX75gtSdTyYMpRozf
/ip4/192.168.100.241/udp/9096/quic/p2p/12D3KooWEdwm9Q7MXvZrYeMujrQ5fCzJwfJQX75gtSdTyYMpRozf

cat ~/.ipfs-cluster/service.json|grep secret
"secret": "8e3d96c01821b7f238e805fb28d6b873d09f279d1ff9e63aa3528848f79b73c3",
  • 节点部署 ipfs-cluster-service

ipfs-cluster-service daemon --bootstrap 指定管理节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
cd ipfs-cluster
wget https://dist.ipfs.io/ipfs-cluster-service/v0.12.1/ipfs-cluster-service_v0.12.1_linux-amd64.tar.gz
...

tar -xvzf ipfs-cluster-service_v0.12.1_linux-amd64.tar.gz
...

./ipfs-cluster-service/ipfs-cluster-service init
...

sed -i 's/"secret".*/"secret": "8e3d96c01821b7f238e805fb28d6b873d09f279d1ff9e63aa3528848f79b73c3",/' ~/.ipfs-cluster/service.json

./ipfs-cluster-service/ipfs-cluster-service daemon --bootstrap /ip4/192.168.100.241/tcp/9096/p2p/12D3KooWEdwm9Q7MXvZrYeMujrQ5fCzJwfJQX75gtSdTyYMpRozf &
...
09:04:53.291 INFO cluster: ** IPFS Cluster is READY ** cluster.go:655
09:04:53.681 INFO cluster: 12D3KooW9qFBb2K7TPRW7s654LJJM19hseGZBcvde6Hpe3sNVkWu: joined 12D3KooWEdwm9Q7MXvZrYeMujrQ5fCzJwfJQX75gtSdTyYMpRozf's cluster cluster.go:993
  • 管理节点部署 ipfs-cluster-service
1
2
3
4
5
6
7
8
9
10
11
12
13
14
cd ipfs-cluster

wget https://dist.ipfs.io/ipfs-cluster-service/v0.12.1/ipfs-cluster-ctl_v0.12.1_linux-amd64.tar.gz
...

tar -xvzf ipfs-cluster-ctl_v0.12.1_linux-amd64.tar.gz
...

cp ipfs-cluster-ctl/ipfs-cluster-ctl /usr/local/bin
ipfs-cluster-ctl peers ls
17:14:13.571 INFO restapilog: 127.0.0.1 - - [13/May/2020:17:14:13 +0800] "GET /peers HTTP/1.1" 200 6641
restapi.go:117
12D3KooW9qFBb2K7TPRW7s654LJJM19hseGZBcvde6Hpe3sNVkWu | xxxxxx | Sees 2 other peers
...

ipfs-cluster pin 测试

  • 管理节点添加文件
1
2
3
4
5
6
7
8
9
10
11
12
13
echo a > a.txt
ipfs-cluster-ctl a.txt
added Qmbvkmk9LFsGneteXk3G7YLqtLVME566ho6ibaQZZVHaC9 a.txt
17:24:10.699 INFO cluster: pinning Qmbvkmk9LFsGneteXk3G7YLqtLVME566ho6ibaQZZVHaC9 on [12D3KooW9qFBb2K7TPRW7s654LJJM19hseGZBcvde6Hpe3sNVkWu 12D3KooWEdwm9Q7MXvZrYeMujrQ5fCzJwfJQX75gtSdTyYMpRozf 12D3KooWSKPbWzmnhjRrpi4EVz6NTCnYE6rfZLc1gzLQFdd4sHb6]: cluster.go:1400
17:24:10.700 INFO crdt: new pin added: Qmbvkmk9LFsGneteXk3G7YLqtLVME566ho6ibaQZZVHaC9 consensus.go:209
17:24:10.700 INFO crdt: adding new DAG head: QmZUnZo3dva57M29Ky2fTPqbEDaWnJWKnUMYMoYkxYpLFU (height: 1) heads.go:114
17:24:10.701 INFO adder: Qmbvkmk9LFsGneteXk3G7YLqtLVME566ho6ibaQZZVHaC9 successfully added to cluster adder.go:182
17:24:10.701 INFO restapilog: 127.0.0.1 - - [13/May/2020:17:24:10 +0800] "POST /add?chunker=size-262144&cid-version=0&hash=sha2-256&hidden=false&layout=&local=false&name=&nocopy=false&progress=false&raw-leaves=false&recursive=false&replication-max=0&replication-min=0&shard=false&shard-size=104857600&stream-channels=true&user-allocations=&wrap-with-directory=false HTTP/1.1" 200 88
restapi.go:117
➜ ipfs-cluster-ctl 17:24:10.803 INFO ipfshttp: IPFS Pin request succeeded: Qmbvkmk9LFsGneteXk3G7YLqtLVME566ho6ibaQZZVHaC9 ipfshttp.go:372

ipfs pin ls |grep Qmbvkmk9LFsGneteXk3G7YLqtLVME566ho6ibaQZZVHaC9
Qmbvkmk9LFsGneteXk3G7YLqtLVME566ho6ibaQZZVHaC9 recursive
  • 节点测试副本

192.168.100.4 及 192.168.100.5 均存在副本

1
2
ipfs pin ls |grep Qmbvkmk9LFsGneteXk3G7YLqtLVME566ho6ibaQZZVHaC9
Qmbvkmk9LFsGneteXk3G7YLqtLVME566ho6ibaQZZVHaC9 recursive

ipfs 文件分发

ipfs
初识ipfs, 认为其能保证文件高可用.听某人聊起在A节点添加文件,过了许久后,A节点离线(退出 ipfs daemon), B节点已无法(添加后的第一次)访问A节点添加的文件, 即 文件并不会主动分发 .由此做一些测试并做如下总结.

ipfs文件add及访问测试

ipfs单节点的文件存储

ipfs add xxx 默认启用pin, 即将文件复制一份副本存储在本地

  • 添加文件
1
2
3
4
5
6
7
8
9
10
11
echo 'pin.no.daemon' > pin.no.daemon.txt

ipfs add pin.no.daemon.txt
added bafk43jqbedag3yc4ixfr73frvbvz5fs6afx4rogxiiaipmdcce7jgcrdlwa5o pin.no.daemon.txt
14 B / 14 B [=======================================================================================================] 100.00%

ipfs pin ls |grep bafk43jqbedag3yc4ixfr73frvbvz5fs6afx4rogxiiaipmdcce7jgcrdlwa5o
bafk43jqbedag3yc4ixfr73frvbvz5fs6afx4rogxiiaipmdcce7jgcrdlwa5o recursive

ipfs cat /ipfs/bafk43jqbedag3yc4ixfr73frvbvz5fs6afx4rogxiiaipmdcce7jgcrdlwa5o
pin.no.daemon

添加文件后使用 ipfs pin ls 命令可查询文件到已 pin 到本地

  • 删除本地文件测试
1
2
3
4
5
6
7
rm pin.no.daemon.txt

ipfs pin ls |grep bafk43jqbedag3yc4ixfr73frvbvz5fs6afx4rogxiiaipmdcce7jgcrdlwa5o
bafk43jqbedag3yc4ixfr73frvbvz5fs6afx4rogxiiaipmdcce7jgcrdlwa5o recursive

ipfs cat /ipfs/bafk43jqbedag3yc4ixfr73frvbvz5fs6afx4rogxiiaipmdcce7jgcrdlwa5o
pin.no.daemon

删除源文件及仍可访问, 推测原因是ipfs add 命令 pin 了副本

  • 删除pin文件测试
1
2
3
4
5
6
7
ipfs pin rm bafk43jqbedag3yc4ixfr73frvbvz5fs6afx4rogxiiaipmdcce7jgcrdlwa5o
unpinned bafk43jqbedag3yc4ixfr73frvbvz5fs6afx4rogxiiaipmdcce7jgcrdlwa5o

ipfs pin ls |grep bafk43jqbedag3yc4ixfr73frvbvz5fs6afx4rogxiiaipmdcce7jgcrdlwa5o

ipfs cat /ipfs/bafk43jqbedag3yc4ixfr73frvbvz5fs6afx4rogxiiaipmdcce7jgcrdlwa5o
pin.no.daemon

“删除” pin文件发现仍可访问,推测’删除’并未立刻生效,联想到是否有类似 GC 机制

  • GC后测试
1
2
3
4
5
6
ipfs repo gc
...
removed bafk43jqbedag3yc4ixfr73frvbvz5fs6afx4rogxiiaipmdcce7jgcrdlwa5o

ipfs cat /ipfs/bafk43jqbedag3yc4ixfr73frvbvz5fs6afx4rogxiiaipmdcce7jgcrdlwa5o
Error: merkledag: not found

GC才会真正回收 unpined 文件

  • GC 机制

ipfs config show 查看 Datastore 配置

1
2
3
4
5
6
7
8
9
{
"Datastore": {
"StorageMax": "10GB", // 最大存储空间, 实际上, 存储量超过该值仍可以继续存储
"StorageGCWatermark": 90, // 存储空间警戒线, 只有 已使用存储空间/最大存储空间 超过该值, 定时自动 GC 才会生效, 否则不会 GC
"GCPeriod": "1h", // 每过 1h, 检查是否需要 GC

...
},
}

默认定时GC不开启, 使用ipfs daemon --enable-gc开启, 使用ipfs repo gc手动触发 GC

多节点情况下测试

A、B 两节点组成的私有网络,都使用 ipfs daemon 启动

  • A节点添加文件
1
2
3
4
5
6
7
8
9
ipfs repo gc
echo 'pin.with.daemon' > pin.with.daemon.txt

ipfs add pin.with.daemon.txt
added bafk43jqbedzjup3mmlj5vuq65nqpkoob3ayjnnsmv4sq75zy7bmu4h3zws43i pin.with.daemon.txt
16 B / 16 B [==========================================================================================] 100.00%

ipfs pin ls |grep bafk43jqbedzjup3mmlj5vuq65nqpkoob3ayjnnsmv4sq75zy7bmu4h3zws43i
bafk43jqbedzjup3mmlj5vuq65nqpkoob3ayjnnsmv4sq75zy7bmu4h3zws43i recursive

文件pin.with.daemon.txt pin 在 A 节点

  • B节点访问
1
2
3
4
5
6
7
8
9
10
11
12
13
14
ipfs repo gc

ipfs pin ls |grep bafk43jqbedzjup3mmlj5vuq65nqpkoob3ayjnnsmv4sq75zy7bmu4h3zws43i

ipfs cat /ipfs/bafk43jqbedzjup3mmlj5vuq65nqpkoob3ayjnnsmv4sq75zy7bmu4h3zws43i
pin.with.daemon

ipfs pin ls |grep bafk43jqbedzjup3mmlj5vuq65nqpkoob3ayjnnsmv4sq75zy7bmu4h3zws43i

ipfs get /ipfs/bafk43jqbedzjup3mmlj5vuq65nqpkoob3ayjnnsmv4sq75zy7bmu4h3zws43i
Saving file(s) to bafk43jqbedzjup3mmlj5vuq65nqpkoob3ayjnnsmv4sq75zy7bmu4h3zws43i
16 B / 16 B [=======================================================================================] 100.00% 0s

ipfs pin ls |grep bafk43jqbedzjup3mmlj5vuq65nqpkoob3ayjnnsmv4sq75zy7bmu4h3zws43i

B 节点可访问,但并无 pin 副本

  • A节点删除文件
1
2
3
4
5
6
7
8
rm pin.with.daemon.txt 

ipfs pin rm bafk43jqbedzjup3mmlj5vuq65nqpkoob3ayjnnsmv4sq75zy7bmu4h3zws43i
unpinned bafk43jqbedzjup3mmlj5vuq65nqpkoob3ayjnnsmv4sq75zy7bmu4h3zws43i

ipfs repo gc
...
removed bafk43jqbedzjup3mmlj5vuq65nqpkoob3ayjnnsmv4sq75zy7bmu4h3zws43i
  • B节点访问
1
2
3
4
ipfs cat /ipfs/bafk43jqbedzjup3mmlj5vuq65nqpkoob3ayjnnsmv4sq75zy7bmu4h3zws43i
pin.with.daemon

ipfs pin ls |grep bafk43jqbedzjup3mmlj5vuq65nqpkoob3ayjnnsmv4sq75zy7bmu4h3zws43i

此时B节点仍旧可以访问,但无 pin 副本

  • B节点GC后访问
1
2
3
4
5
6
ipfs repo gc
removed bafk43jqbedzjup3mmlj5vuq65nqpkoob3ayjnnsmv4sq75zy7bmu4h3zws43i

ipfs pin ls |grep bafk43jqbedzjup3mmlj5vuq65nqpkoob3ayjnnsmv4sq75zy7bmu4h3zws43i

ipfs cat /ipfs/bafk43jqbedzjup3mmlj5vuq65nqpkoob3ayjnnsmv4sq75zy7bmu4h3zws43i

B节点缓存失效, ipfs cat 因私有网络已无副本挂起

ipfs文件add及访问机制

  • ipfs add并不会主动分发文件到其他节点

  • 访问文件并不会pin到本地节点

  • 无论是访问操作还是 pin 操作均会在数据库中备份(大于分片的情况下可能不是全量)

  • 触发GC会删除unpined数据

ipfs如何保证可靠分布式存储

参考 Replication on IPFS

  • ipfs 协议用于保证节点之间的数据快速共享访问, 并不保证可靠存储

  • 若要实现可靠存储,应基于 ipfs 之上构建实现可靠存储的新协议,如 ipfs-cluster

流量拦截

流量引导方案

OTT设备中的app通常需要对视频、游戏加速, 广电等运营商网络中的IPTV终端需要进行内容管控及控制带宽成本, 各类终端(笔记本、路由器)等需要科学上网首先要解决的就是拦截本机流量并转发至远程代理. 下面以 linux 为例探讨本地流量引导技术方案

应用层拦截

http 代理协议, 如 linux 系统设置全局http代理, 需要客户端系统主动设置, 可实现http流量全量引导, 应用层加速受协议限制较大

传输层拦截

引导方案

  • 通过添加 iptables -t nat ... -j DNAT/REDIRECT ... 规则实现流量转发至本地代理(远程代理),本地代理可对流量做二次过滤将流量转发到远程代理服务器或源站
  1. DNAT 流量转发至远程代理, 通常用于特定端口转发, 远程代理无法知道原始目的地址, 按既定的加速服务处理请求

  2. REDIRECT/DNAT 流量转发至本地, 本地代理可以获取原始目的地址, 选择直接发送数据包至原站或发送数据包及原始目的地址到远程代理服务器加速

  3. tproxy 配合策略路由将流量引导至本地代理, 针对 udp, 具体做法是在 mangle 表的 PREROUTING 链中为每个 udp 数据包打上 0x2333/0x2333 标签并设定需获取报文信息的本地代理地址 127.0.0.1:1080, 策略路由设置将具有 0x2333/0x2333 标志的数据包投递到本地环回设备上的 1080 端口

1
2
3
iptables -t mangle -A PREROUTING -p udp -j tproxy --tproxy-mark 0x2333/0x2333 --on-ip 127.0.0.1 --on-port 1080
ip rule add fwmark 0x2333/0x2333 pref 100 table 100
ip route add local default dev lo table 100

对监听 1080 端口的本地代理 socket 需设置 IP_TRANSPARENT 标志

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int
create_server_socket(const char *host, const char *port)
{
...
#ifdef MODULE_REDIR
if (setsockopt(server_sock, SOL_IP, IP_TRANSPARENT, &opt, sizeof(opt))) {
ERROR("[udp] setsockopt IP_TRANSPARENT");
exit(EXIT_FAILURE);
}
if (rp->ai_family == AF_INET) {
if (setsockopt(server_sock, SOL_IP, IP_RECVORIGDSTADDR, &opt, sizeof(opt))) {
FATAL("[udp] setsockopt IP_RECVORIGDSTADDR");
}
} else if (rp->ai_family == AF_INET6) {
if (setsockopt(server_sock, SOL_IPV6, IPV6_RECVORIGDSTADDR, &opt, sizeof(opt))) {
FATAL("[udp] setsockopt IPV6_RECVORIGDSTADDR");
}
}
#endif
...
}

本地代理原始目的地址获取

  • tcp协议

本地代理收到经 iptables -t nat ... -j DNAT/REDIRECT ... 转发后的tcp请求可采用 getsockopt(fd, SOL_IP, SO_ORIGINAL_DST, destaddr, &socklen) 获取原始目的地址 destaddr

  • udp协议

事实上在 linux 中对于上述 SO_ORIGINAL_DST 调用最终会调用 getorigdst, 因此 udp 协议无法使用 SO_ORIGINAL_DST 获取原始目的地址

1
2
3
4
5
6
/* We only do TCP and SCTP at the moment: is there a better way? */
if (tuple.dst.protonum != IPPROTO_TCP &&
tuple.dst.protonum != IPPROTO_SCTP) {
pr_debug("SO_ORIGINAL_DST: Not a TCP/SCTP socket\n");
return -ENOPROTOOPT;
}
  1. tproxy 方案 udp 原始目的地址获取

对监听 1080 端口的本地代理 socket 设置 IP_TRANSPARENT 标志后, 需调用 recvmsg 函数接收数据包,采用循环比对 IP_RECVORIGDSTADDR/IPV6_RECVORIGDSTADDR 获取原始目的地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
static int
get_dstaddr(struct msghdr *msg, struct sockaddr_storage *dstaddr)
{
struct cmsghdr *cmsg;

for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
if (cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_RECVORIGDSTADDR) {
memcpy(dstaddr, CMSG_DATA(cmsg), sizeof(struct sockaddr_in));
dstaddr->ss_family = AF_INET;
return 0;
} else if (cmsg->cmsg_level == SOL_IPV6 && cmsg->cmsg_type == IPV6_RECVORIGDSTADDR) {
memcpy(dstaddr, CMSG_DATA(cmsg), sizeof(struct sockaddr_in6));
dstaddr->ss_family = AF_INET6;
return 0;
}
}

return 1;
}
  1. REDIRECT/DNAT 方案 udp 原始目的地址获取

上述 1. 中 tproxy 模块需要编译内核开启支持, 实际上 linux 内核采用 nf_conntrack 跟踪网络连接信息(包括udp), 此表可以通过**/proc/net/nf_conntrack** 查看, 里面包含tcp、udp的连接跟踪信息
本地代理接收到 REDIRECT/DNAT 的udp 数据包后, 可依据本地代理地址, 客户端地址, nat端口信息查询 nf_conntrack 表获取原始目的地址, 可参考 一种UDP协议加速方法和系统

IP层拦截

路由器 梅林 RT-AC68U

梅林固件

  • RT-AC68U/RT-1750B1 默认出厂采用路由器设置界面直接上传梅林固件(第三方固件)刷机无法通过验证,可采用降级后再刷

  • 固件升级在路由器管理界面中的系统管理 - 固件升级

  1. 降级至 3.0.0.4.384.10007

3.0.0.4.384.10007, 提取码 2dft

  1. 升级至 380.70-0-X7.9

380.70-0-X7.9

公网ip及ddns

  • 公网ip

坐标福建厦门,电信宽带拨号,拨打10000号申请公网ip,按客服提示半小时后重启光猫及路由器即可,其中光猫客服可远程重启

  • ddns

RT-AC68U自带华硕的ddns,可开启梅林 外部网络(WAN) - DDNS 中的 DDNS Client, 将自定义的 主机名称 与路由器 WAN ip绑定ddns setting
设置完成后梅林中的 网络地图显示如下netmap
外网可通过 ping 自定义前缀.asuscomm.com ( 需开启梅林中的 防火墙 - 一般设置 中的 响应ICMP Echo(ping)要求 )

  • ssh

具有公网ip后,设置梅林的 系统管理 - 系统设置 中的 SSH Daemon, 即可采用公网登陆梅林ssh setting

NAS

由于受够了一些某网的上传慢、限速等诟病,且云盘安全性及迁移也较为麻烦.考虑到更新了电信宽带具备公网ip,因此依托梅林搭建具备 公网访问能力的NAS服务 供家庭使用,用于手机等客户端随时随地能下载备份照片、文件及电影等,即安全又方便.NAS服务 以smb协议为例实现局域网及公网NAS服务

局域网 NAS

  • 插入外置U盘或移动硬盘

  • 设置smb

开启梅林中的 USB 相关应用程序 - 网络共享(Samba) / 云端硬盘, 其中默认账户为路由器账户,按硬盘存储的文件夹共享,可设置账号、共享文件夹、权限smb setting
iphone 手机连接路由wifi并设置smb(路由器ip,smb默认端口445)即可,例如 ES文件游览器 FileExplore

公网 NAS

  • 由于安全问题,运营商 封了smb服务 的tcp端口 445、139 及udp端口 137、138, 同时封闭了 80、8080 等需要备案的端口

  • smb服务优先运行于445端口,因此可做端口转发实现外网访问smb服务,由于 路由器管理界面无法添加445端口的端口转发, 因此必须 ssh 登陆路由器设置端口转发

  1. 内网 NAS 设置完成后 ps |grep smbd 可以观察到 smbd 进程, netstat -ntlp|grep 445 发现其监听在 br0 及 lo 网卡上

  2. 添加转发规则,其中 192.168.50.1 应改为 smbd 监听网卡ip, 本例防火墙是关闭的,若开启需要考虑防火墙对端口转发的限制,自定义防火墙设置可采用 jssf 中添加脚本守护

1
iptables -t nat -I VSERVER -p tcp -m tcp --dport 30445 -j DNAT --to-destination 192.168.50.1:445
  1. 实测发现,端口转发不起作用,原因是 梅林 源码 lfpctrl 对 445 做了端口转发限制, 关闭限制
1
echo 0 > /proc/net/lfpctrl

由于 2. 和 3. 的操作无法固化,路由器重启后将失效,可在 jssf 中增加启动脚本新建 cron 定时任务 check. 其中 jffs 为路由器的非易失存储,重启后数据仍保持,但是刷固件后会丢失,需要在梅林中配置开启(某些版本固件已经开启请忽略). init-start 为 jffs mount后其他服务启动前的调用脚本jssf setting
ssh登入路由器执行如下脚本,两分钟后 iptables -t nat -nL|grep 30445 查看是否有转发规则

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
cd /jffs/scripts

cat << EOF > custom-smb
#!/bin/sh
touch /tmp/000custom_smb

success_30445=`iptables -t nat -nL|grep 30445`
if [ "$success_30445" == "" ]; then
iptables -t nat -I VSERVER -p tcp -m tcp --dport 30445 -j DNAT --to-destination 192.168.50.1:445
echo "fix success_30445!\n" >> /tmp/000custom_smb
fi

success_lfpctrl=`cat /proc/net/lfpctrl`
if [ "$success_lfpctrl" != "0,(null),0" ]; then
echo 0 > /proc/net/lfpctrl
echo "fix success_lfpctrl!\n" >> /tmp/000custom_smb
fi
EOF

cat << EOF > init-start
#!/bin/sh

cru a custom-smb "*/2 * * * * /jffs/scripts/custom-smb"
EOF

chmod 755 init-start
chmod 755 custom-smb
./init-start

NAS 客户端

  • 由于smb服务端口设置了转发,客户端需要能 指定非445的服务端端口 ,ios系统有 FileExplorer 支持多种格式,对于处理文档图片等很实用

  • 关于smb中的视频, FileExplorer 支持的视频格式不多,但支持调用外部播放器. 其他的支持自定义端口smb服务端的NAS视频播放器有 nPlayer 也是非常值得推荐的

nodejs array join

Array 和 Uint8Array 初始化

new Array(3) 初始化为 3个 undefine 元素的 []
new Uint8Array(3) 初始化为 3个 ‘0’ 元素的 []

Array join

1
2
console.log(new Array(3).join('0')) // 输出: "00"
console.log(new Uint8Array(3).join('0')) // 输出: "00000"

leftPad 的两种实现,运用 Uint8Array 和 Array 的区别

1
2
3
4
5
6
7
8
9
10
11
12
13
function leftPadByUint8Array(input, num) {
if (input.length >= num) return input

return (new Uint8Array(num - input.length)).join('') + input
}

function leftPadByArray(input, num) {
if (input.length >= num) return input

return (new Array(num - input.length + 1)).join('0') + input
}

console.log(leftPadByUint8Array('00567', 8) == leftPadByArray('0567', 8))

椭圆曲线加密

椭圆曲线应用原理

离散对数问题

ECC算法是在有限域Fp定义公式:Q=kP,已知大数k和点P的情况下,很容易求点Q,但是已知的点P、点Q,却很难求得k,这就是经典的离散对数问题,ECC算法正是利用该特点进行加密,点Q为公钥,大数k为私钥,点P为基点

数字签名算法

1、选择一条椭圆曲线Ep(a,b),和基点G
2、选择私有密钥da(da < n, n为G的阶),利用基点G计算公开密钥 Qa = da * G
3、产生一个随机整数k (k < n),计算点(x, y) = k * G,取x坐标为R
4、对原数据做hash为z
5、利用方程 S = k^-1 * (z + da * R) mod p 计算S
6、R和S做为签名值, 如果R和S其中一个为0, 重新从第3步开始执行

数字签名验证

1、接受方在收到消息(m)和签名值(R,S)后,进行以下运算
2、对原数据做hash为z
3、计算 P = S^-1 * z * G + S^-1 * R * Qa
4、如果P的 x = R, 则签名有效

椭圆曲线非对称加解密

1、椭圆曲线上的两对公私钥 Qa = da * G, Qb = db * G
2、设用户密钥为 [da, Qa]
3、临时生成密钥为 [db, Qb]
4、运用用户公钥及临时私钥计算加密密钥: Key = Qa * db,运用Key对原文做加密,返回密文及临时公钥Qb,返回信息中可选包含Key及原文的hash
5、解密时提取临时公钥Qb,使用用户私钥计算加密密钥: Key = da * Qb,运用Key对密文做解密,可选对hash信息做验证
6、运用Key对原文做加解密本质是对称加密,只要能通过Key进行加解密即可,由于Key的长度有限,需要考虑Key的加解密算法安全性

椭圆曲线

曲线方程

1
2
y^2 = x^3 + ax + b
4a^3 + 27b^2 ≠ 0

其中:4a^3 + 27b^2 ≠ 0 这个限定条件是为了保证曲线不包含奇点(在数学中是指曲线上任意一点都存在切线)

加法

1
C = A + B

椭圆曲线上的两点A、B直线与椭圆曲线的交点,该交点关于x轴对称位置的点,定义为 A + B

数乘

1
C = 2A = A + A

椭圆曲线在A点的切线与椭圆曲线的交点,该焦点关于x轴对称位置的点,定义为 2A

正负取反

椭圆曲线上的点A关于x轴对称位置的点定义为-A

无穷远点

将A与-A相加,过A与-A的直线平行于y轴,可以认为直线与椭圆曲线相交于无穷远点

椭圆曲线中的阿贝尔群

阿贝尔群

一个集合以及一个二元运算所组成的结构满足如下条件(其中 “+” 表示某二院运算):

1、封闭性(closure),如果a和b被包含于群,那么 a + b 也一定是群的元素
2、结合律(associativity)
3、存在一个单位元(identity element)0, 0与任意元素运算不改变其值的元素,即 a + 0 = 0 + a = a
4、每个元素都存在一个逆元(inverse)
5、交换律(commutativity),即 a + b = b + a

椭圆曲线中的阿贝尔群

在椭圆曲线上定义一个群:

1、群中的元素就是椭圆曲线上的点
2、单位元就是无穷处的点0
3、相反数P,是关于X轴对称的另一边的点
4、二元运算规则定义如下:取一条直线上的三点(这条直线和椭圆曲线相交的三点)P, Q, R(皆非零),他们的总和等于0,即: P + Q + R = 0, 如果P,Q,R在一条直线上的话,他们满足: P + ( Q + R ) = Q + ( P + R ) = R + ( P + Q ) = ⋯ = 0,当P,Q点为同一点时,P = Q,满足: P + Q = -R

推出:因为阿贝尔群满足交换律和结合律,所以点P和点-R的二元运算结果必会在曲线上,即 P + P + P 的结果必会在曲线上的另一点Q,以此类推,可以得出得出: Q = kP (k个相同的点P进行二元运(数乘),记做kP)

openssl update

openssl 升级至 1.1.1d

下载openssl-1.1.1d

1
2
3
cd /usr/local/src/
wget https://www.openssl.org/source/openssl-1.1.1d.tar.gz
tar xf openssl-1.1.1d.tar.gz

编译openssl

1
2
3
4
sudo yum -y install gcc zlib zlib-devel
cd openssl-1.1.1d
./config --prefix=/usr/local/openssl shared zlib
make && make install

配置openssl-1.1.1d

1
2
3
4
5
6
7
cp /usr/bin/openssl /usr/bin/openssl.old
ln -sf /usr/local/openssl/bin/openssl /usr/bin/openssl
ln -s /usr/local/openssl/lib/libssl.so.1.1 /usr/lib64/libssl.so.1.1
ln -s /usr/local/openssl/lib/libcrypto.so.1.1 /usr/lib64/libcrypto.so.1.1
sudo echo "/usr/local/openssl/lib/" > /etc/ld.so.conf.d/openssl_1.1.1.conf
sudo ldconfig -v
/usr/bin/openssl version

cmake 引用openssl-1.1.1d

find_package 引用 openssl-1.1.1d

1
2
3
4
5
6
7
8
9
10
cmake_minimum_required( VERSION 3.8 )

project( test )
set( OPENSSL_ROOT_DIR "/usr/local/openssl" )
set( OPENSSL_INCLUDE_DIR "/usr/local/openssl/include" )

find_package( OpenSSL REQUIRED )
message( "OPENSSL_VERSION: " ${OPENSSL_VERSION} )
message( "OPENSSL_INCLUDE_DIR: " ${OPENSSL_INCLUDE_DIR} )
message( "OPENSSL_LIBRARIES: " ${OPENSSL_LIBRARIES} )

pkg_search_module 引用 openssl-1.1.1d

1
2
3
4
5
6
7
8
9
10
11
cmake_minimum_required( VERSION 3.8 )

project( test )

find_package( PkgConfig REQUIRED )
set(ENV{PKG_CONFIG_PATH} "/usr/local/openssl/lib/pkgconfig" )
pkg_search_module( LIBCRYPTO REQUIRED libcrypt )
message( "LIBCRYPTO_VERSION: " ${LIBCRYPTO_VERSION} )

pkg_search_module( LIBSSL REQUIRED libssl )
message( "LIBSSL_VERSION: " ${LIBSSL_VERSION} )

Hello World

Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub.

Quick Start

Create a new post

1
$ hexo new "My New Post"

More info: Writing

Run server

1
$ hexo server

More info: Server

Generate static files

1
$ hexo generate

More info: Generating

Deploy to remote sites

1
$ hexo deploy

More info: Deployment

enable img

  1. 在根目录下配置文件_config.yml 中有 post_asset_folder:false 改为 true

  2. 安装插件

1
npm install https://github.com/7ym0n/hexo-asset-image --save
  1. 应用图片

_posts 目录下会有同名目录,文章中引用目录下的 test.jpg

1
![说明文字](test.jpg)