最後一個網絡lab是實現一個簡單的VPN。其實我更覺得這是一個典型的Client-sever的程序。因爲收到一個需要轉發的包後,將網絡層及以上的協議棧內容當作數據(我使用的數據包socket,即爲UDP協議的payload),原封不動地轉發出去,內核會根據目的IP地址自動確定路由規則。收到一個需要分發給客戶端的包後,去除前面所說的UDP及以下的協議棧(UDP、IP、ETH),即,將UDP的payload當作鏈路層的負載,直接發送,因爲UDP的payload本來就有IP層,只差一層鏈路層了,這時使用RAW IP發送再合適不過。
也就是說,總共用到3個socket,收包用ETH_P_IP參數,發包分別用數據報socket和RAW IP:
// open sockets, arguments counts void opensocket() { // receive all IP packets as low level frame if ((recvfd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_IP))) == -1) { errExit("open recvfd socket"); } // distribute raw IP packets, we don't care link layer if ((distributefd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) == -1) { errExit("open distributefd socket"); } // simply forward packet, we don't care link layer and IP layer if ((forwardfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { errExit("open forwardfd socket"); } // bind forwardfd to our port, tell host we are here, and prevent later vpn process running struct sockaddr_in addr; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(vpnport); if (bind(forwardfd, (struct sockaddr *)&addr, sizeof(addr)) == -1) { errExit("bind"); } }
轉發流程圖如下:
received packet
+--------------+ by adding an offset to
| | throw out link layer data
| Link layer | (because it is no need)
| |
+--------------+ if it is from PC1 +---------------
| | then forward it | |
| Network Layer| +---------------> | Network Layer|
| | | |
+--------------+ +--------------+
| | | |
| ... | | ... |
| | | |
+--------------+ +--------------+
+-----------------+ +--+
| | |
| Link layer | |
| | |
+-----------------+ |
| | |
| Network Layer | | by kernel
| | |
+-----------------+ |
| | |
| Transport layer | |
| | +--+
+--------------- +-----------------+
| | then see it as payload of UDP, | | +--+
| Network Layer| (wrap link layer, network layer, | Network Layer | |
| | transport layer by kernel) | | |
+--------------+ +--------------------------------> +-----------------+ | payload
| | | | |
| ... | | ... | |
| | | | |
+--------------+ +-----------------+ +--+
分發流程圖:
+-----------------+ +--+
| | |
| Link layer | |
| | |
+-----------------+ |
| | |
| Network Layer | | no use
| | |
+-----------------+ |
| | |
| Transport layer | |
| | +--+
+-----------------+ +---------------
| | +--+ by adding an offset to throw away | |
| Network Layer | | no use data, it is wrapped by | Network Layer|
| | | kernel since we use UDP | |
+-----------------+ | data we want +--------------------------------> +--------------+
| | | | |
| ... | | | ... |
| | | | |
+-----------------+ +--+ +--------------+
+-----------------+ +--+
| | | added by kernel
| Link layer | |
| | +--+
+--------------- +-----------------+
| | distribute it use RAW IP socket | | +--+
| Network Layer| kernel will add link layer | Network Layer | |
| | | | |
+--------------+ +-------------------------------> +-----------------+ | now we successfully
| | | | | forward PC1's data to PC2
| ... | | ... | |
| | | | |
+--------------+ +-----------------+ +--+