最后一个网络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
| ... | | ... | |
| | | | |
+--------------+ +-----------------+ +--+