Linux的Netfilter鉤子點的位置會導致一些奇怪的問題,比如本機發出的包無法使用基於mark的策略路由,這是因為mark一般是在Netfilter中進行的,而Linux的路由處在OUTPUT鉤子點之前,因此這是一個順序倒置的問題,如何來解決呢?只能在路由之前打上mark,而我們知道,對於外部進入的包,mark是在PREROUTING進行的,因此對於外部進入的包,策略路由是好使的,對於本機發出的包,路由之前只能是socket層了,那為何不能在傳輸層做呢?因為一來傳輸層比較雜,二來很多協議直接走到IP層,比如OSPF之類的,三來很多傳輸層協議也需要路由查找,比如TCP在connect的時候就需要查找路由以確定源IP地址(如果沒有bind的話)。
幸運的是,Linux的socket支持SO_MARK這樣一個option,可以很方便的使用:
mark = 100;
setsockopt(client_socket, SOL_SOCKET, SO_MARK, &mark, sizeof(mark));
1.對TRACK的影響
雖然raw表是數據包經過的第一個表,使用SO_MARK仍然可以在整個raw表起作用之前做點mark,從而使得一個特定socket發出的包統統NOTRACK:
iptables -t raw -A OUTPUT -m mark --mark 100 -j NOTRACK
如果不這樣的話,就需要:
iptables -t raw -A OUTPUT [-s xxxx] [-d yyyy] [-p tcp|udp [--sport X] [--dport Y] ... -j MARK --set-mark 100
...[一大堆和上面類似的規則]
iptables -t raw -A OUTPUT -m mark --mark 100 -j NOTRACK
正如在PREROUTING上的raw表需要做的那樣。我們得意於OUTPUT上面是socket,是應用程序的世界,而PREROUTING以下則是內核以及驅動的世界了,後者太雜太亂,不便做更多的事,而前者則是我們可以掌控的范圍。
2.對策略路由的影響
SO_MARK最大的受益者就是策略路由了,如果我們這是以下的路由:
ip rule add fwmark 100 table abc
ip route add 1.2.3.4/32 via 192.168.0.254 table abc
ip route del 1.2.3.4/32 table main
不設置SO_MARK的情況下,所有的訪問1.2.3.4的流量將因為沒有路由而被丟棄,因為在進入PREROUTING前首先要查找路由,而此時還沒有打上mark,因而不會匹配到abc策略表中的那條路由,而main表中的對應路由我們已經刪除了...但是如果我們在應用程序中加入:
mark = 100;
setsockopt(client_socket, SOL_SOCKET, SO_MARK, &mark, sizeof(mark));
訪問就可以正常了,因為在查找路由的時候,已經有這個mark了。