Linux 中的快速路由和 NAT

随着 IPv4 地址的耗尽,许多电信运营商都面临着使用地址转换为其客户提供网络访问的需要。 在本文中,我将告诉您如何在商用服务器上获得运营商级 NAT 性能。

有一点历史

IPv4 地址空间耗尽的话题不再是新鲜事。 在某个时候,RIPE 中出现了等待名单,然后出现了交易所,在这些交易所上进行地址块的交易并达成租赁这些地址的交易。 逐渐地,电信运营商开始使用地址和端口转换来提供互联网接入服务。 有些人无法获得足够的地址来向每个订阅者发放“白色”地址,而另一些人则开始通过拒绝在二级市场上购买地址来省钱。 网络设备制造商支持这个想法,因为此功能通常需要额外的扩展模块或许可证。 例如,在 Juniper 的 MX 路由器系列中(最新的 MX104 和 MX204 除外),您可以在单独的 MS-MIC 服务卡上执行 NAPT,Cisco ASR1k 需要 CGN 许可证,Cisco ASR9k 需要单独的 A9K-ISM-100 模块以及他的 A9K-CGN 许可证 -LIC。 一般来说,这种乐趣要花很多钱。

iptables的

执行 NAT 的任务不需要专门的计算资源;它可以通过安装在例如任何家庭路由器中的通用处理器来解决。 在电信运营商的规模上,可以使用运行 FreeBSD (ipfw/pf) 或 GNU/Linux (iptables) 的商品服务器来解决这个问题。 我们不会考虑 FreeBSD,因为…… 我很久以前就不再使用这个操作系统了,所以我们将坚持使用 GNU/Linux。

启用地址转换并不困难。 首先需要在 iptables 的 nat 表中注册一条规则:

iptables -t nat -A POSTROUTING -s 100.64.0.0/10 -j SNAT --to <pool_start_addr>-<pool_end_addr> --persistent

操作系统将加载 nf_conntrack 模块,该模块将监视所有活动连接并执行必要的转换。 这里有几个微妙之处。 首先,由于我们讨论的是电信运营商规模的 NAT,因此有必要调整超时,因为使用默认值时,转换表的大小将迅速增长到灾难性的值。 以下是我在服务器上使用的设置示例:

net.ipv4.ip_forward = 1
net.ipv4.ip_local_port_range = 8192 65535

net.netfilter.nf_conntrack_generic_timeout = 300
net.netfilter.nf_conntrack_tcp_timeout_syn_sent = 60
net.netfilter.nf_conntrack_tcp_timeout_syn_recv = 60
net.netfilter.nf_conntrack_tcp_timeout_established = 600
net.netfilter.nf_conntrack_tcp_timeout_fin_wait = 60
net.netfilter.nf_conntrack_tcp_timeout_close_wait = 45
net.netfilter.nf_conntrack_tcp_timeout_last_ack = 30
net.netfilter.nf_conntrack_tcp_timeout_time_wait = 120
net.netfilter.nf_conntrack_tcp_timeout_close = 10
net.netfilter.nf_conntrack_tcp_timeout_max_retrans = 300
net.netfilter.nf_conntrack_tcp_timeout_unacknowledged = 300
net.netfilter.nf_conntrack_udp_timeout = 30
net.netfilter.nf_conntrack_udp_timeout_stream = 60
net.netfilter.nf_conntrack_icmpv6_timeout = 30
net.netfilter.nf_conntrack_icmp_timeout = 30
net.netfilter.nf_conntrack_events_retry_timeout = 15
net.netfilter.nf_conntrack_checksum=0

其次,由于转换表的默认大小不适用于电信运营商的条件,因此需要增加:

net.netfilter.nf_conntrack_max = 3145728

还需要增加存储所有广播的哈希表的桶数(这是 nf_conntrack 模块中的一个选项):

options nf_conntrack hashsize=1572864

经过这些简单的操作后,就获得了一个完整的工作设计,可以将大量客户端地址转换为外部地址池。 然而,该解决方案的性能还有很多不足之处。 在我第一次尝试使用 GNU/Linux 进行 NAT 时(大约 2013 年),我能够以每台服务器 7Mpps 的速度获得大约 0.8Gbit/s 的性能 (Xeon E5-1650v2)。 从那时起,GNU/Linux 内核网络堆栈进行了许多不同的优化,同一硬件上一台服务器的性能已提高到几乎 18-19 Gbit/s、1.8-1.9 Mpps(这些是最大值) ,但对一台服务器处理的流量的需求增长得更快。 因此,开发了一些方案来平衡不同服务器上的负载,但这一切都增加了设置、维护和维护所提供服务质量的复杂性。

NF表

如今,“移包”软件的一个流行趋势是使用DPDK和XDP。 关于这个主题已经写了很多文章,做了很多不同的演讲,并且商业产品正在出现(例如 VasExperts 的 SKAT)。 但考虑到电信运营商的编程资源有限,基于这些框架自行创建任何“产品”是相当有问题的。 未来操作这样的解决方案将会更加困难;特别是必须开发诊断工具。 例如,带有 DPDK 的标准 tcpdump 不会像那样工作,并且它不会“看到”使用 XDP 发送回线路的数据包。 在所有关于将数据包转发到用户空间的新技术的讨论中,它们都没有被注意到 报告 и 文章 Pablo Neira Ayuso,iptables 维护者,关于 nftables 中流量卸载的开发。 让我们仔细看看这个机制。

主要思想是,如果路由器在流的两个方向上传递来自一个会话的数据包(TCP 会话进入 ESTABLISHED 状态),则无需通过所有防火墙规则传递该会话的后续数据包,因为所有这些检查仍然会随着数据包进一步传输到路由而结束。 我们实际上不需要选择路由 - 我们已经知道我们需要在此会话中将数据包发送到哪个接口以及哪个主机。 剩下的就是存储这些信息并在数据包处理的早期阶段将其用于路由。 进行NAT时,需要额外存储nf_conntrack模块翻译的地址和端口变化信息。 是的,当然,在这种情况下,iptables 中的各种监管器和其他信息和统计规则将停止工作,但在单独的常设 NAT 或边界的任务框架内,这并不那么重要,因为服务分布在各个设备上。

布局

要使用这个功能我们需要:

  • 使用新鲜的内核。 尽管该功能本身出现在内核 4.16 中,但在相当长的一段时间内它都非常“原始”,并且经常引起内核恐慌。 2019 年 4.19.90 月左右,当 LTS 内核 5.4.5 和 XNUMX 发布时,一切都稳定下来。
  • 使用 nftables 的最新版本以 nftables 格式重写 iptables 规则。 与 0.9.0 版本完全兼容

如果第一点原则上的一切都很清楚,最主要的是不要忘记在组装期间将模块包含在配置中(CONFIG_NFT_FLOW_OFFLOAD = m),那么第二点需要解释。 nftables 规则的描述与 iptables 中的规则完全不同。 Документация 揭示了几乎所有的点,也有特殊的 转换器 从 iptables 到 nftables 的规则。 因此,我仅给出设置 NAT 和流量卸载的示例。 一个小传说例如: , - 这些是流量通过的网络接口;实际上可以有两个以上。 , —“白色”地址范围的起始和结束地址。

NAT配置非常简单:

#! /usr/sbin/nft -f

table nat {
        chain postrouting {
                type nat hook postrouting priority 100;
                oif <o_if> snat to <pool_addr_start>-<pool_addr_end> persistent
        }
}

对于流量卸载,情况稍微复杂一些,但很容易理解:

#! /usr/sbin/nft -f

table inet filter {
        flowtable fastnat {
                hook ingress priority 0
                devices = { <i_if>, <o_if> }
        }

        chain forward {
                type filter hook forward priority 0; policy accept;
                ip protocol { tcp , udp } flow offload @fastnat;
        }
}

事实上,这就是整个设置。 现在,所有 TCP/UDP 流量都将落入 fastnat 表中,并且处理速度会更快。

结果

为了清楚地说明这有多“快”,我将附上两台真实服务器上的负载屏幕截图,具有相同的硬件(Xeon E5-1650v2)、相同的配置、使用相同的 Linux 内核,但在 iptables 中执行 NAT (NAT4) 和 nftables (NAT5)。

Linux 中的快速路由和 NAT

屏幕截图中没有每秒数据包的图表,但在这些服务器的负载配置文件中,平均数据包大小约为 800 字节,因此该值高达 1.5Mpps。 可以看到,带有nftables的服务器有巨大的性能储备。 目前,该服务器在 30Mpps 下的处理速度高达 3Gbit/s,显然能够满足 40Gbps 的物理网络限制,同时拥有空闲的 CPU 资源。

我希望这份材料对试图提高服务器性能的网络工程师有用。

来源: habr.com

添加评论