export PATH=$PATH:/opt/toolchains/taowrt-toolchain-1.0.4-mediatek-filogic_gcc-13.3.0_musl.Linux-x86_64/toolchain-aarch64_cortex-a53_gcc-13.3.0_musl/bin/
./configure --build=aarch64-openwrt-linux
make
Views: 1
export PATH=$PATH:/opt/toolchains/taowrt-toolchain-1.0.4-mediatek-filogic_gcc-13.3.0_musl.Linux-x86_64/toolchain-aarch64_cortex-a53_gcc-13.3.0_musl/bin/
./configure --build=aarch64-openwrt-linux
make
Views: 1
我这里排查到两点原因
1. output hook只能是rotue链,不能是filter链,而prerouting则只能是filter链
要这样写
nft add chain inet fw4 gfw_output { type route hook output priority mangle\; policy accept\; }
filter的优先级是比route低的
官方是这么写的
filter: Supported by arp, bridge, ip, ip6 and inet table families.
route: Mark packets (like mangle for the output hook, for other hooks use the type filter instead), supported by ip and ip6.
nat: In order to perform Network Address Translation, supported by ip and ip6.
2. 本地出口源IP的问题
可能因为默认路由在wan口上,所以本地发包源IP始终是wan口上的IP,而这个IP是运营商分配的100.64.0.0/10段的IP,这样就导致远端服务器找不到到100.64.0.0/10的路由,数据包有去无回,需要在服务器上配置这个网段的路由
如果是wireguard在AllowedIPs中添加100.64.0.0/10网段即可。
参考:
https://forums.gentoo.org/viewtopic-t-1136379-start-0.html
Views: 14
C语言1:
#include <stdio.h>
#include <arpa/inet.h>
unsigned long checksum(char *buf,int nword)
{
unsigned long sum;
unsigned short *data = (unsigned short *) buf;
for(sum=0; nword>0; nword--)
{
sum += *data;
data++;
sum = (sum>>16) + (sum&0xffff);
}
sum = ~checksum & 0xFFFF;
return htons(sum); //转为大端字节序
}
void main(int argc, char *argv[])
{
char b [] = "\0d\0e";
unsigned long ret = checksum(b, 2);
printf("checksum is: 0x%lx\n", ret);
printf("checksum is: %lu\n", ret);
}
C语言2(github copilot):
#include <stdio.h>
#include <stdint.h>
uint16_t udp_checksum(uint8_t *data, size_t len) {
uint32_t checksum = 0;
// 如果数据长度是奇数,填充一个字节
if (len % 2 != 0) {
data[len] = 0;
len++;
}
// 将字节数组分成16位的块并相加
for (size_t i = 0; i < len; i += 2) {
uint16_t word = (data[i] << 8) + data[i + 1];
checksum += word;
// 处理进位
checksum = (checksum & 0xFFFF) + (checksum >> 16);
}
// 取反码
checksum = ~checksum & 0xFFFF;
return (uint16_t)checksum;
}
int main() {
uint8_t data[] = {'a', 'b', 'c', 'd'};
size_t len = sizeof(data) / sizeof(data[0]);
printf("Checksum: 0x%04X\n", udp_checksum(data, len));
return 0;
}
Python(Github copilot):
def udp_checksum(data):
"""下面是一个用Python编写的UDP校验和算法:
### 步骤:
1. 定义一个函数 `udp_checksum`,接受一个字节数组作为参数。
2. 将字节数组分成16位的块。
3. 将所有16位块相加。
4. 如果有进位,则将进位加到结果中。
5. 取结果的反码。
6. 返回低16位,就是校验和。
"""
if len(data) % 2 != 0:
data += b'\x00' # 如果数据长度是奇数,填充一个字节
checksum = 0
for i in range(0, len(data), 2):
# 这个公式是大端的算法,如果是小端可以写作(data[i+1] << 8) + data[i]
word = (data[i] << 8) + data[i + 1]
print(word)
checksum += word
checksum = (checksum & 0xFFFF) + (checksum >> 16) # 处理进位
checksum = ~checksum & 0xFFFF # 取反码
return checksum
# 示例用法
data = b'\0d\0e'
print(hex(udp_checksum(data))) # 输出校验和
关于接收端验证的方法:
把整个需要校验的数据计算校验和(反码求和),如果结果为0x0000则结果就是正确的,或者计算原码求和(累加后不取反),结果为0xffff则就是正确的。网上有的说验证结果为0x0000,有的说验证结果为0xffff要了解其中的原因。
参考:
https://blog.csdn.net/weixin_28673511/article/details/130314065
https://zhuanlan.zhihu.com/p/74381973
Views: 2
dnsmasq开启dhcpv6需要开启以下两项配置
enable-ra
启用icmpv6 ra报文发送功能,这是SLAAC所必须的,实测开不开这一项都会发送ra报文
dhcp-range=::1,::1000,constructor:br-lan,ra-names
要开始dhcpv6必须指定起始和结束主机号,这里是::1和::1000,据我测试指定::或单独一个::1都无法启用dhcpv6,不用担心指定主机号范围后无法使用SLAAC功能,实际上指定主机号范围后SLAAC和DHCPV6是同时开启的。
constructor:br-lan这一项是指定绑定的网卡
ra-names 这一项是利用dhcpv4的主机名来推导ipv6的主机名,可以与其它mode合并使用
不指定结束主机号且无其它mode M:0,O:0,仅使用SLAAC,dhcp服务不启动
指定结束主机号 slaac和dhcpv6同时生效,M:1,O:1
slaac 实测没有效果
ra-only 实测没有效果
ra-stateless M:0,O:1,使用无状态dhcpv6,并禁用有状态dhcpv6,dhcpv6应答会显示无可用地址
如果是openwrt系统,创建/etc/dnsmasq.d/ipv6.conf,添加以下两行
enable-ra
dhcp-range=::1,::1000,constructor:br-lan,ra-names
openwrt系统开启dnsmasq的dhcpv6功能后需要关闭openwrt自带的dhcpv6功能
merlin系统则可以在/jffs/configs目录下修改dnsmasq配置
这样能通过主机名访问到的就是IPv6地址了
更多的语法参考这里:
https://thekelleys.org.uk/dnsmasq/docs/dnsmasq-man.html
RDNSS:
https://datatracker.ietf.org/doc/rfc8106/
dnsmasq实现的RDNSS只有dns server list,没有dns search list
Views: 3
根据RFC4704的说明,只有dhcpv6 statefull才支持主机名解析,也就是dhcp请求报文中的option39字段,stateless及SLAAC都是不支持的,这给主机名解析带来了一些难度,要实现主机名解析可以从以下两个方面入手:
1. 使用dhcpv6 statefull
dnsmasq和odhcpd都支持statefull功能,还需要注意以下方面
– 把RA报文的M和O位设置为1
其实不是也有的客户端都严格按照RA报文的标志来处理,像Windows10上就是始终会执行SLAAC 和DHCPv6两种请求
– 客户端请求报文携带option 39字段
在Windows上保持默认即可,部分Linux系统(Debian)需要在/etc/dhcp/dhclient.conf中 添加send fqdn.fqdn = gethostname();
据我测试macos上也存在这个问题,目前没有解决办法。
statefull和slaac是可以同时使用的,RA通告前缀的同时使用dhcpv6提供地址分配,这样客户端会 获得两个IP地址
参考:
https://datatracker.ietf.org/doc/rfc4704/
https://serverfault.com/questions/1033682/dhclient-is-sending-host-name-for-ipv4-but-not-ipv6
Views: 6
标准的debian系统在
/etc/network/interface中
配置
auto eth0
iface eth0 inet6 dhcp
accept_ra 2
autoconf 1
就好了
但是prombox上面的network script似乎与debian系统不一样,dhcp不生效。
可以采用以下配置
auto vmbr0
iface vmbr0 inet static
address 192.168.33.15/24
gateway 192.168.33.1
bridge-ports eth0 eth1
bridge-stp off
bridge-fd 0
dns-nameservers 192.168.33.1
up dhclient -6 -nw -w vmbr0
down dhclient -6 -r vmbr0
iface vmbr0 inet6 auto
accept_ra 2
autoconf 1
如果还是不生效把dhclient -6 -nw -w vmbr0
写入到/etc/rc.local中
Views: 2
在rule中添加meta nftrace set 1就可以启用trace
nft insert rule inet fw4 input meta l4proto {icmp,icmpv6} nftrace set 1 counter accept #监视icmp,icmpv6
nft insert rule inet fw4 input ip protocol icmp meta nftrace set 1 counter accept #监视icmp
nft insert rule inet fw4 input ip6 nexthdr icmpv6 meta nftrace set 1 counter accept #监视icmpv6
nft insert rule inet fw4 foward ip daddr 192.168.33.6 tcp dport 80 nftrace set 1 counter accept #监视转发的http
nft insert rule inet fw4 prerouting ip daddr 192.168.33.6 tcp dport 80 nftrace set 1 counter accept #监视所有的http
然后执行
nft monitor trace
就可以看到每个收到的包及各个链中rule处理结果
Views: 4
debian/ubuntu安装
apt install coturn
centos安装
yum install coturn coturn-utils
编辑配置文件/etc/turnserver.conf
listening-port=3478
listening-ip=172.31.25.80
listening-ip=240b:4001:210:4a01:f788:1b78:de22:8707
external-ip=8.218.139.135/172.31.25.80
user=test:123456
log-file=/var/log/turn.log
如果监听的IP不是公网IP还需要指定external-ip
systemctl enable coturn
systemctl restart coturn
然后防火墙开放3478端口,包括UDP和TCP
Views: 14
https://www.wireguardconfig.com/
Views: 1
旧标准:
https://datatracker.ietf.org/doc/rfc3489/
RFC 3489 STUN协议设计,2003年发布
https://datatracker.ietf.org/doc/rfc5389/
RFC 5389 STUN工程实现和最佳实践,并对RFC3489进行了更新,2008年发布
新标准:
https://datatracker.ietf.org/doc/rfc5780/
RFC 5780, STUN协议设计,2010年发布
https://datatracker.ietf.org/doc/rfc8489/
RFC 8489 STUN工程实现和最佳实践,2020年发布
STUN的主要作用有两个:
1. 获取通信双方的公网IP地址和端口
2. 检测NAT类型(用于确定后续穿透方案)
内网主机建立一个UDP socket(LocalIP:LocalPort) 第一次使用这个socket给外部主机发送数据时NAT会给其分配一个公网(PublicIP:PublicPort),以后用这个socket向外面任何主机发送数据都将使用这对(PublicIP:PublicPort)。此外,任何外部主机只要知道这个(PublicIP:PublicPort)就可以发送数据给(PublicIP:PublicPort),内网的主机就能收到这个数据包
Restricted Cone NAT
内网主机建立一个UDP socket(LocalIP:LocalPort) 第一次使用这个socket给外部主机发送数据时NAT会给其分配一个公网(PublicIP:PublicPort),以后用这个socket向外面任何主机发送数据都将使用这对(PublicIP:PublicPort)。此外,如果任何外部主机想要发送数据给这个内网主机,只要知道这个(PublicIP:PublicPort)并且内网主机之前用这个socket曾向这个外部主机IP发送过数据。只要满足这两个条件,这个外部主机就可以用自己的(IP,任何端口)发送数据给(PublicIP:PublicPort),内网的主机就能收到这个数据包
Port Restricted Cone NAT
内网主机建立一个UDP socket(LocalIP:LocalPort) 第一次使用这个socket给外部主机发送数据时NAT会给其分配一个公网(PublicIP:PublicPort),以后用这个socket向外面任何主机发送数据都将使用这对(PublicIP:PublicPort)。此外,如果任何外部主机想要发送数据给这个内网主机,只要知道这个(PublicIP:PublicPort)并且内网主机之前用这个socket曾向这个外部主机(IP,Port)发送过数据。只要满足这两个条件,这个外部主机就可以用自己的(IP,Port)发送数据给(PublicIP:PublicPort),内网的主机就能收到这个数据包
Symmetric NAT
内网主机建立一个UDP socket(LocalIP,LocalPort),当用这个socket第一次发数据给外部主机1时,NAT为其映射一个(PublicIP-1,Port-1),以后内网主机发送给外部主机1的所有数据都是用这个(PublicIP-1,Port-1); 如果内网主机同时用这个socket给外部主机2发送数据,第一次发送时,NAT会为其分配一个(PublicIP-2,Port-2), 以后内网主机发送给外部主机2的所有数据都是用这个(PublicIP-2,Port-2).
这九种包含了RFC 3489中的四种类型
RFC 5780中分为了映射类型和过滤类型
如图1所示,假设一个内网主机HostX的内网IP地址为X,端口号为x,经NAT映射后的外网IP地址为M,端口号为m。为方便描述,将内网的Endpoint记为Endpoint(X,x),映射后外网的Endpoint记为Endpoint(M,m)。内网Endpoint(X,x)发往外网HostD1的IP地址和端口号记为目的Endpoint(D1,d1);发往外网HostD2的IP地址和端口号记为目的Endpoint(D2,d2)。
NAT过滤类型
如图2所示,假设一个内网主机HostX的内网IP地址为X,端口号为x,经NAT映射后的外网IP地址为M,端口号为m。为方便描述,将内网的Endpoint记为Endpoint(X,x),映射后外网的Endpoint记为Endpoint(M,m)。内网Endpoint(X,x)发往外网HostD1的IP地址和端口号记为目的Endpoint(D1,d1);发往外网HostD2的IP地址和端口号记为目的Endpoint(D2,d2)。
图2 NAT过滤类型
把映射类型和过滤类型组合起来就是9种NAT类型,分别对应如下:
以上只是RFC的分类方法,不同厂家设备实现细节上可能还有不一样。
全椎型是打通速度最快的,受限锥型可能会延个几秒(防火墙需要等互相收到对方发过来的包才能建立会话),对称型虽然不能直接打通,但是猜测对方端口在一个范围内进行扫描也是可以的,大部分防火墙来说NAT端口分配都是连续的,下一个连接总是在上一个连接的端口号上加一,如果一次没猜中多猜几次就好了,再加上请求的时候 可以一次性打开多个端口,这样更容易猜中。
https://github.com/coturn/coturn
支持STUN,TURN,ICE,但是对STUN的支持并不完整,不支持探测NAT类型
https://github.com/jselbie/stunserver
仅支持STUN,但完整实现了RFC的所有特性,但是要开启所有特性需要服务器有两个公网IP
https://github.com/HMBSbige/NatTypeTester/tree/master
另外stunserver中自带的也有个客户端
如果网络是多出口的对NAT类型探测和穿透还是有很大影响的,在地址受限测试中客户端访问不同的STUN Server IP如果请求从不同的出口出去的话会导致IP和端口都发生变化,这样就成了端口地址受限NAT。在后面的穿透工作中如果访问NAT Server和实际通信用的不是一个出口(IP),就会出现通信振荡不稳定。所以对于多出口的环境最后能绑定线路,或者把有多出口的一端当成对称型NAT来用,由另一端进行穿透。这种情况在一端进行穿透反而比两端同时进行穿透要稳定。如果STUN能增加多线路的支持就更好了,还能利用多个线路同时传输数据。
https://arthurchiao.art/blog/how-nat-traversal-works-zh/#41-stun-%E5%8E%9F%E7%90%86
https://blog.csdn.net/wh445306/article/details/127831832
https://blog.csdn.net/momo0853/article/details/105387675/
https://zhaoyanbai.com/articles/RFC8489_STUN_zh_CN
https://gist.github.com/mondain/b0ec1cf5f60ae726202e
Views: 5