icmp 流量抓取 转发 代理(2)
客户端C到服务器S的icmp包经过本机P时被截获,在上一篇中已经介绍了如何获取原始目的地址,你必须将数据转发到原始目的地址S,并且在收到从原始目的地址的响应之后转发给客户端。此时,要实现透明代理,则你返回给客户端的icmp响应的源地址必须为客户端请求的原始目的地址S。由于使用的是raw socket,无法用IP_TRANSPARENT的socket选项绑定非本机地址的方法(bind会报错:提示无法绑定这个地址),因此使用IP_HDRINCL的socket选项,手动填充IP头将源地址设为S,目的地址设为C(其实就是将原来从客户端收到的icmp包的两个地址对调)。
socklen_t sender_len; struct msghdr msg; struct iovec iov; struct sockaddr_in sender_addr; sender_len = sizeof(sender_addr); char buf[2048] = {0}; int len = 0; char cmsg_buf[2048] = {0}; msg.msg_name = &sender_addr; msg.msg_namelen = sender_len; msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_iov->iov_base = buf; msg.msg_iov->iov_len = 2048; msg.msg_control = cmsg_buf; msg.msg_controllen = 2048; msg.msg_flags = 0; //从客户端接收icmp包 len = recvmsg(sockfd,&msg,0); if (len == -1) { perror("recvmsg()"); } else if (len == 0) { printf("Connection Closed\n"); } else { printf("Read from Client: len [%d]\n --content:", len); print_data(buf,len); struct cmsghdr * cmsg = NULL; for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) { // ignore the control headers that don't match what we want if (cmsg->cmsg_level != IPPROTO_IP || cmsg->cmsg_type != IP_PKTINFO) { continue; } struct in_pktinfo *pi = CMSG_DATA(cmsg); // printf("ipi_spec_dst:%s\n", inet_ntoa(pi->ipi_spec_dst)); // printf("ipi_addr:%s\n", inet_ntoa(pi->ipi_addr)); char sender_ip[32] = {0}; int sender_port = 0; transfer_sock_addr(&sender_addr, sender_ip, 32, &sender_port); printf("source ip:%s port:%d\n",sender_ip,sender_port); // at this point, peeraddr is the source sockaddr // pi->ipi_spec_dst is the destination in_addr // pi->ipi_addr is the receiving interface in_addr } char orig_ip[32] = {0}; int orig_port = 0; struct sockaddr_in *orig_addr; for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg,cmsg)) { if (cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_ORIGDSTADDR) { orig_addr = (struct sockaddr_in *) CMSG_DATA(cmsg); transfer_sock_addr(orig_addr, orig_ip, 32, &orig_port); break; } } if (cmsg == NULL) { printf("IP_ORIGDSTADDR not enabled or small buffer or I/O error"); return; } printf("original destination ip:%s - port:%d\n", orig_ip, orig_port); // struct sockaddr_in sin; // memset(&sin, 0, sizeof(sin)); // sin.sin_family = AF_INET; // sin.sin_addr.s_addr = inet_addr("192.168.128.2"); // sin.sin_port = htons(orig_port); int i; int iphdrlen; //ip头长度 struct ip *ip; struct icmp *icmp; ip = (struct ip *)buf; iphdrlen = ip->ip_hl << 2; //求IP报文头长度,即IP报头长度乘4 icmp = (struct icmp *)(buf + iphdrlen); //越过IP头,指向ICMP报头 len -= iphdrlen; //转发到服务端 printf("sendto server\n"); sendto(sockfd, icmp, len, 0, (struct sockaddr *)&orig_addr, sizeof(struct sockaddr)); //从服务端接收icmp响应 printf("recvfrom server\n"); if((len = recvfrom(sockfd,buf,2048,0, (struct sockaddr *)&from,&fromlen)) < 0) { perror("recvfrom error"); return -1; } int sock_fd; int flag = 1; if ((sock_fd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) { perror("socket()"); return -1; } if (setsockopt(sock_fd, IPPROTO_IP, IP_HDRINCL, &flag, sizeof(int)) < 0) { perror("setsockopt() - IP_HDRINCL"); return -1; } if(connect(sock_fd, &sin, sizeof(struct sockaddr_in))<0) { close(sockfd); perror("connect error"); return -1; } ip = (struct ip *)buf; struct in_addr ip_src, ip_dst; ip_src.s_addr = inet_addr(orig_ip); ip_dst.s_addr = sender_addr.sin_addr.s_addr; ip->ip_src = ip_src; ip->ip_dst = ip_dst; iphdrlen = ip->ip_hl << 2; //求IP报文头长度,即IP报头长度乘4 //icmp = (struct icmp *)(buf + iphdrlen); //越过IP头,指向ICMP报头 //len -= iphdrlen; //将响应发给客户端 printf("sendto client\n"); sendto(sock_fd, buf, len, 0, (struct sockaddr *)&sender_addr, sizeof(struct sockaddr)); close(sock_fd); |