生活随笔
收集整理的這篇文章主要介紹了
实现socket监听所有网络命名空间
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
當前Linux內核的實現,一個socket監聽在一個特定的網絡命名空間中,不同的命名空間可有具有相同的socket,即可監聽相同的地址端口,這樣很好的實現網絡隔離虛擬化的功能。但是對于網路設備來說,并不需要如此完全的隔離。比如VPN設備/路由器等,一個ike進程或者quagga進程能監聽在所有的命名空間,更利于實現和管理。
這樣就要求應用層socket能夠接收到所有命名空間的數據包,并且能夠感知當前連接的命名空間。
數據包接收
內核默認創建一個網絡命名空間(init_net),起初所有的socket都監聽在默認的網絡命名空間。要能夠接收其它命名空間的數據包,需要修改sock查找函數。在一個新的連接請求進來之后,查找監聽sock時(__inet_lookup_listener),內核默認僅在接收數據包所在的命名空間查找。修改為在找不到的情況下,去init_net命名空間查找,此時,監聽在init_net的socket就能接收到新的連接了。
sk = __inet_lookup_listener(dev_net(dev), hashinfo, saddr, sport, daddr, hnum, dif, flags);
if (!sk) {sk = __inet_lookup_listener(&init_net, hashinfo, saddr, sport, daddr, hnum, dif, flags);
}
以上是TCP的sock查找修改,對于UDP可做相同的修改。
數據包發送
sock的查找修改之后已經可以接收到新的連接請求,但是并沒有修改sock結構中的sk_net的值,其還是init_net(socket總監聽在此命名空間),不能使用其查找路由。如要能正常回復此連接請求(SYN+ACK),我們的sock需要使用接收到數據包的接口所在net_namespace的路由。所以在請求路由時,使用接收命名空間查找:
static inline struct net *sock_net(const struct sock *sk)
{return read_pnet(&sk->sk_net);
}
struct dst_entry *inet_csk_route_req(struct sock *sk, struct flowi4 *fl4, const struct request_sock *req)
{rt = ip_route_output_flow(skb_real_net, fl4, sk);
}
發送連接建立之后正常的數據包涉及到的也是路由問題,如何告訴內核代碼要在哪個命名空間發送?此時需要在創建子sock的時候,把真正的接收命名空間保存在子sock中。在發送時使用。例如ip_queue_xmit函數,查詢路由時使用真正的child_sk_real_net去查:
int ip_queue_xmit(struct sk_buff *skb, struct flowi *fl)
{rt = ip_route_output_ports(child_sk_real_net, fl4, sk, daddr, inet->inet_saddr,inet->inet_dport, inet->inet_sport, sk->sk_protocol, RT_CONN_FLAGS(sk), sk->sk_bound_dev_if);
}
方能找到正確的出口路由,正常發送數據包。實現應用層socket監聽多個網絡命名空間。
總結
以上是生活随笔為你收集整理的实现socket监听所有网络命名空间的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。