跳转至内容

Internet 共享

来自 ArchWiki
(从 Internet Sharing 重定向)

本文解释了如何将一台机器上的互联网连接共享给其他机器。

需求

充当服务器的机器应具有额外的网络设备,也称为网络接口。该网络设备需要与将要获得互联网访问权的机器建立功能性的数据链路层连接。

  • 为了能够将互联网共享给多台机器,交换机可以提供数据链路层连接。
  • 无线设备也可以向多台机器共享访问权,在这种情况下,请先参阅软件接入点
  • 如果你只共享给一台机器,交叉网线就足够了。如果两台计算机的以太网卡之一具有自动 MDI-X 功能,则不需要交叉网线,可以使用普通的以太网线。以 root 身份执行 ethtool interface | grep MDI 命令有助于确定。即使该命令给出错误或未在任一台机器上找到任何信息,而你又没有交叉网线,你可能仍然可以继续。

配置

注意 本节假设连接到客户端计算机的网络设备名为 net0,连接到互联网的网络设备名为 internet0
提示 你可以使用 udev#设置静态设备名称 将设备重命名为该方案。

所有配置都在服务器计算机上完成,除了最后一步#为客户端 PC 分配 IP 地址

静态 IP 地址

在服务器计算机上,为连接到其他计算机的接口分配一个静态 IPv4 地址。该地址的前 3 个字节不能与另一个接口的字节完全相同,除非两个接口的网络掩码都严格大于 /24。

# ip link set up dev net0
# ip addr add 192.168.123.100/24 dev net0 # arbitrary address

要在启动时分配静态 IP,你可以使用网络管理器

启用数据包转发

警告 在没有正确配置防火墙的情况下启用 IP 转发存在安全风险。

要检查当前的数据包转发设置,请运行

# sysctl -a | grep forward

你会注意到有关控制默认转发、按接口转发以及按接口区分 IPv4/IPv6 的选项。有关所有可用选项的详细描述,请参阅内核文档

要启用 IPv4 和 IPv6 数据包转发,请配置 sysctl 来设置这些选项

net.ipv4.ip_forward = 1
net.ipv4.conf.all.forwarding = 1
net.ipv6.conf.all.forwarding = 1
提示 要为特定接口选择性地启用数据包转发,请使用 net.ipv[46].conf.interface_name.forwarding=1 而不是。
警告

本文或本章节已过时。

原因:很可能已过时。由于 systemd-networkd 256+ 现在支持在每个链接级别设置 IPv4Fowarding。请删除此说明和下面的警告(在 Talk:Internet sharing 中讨论)。

如果系统使用 systemd-networkd 来控制网络接口,则无法进行按接口设置 IPv4,即 systemd 逻辑会将任何配置的转发传播为 IPv4 的全局(所有接口)设置。建议的解决方法是使用防火墙在选择性接口上禁止再次转发。有关更多信息,请参阅 systemd.network(5) 手册页。在系统版本 220/221 中引入以尊重内核设置的 IPForward=kernel 语义已不再适用。[1] [2]

要使更改在重启后持久生效,请参阅 Sysctl#配置。您可以考虑将设置写入一个具有描述性文件名的文件,例如 /etc/sysctl.d/30-ipforward.conf

之后,建议在重启后仔细检查转发是否按要求启用。

使用 systemd-networkd 进行数据包转发

本文档或节候选合并至 systemd-networkd#[Network]

说明:任意文件名对于通用示例没有意义。没有 [Match] 部分的 .network 文件没有意义。只需说明需要设置什么,然后链接到 systemd-networkd#[Network] 页面。(在 Talk:Internet sharing 中讨论)

如果你正在使用 systemd-networkd 来管理你的网络配置,你也可以在重启后持久化这些设置

/etc/systemd/network/20-lan.network
[Network]
IPv4Forwarding=yes
...

这实际上设置了与上一节中提到的 net.ipv[46].conf.interface_name.forwarding=1 相同的选项。对于 IPv6,配置是 IPv6Forwarding=yes

注意 从 systemd 256 开始,IPv6Forwarding 选项的行为与文档不符,请参阅 [3]

这仅为特定接口设置了数据包转发。为了使互联网共享正常工作,你需要为应该在其中路由流量的所有接口(通常是你的 lanwan 接口)启用数据包转发。

另见 systemd-networkd#[Network][链接已损坏: 无效的节]

启用 NAT

除了此处列出的方法外,还可以使用 ufw 来设置 NAT。

使用 iptables

安装 iptables 包。使用 iptables 启用 NAT

# iptables -t nat -A POSTROUTING -o internet0 -j MASQUERADE
# iptables -A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
# iptables -A FORWARD -i net0 -o internet0 -j ACCEPT
注意 当然,这对于移动宽带连接(在路由 PC 上通常称为 ppp0)也有效。

如果你安装了 docker,请使用 -I DOCKER-USER 而不是 -A FORWARD[4]

# iptables -t nat -A POSTROUTING -o internet0 -j MASQUERADE
# iptables -I DOCKER-USER 1 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
# iptables -I DOCKER-USER 2 -i net0 -o internet0 -j ACCEPT

如果通过 PPPoE 连接,请将 mss 限制为 pmtu 以防止分片

# iptables -t mangle -A FORWARD -o ppp0 -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu

有关更多信息,请阅读 iptables 文章(尤其是保存规则和在启动时自动应用它)。还有一个关于 iptables 的优秀指南 简单的状态防火墙

使用 nftables

安装 nftables 包。使用 nftables 启用 NAT,你需要在新/现有表中创建 postrouting

# nft add table inet nat
# nft add chain inet nat postrouting '{ type nat hook postrouting priority srcnat ; }'

之后,你需要为 internet0 上的 net0 地址进行伪装

# nft add rule inet nat postrouting oifname internet0 masquerade

许多防火墙配置,例如 默认的 /etc/nftables.conf,将 'filter' 表的 'forward' 链的默认策略设置为 'drop'。在这种情况下,你需要规则来允许转发 NAT 流量

# nft add rule inet filter forward ct state related,established accept
# nft add rule inet filter forward iifname net0 oifname internet0 accept
提示 当使用 systemd-networkd 时,使用动态命名集 (NFTSet=) 来避免在 nftables 规则中硬编码接口名称。

你可以在 nftables Wiki 中找到更多关于 nftables 中 NAT 的信息。如果你想使这些更改永久生效,请遵循 nftables 上的说明。

使用 firewalld

安装 firewalld 包。 firewalld 是一个防火墙守护进程,它依赖于 nftablesiptables。首先更改网络接口的 firewalld 区域

# firewall-cmd --zone=external --change-interface=internet0 --permanent
# firewall-cmd --zone=internal --change-interface=net0 --permanent

然后添加一个新的策略,让流量在内部和外部区域之间流动

# firewall-cmd --permanent --new-policy int2ext
# firewall-cmd --permanent --policy int2ext --add-ingress-zone internal
# firewall-cmd --permanent --policy int2ext --add-egress-zone external
# firewall-cmd --permanent --policy int2ext --set-target ACCEPT
# firewall-cmd --reload
提示 你可以使用比纯粹 ACCEPT 更严格的策略规则,正如 firewalld 概念页面[5]Firewall Rules 部分所示。

例如,要只允许 192.168.2.0/24 中的节点访问互联网,请执行

firewall-cmd --permanent --policy int2ext --add-rich-rule='rule family=ipv4 source address=192.168.2.0/24 accept' 之后不要忘记重新加载规则

firewall-cmd --reload

为客户端 PC 分配 IP 地址

如果你计划让多台机器定期使用此机器共享的互联网,那么安装一个DHCP服务器是个好主意。有关可用选项,请参阅Router#DNS 和 DHCP。然后在每台客户端 PC 上配置一个 DHCP 客户端,请参阅网络配置#网络管理器

本文或本节需要在语言、wiki 语法或风格方面进行改进。请参阅 Help:Style 获取参考。

原因:这不是一个 iptables 指南。使用 iptables -I 扩展链可能会跳过其他重要规则;如果你需要脚本化一个 ON/OFF 开关,请使用自定义链,并小心地将其放在 INPUT 链中。(在 Talk:Internet sharing 中讨论)

必须允许 UDP 端口 67 的入站连接以供 DHCP 服务器使用。还需要允许 UDP/TCP 端口 53 的入站连接以供 DNS 请求使用。

# iptables -I INPUT -p udp --dport 67 -i net0 -j ACCEPT
# iptables -I INPUT -p udp --dport 53 -s 192.168.123.0/24 -j ACCEPT
# iptables -I INPUT -p tcp --dport 53 -s 192.168.123.0/24 -j ACCEPT

或者使用 firewalld

# firewall-cmd --zone=internal --permanent --add-service dns
# firewall-cmd --zone=internal --permanent --add-service dhcp
# firewall-cmd --zone=internal --permanent --add-service dhcpv6

如果你不打算定期使用此设置,则可以改为手动为每个客户端添加 IP。

手动添加 IP

除了使用 DHCP 之外,还可以手动配置静态 IP 地址和通过 192.168.123.100 的默认路由。有许多工具可用于相应地配置网络。一个突出的例子是 ip(8),请参阅 网络配置#网络管理。或者,可以使用 .network 文件,请参阅 systemd-networkd#使用静态 IP 的有线适配器 来设置静态 IP。

为每个客户端配置 DNS 服务器,有关详细信息,请参阅 域名解析

就这样。客户端 PC 现在应该可以访问互联网了。

故障排除

如果你能够连接两台 PC 但无法发送数据(例如,如果客户端 PC 向服务器 PC 发出 DHCP 请求,服务器 PC 收到请求并向客户端提供 IP,但客户端不接受,而是超时),请检查你是否没有其他 iptables 规则 干扰

客户端无法访问互联网或无法连接

症状可能还包括:ping 主机时客户端显示 host is down,ping 外部 LAN 设备(应由 NAT 转发)时显示 no route to hostDestination Host Unreachable,DHCP 响应未跨越桥接接口,等等。

已知 docker 可能导致这些问题。只需禁用 docker.servicedocker.socket 即可解决此问题。

docker github issue.

已连接的第二台 PC 无法使用桥接 LAN

第一台 PC 有两个 LAN。第二台 PC 有一个 LAN 并连接到第一台 PC。假设第二台 PC 在桥接接口后将所有访问权限给予 LAN

本文章或章节需要扩充。

原因:解释这些设置实际上做了什么。(在 Talk:Internet sharing 中讨论)
# sysctl net.bridge.bridge-nf-filter-pppoe-tagged=0
# sysctl net.bridge.bridge-nf-filter-vlan-tagged=0
# sysctl net.bridge.bridge-nf-call-ip6tables=0
# sysctl net.bridge.bridge-nf-call-iptables=0
# sysctl net.bridge.bridge-nf-call-arptables=0

参见