是否可以根据时间戳构建 nftables 映射?
目前我使用:
numgen random mod 2 map {
0: 10.10.10.1,
1: 10.10.10.2,
}
但是如何将random mod 2
表达式转换为(timestamp / 1800) mod 2
,即每30分钟交替生成像0
和这样的地图的键?1
答案1
可以解决这个问题nftables通过使用名为地图。
必需的:
- 内核 >= 5.4:
meta hour
- nftables >= 0.9.4 是较新的版本
typeof
,可以代替它,type
并且它似乎是地图对于meta hour
.type meta hour
或type hour
将不会被接受,即使像使用旧版 nftablestypeof meta hour
一样读回。type hour
nftables
无法执行任意算术(或逻辑等)运算。目前它仅限于对必须来自数据包路径的数据执行一些左侧(LHS)运算或一些扩展(如)numgen
,并将其与常数右侧(RHS)进行比较,包括从集合和映射中获取 RHS 值。
没有办法nftables在 LHS 中计算任意除法(使用右移位除以 2 的幂是可行的,但见下文)。而且似乎也没有办法将这种计算的结果表达为命名映射中可接受的数据类型,因为“不合格”整数此类操作的结果通常是类型,至少目前不是命名映射中的有效键类型。例如,我不确定表达式numgen
是否可以与命名地图,即使它与匿名地图一起使用。
对于这种情况,Linux 内核 5.4在内核中引入与数据包时间戳相关的元语句:
- 在内核中引入时间、日期和小时的元匹配犯罪
这里meta hour
提供了缺少的必需操作“mod 86400”,该操作与 OP 的“timestamp / 1800”要求足够相关:给出自一天开始以来数据包的时间。此外,它具有特定类型,允许在命名映射中使用它。
类似于循环展开为了简化复杂的操作,可以预先计算表达式中无法计算的值并将其存储在映射表中,只要类型对于命名映射有效即可。这是可以接受的,因为条目数是预先知道的,但仍然是有限的:在这种情况下,一天有 48 个半小时。
然后映射可以提供最终结果:IPv4 地址。
负载均衡器规则集示例(执行基因)转发收到的所有流量wan0每小时的前半个小时为 10.10.10.1,后半个小时为 10.10.10.2:
table ip mytable
delete table ip mytable
table ip mytable {
map hour2ip {
typeof meta hour : ip daddr
flags interval
}
chain mylb {
type nat hook prerouting priority dstnat; policy accept;
iif wan0 dnat to meta hour map @hour2ip
}
}
脚本生成 nftables 命令来填充地图小时2ip(使用./script.sh | nft -f -
):
#!/bin/sh
for h in $(seq 0 23); do
for m in 0 30; do
printf 'add element ip mytable hour2ip { "%02d:%02d:00"-"%02d:%02d:59" : %s }\n' $h $m $h $((m+29)) '$ip'
done
done |
sed 's/$ip/10.10.10.X/g' |
awk '{ printf "%s\n",gensub("X",(NR-1)%2+1,1) }'
这将生成此类命令:
add element ip mytable hour2ip { "00:00:00"-"00:29:59" : 10.10.10.1 }
add element ip mytable hour2ip { "00:30:00"-"00:59:59" : 10.10.10.2 }
[...]
add element ip mytable hour2ip { "23:00:00"-"23:29:59" : 10.10.10.1 }
add element ip mytable hour2ip { "23:30:00"-"23:59:59" : 10.10.10.2 }
笔记:
meta hour
本地时区会影响类型数据(模数)发送到内核或从内核显示回来的方式,内核通常只使用 UTC 时间。这就是为什么排序会根据当前时区而发生变化,但其行为将符合预期。您可以检查TZ=UTC
在显示回来时使用或不使用环境变量的效果nft list map ip mytable hour2ip
,以更好地理解它。- 00:29:59 和 00:30:00 之间没有间隙:时间戳向下舍入,因此例如 00:29:59.800ms 仍然与 00:29:59 匹配。
如果你喜欢使用标记获得更多的灵活性(通过重复使用标记除了基因规则,包括将其保存到康马克),例如,可以使用两个映射:一个映射小时到标记以及另一个用于映射标记到 IP 地址。
为此,例如将以前的规则集替换为:
table ip mytable
delete table ip mytable
table ip mytable {
map hour2mark {
typeof meta hour : meta mark
flags interval
}
map mark2ip {
typeof meta mark : ip daddr
elements = { 1 : 10.10.10.1, 2 : 10.10.10.2 }
}
chain mylb {
type nat hook prerouting priority dstnat; policy accept;
iif wan0 meta mark set meta hour map @hour2mark dnat to meta mark map @mark2ip
}
}
并在先前的脚本中替换生成的条目:
sed 's/$ip/10.10.10.X/g' |
和:
sed 's/hour2ip/hour2mark/;s/$ip/X/g' |
创建要填充的命令小时2标记, 喜欢:
add element ip mytable hour2mark { "00:00:00"-"00:29:59" : 1 }
add element ip mytable hour2mark { "00:30:00"-"00:59:59" : 2 }
[...]
add element ip mytable hour2mark { "23:00:00"-"23:29:59" : 1 }
add element ip mytable hour2mark { "23:30:00"-"23:59:59" : 2 }