Netfilter

Netfilter

Netfilter 是 Linux 内核网络协议栈提供的网络管理框架。其功能非常强大,能够对流经内核网络栈的数据包做任何修改,可以轻而易举的实现防火墙(Firewall)、NAT、策略路由(Policy Based Routing)等功能。Netfilter 可以通过用户态的 iptables 工具管理。

mark

mark 用于对经过本机网络栈数据包作标记,可以配合策略路由等。mark 是一个 int 型数字(对于32位系统即32位数字,64位系统则为 64位数字。默认值 0),可以独立设置或测试匹配 mark 的每一个 bit 位;按照惯例,通常在 mangle 表里设置(修改)mark。

如果需要对 OUTPUT (本机发出的) 数据包都设置 mark,在 -t mangle OUTPUT 链里设置规则。
如果需要对 FORWARD (本机接收并转发的) 数据包设置 mark,在 -t mangle PREROUTING 链里设置规则(注意在 -t mangle FORWARD 里设置无效!)。

# 对所有 dst IP 属于 "chinaip" 这个 ipset 的数据包打上 mark bit 0
# 然后对所有 mark bit 0  == 1 的数据包做策略路由,使其走系统默认路由

ipset create china hash:net
ipset add china 1.0.1.0/24
#... add more China IP range entry to IP set

# mark format: VALUE/MASK
iptables -t mangle -A PREROUTING -m set --match-set china dst -j MARK --set-mark 0x1/0x1
iptables -t mangle -A OUTPUT -m set --match-set china dst -j MARK --set-mark 0x1/0x1
ip rule add fwmark 0x1/0x1 lookup main prio 1

参考:中国 IP

Traversing of packets

网络数据包通过 iptables tables / chains 的流程: Docs

Receive:

某个interface收到数据包 -> PREROUTING (manage, nat) -> route ->
    是发送给本机的数据包? -> INPUT (manage, filter) -> app
    不是 -> FORWARD (manage, filter) -> POSTROUTING (manage, nat) -> 某个interface发出

Send:

app发送数据包 -> OUTPUT (manage, nat, filter) -> route -> POSTROUTING (manage, nat) -> 某个interface发出

Flow graph

完整流程图

Tips

  • 上面的图中画出了两个 "route decision" 阶段,但对于 receive / send 而言,其各自实际上应该主要使用了1个 route 阶段: receive 的 route 在 PREROUTING 之后;send 的 route 在 OUTPUT 之后。
  • Receive时判断是否是发送给本机的数据包的方法是:收到数据包的destination IP是否与本机某个interface的IP相同。
  • Send的OUTPUT chain除了manage和filter以外nat表里也有。nat的OUTPUT位于filter的之前,用于对从本机(app)发出的(而不是收到并FORWARD的)请求做DNAT或REDIRECT。
  • Send的route阶段确定了数据包的source ip和source port。source ip和source port由app发送数据包时决定。如果app没有bind某个interface并且没有设置source ip/port,则source ip时为route使用的interface ip。
  • filter表的INPUT, OUTPUT和FORWARD用来过滤数据包 -j ACCEPT /DROP
  • nat表的PREROUTING / OUTPUT用来做DNAT(或REDIRECT), POSTROUTING用来做SNAT(或MASQUERADE)
  • FORWARD (以及所有 FORWARD 之后的 chain) 需要设置内核参数 "net.ipv4.ip_forward=1"。(否则内核网络栈会直接丢弃网卡收到的 dst 非本机的数据包)
  • nat 表的 PREROUTING / POSTROUTING 链有些时候会被跳过。主要是指对于有连接的会话 (conn),只有初始发送的数据包会经过 PREROUTING / POSTROUTING,之后发送的数据包以及收到的来自对方的数据包都会根据内核维护的 conn 连接表而被直接处理。
  • conntrack (Connection tracking) 工作在 PREROUTING / OUTPUT 链(分别对于收到的/本机发出的数据包)。

bind

socket 可以 bind 某个 src IP。

#include <sys/socket.h>

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

注意 bind 某个 interface 并不会使"发送数据包"跳过 route decision 阶段,bind 的作用仅仅是指定发送数据包时的 src IP。

SO_BINDTODEVICE

另一个容易与 "bind" 混淆的是 setsockopt 的 SO_BINDTODEVICE 选项:

#include <sys/socket.h>

setsockopt(sock, SO_BINDTODEVICE, "eth0");

SO_BINDTODEVICE 仅对 AF_INET (即 Internet 数据包)有效。其作用是:

If a socket is bound to an interface, only packets received from that particular interface are processed by the socket.

参考

Source Address Selection

本机发送的数据包源 IP 选择:

http://linux-ip.net/html/routing-saddr-selection.html

  1. The application is already using the socket, in which case, the source address has been chosen. Also, the application can specifically request a particular address (not necessarily a locally hosted IP) using the bind call.

  2. The kernel performs a route lookup and finds an outbound route for the destination. If the route contains the src parameter, the kernel selects this IP address for the outbound packet. Otherwise, the kernel will choose the first address configured on the interface which falls in the same network as the destination address or the nexthop router.


Last update: 2018-06-29 09:01:17 UTC