icmp 流量抓取 转发 代理
通过以下步骤,将经过本机的(设置本机为网关)icmp流量抓取,并获取其目标地址,可以用来做icmp代理。
1. iptables 在 mangle 表的 prerouting链中添加规则,给icmp包做标记,比如0x15
iptables -t mangle -A PREROUTING -p icmp -j MARK --set-mark 0x15 |
2. ip rule 添加规则:标记0x15的包查询路由表100
ip route 添加规则:将目的地址为0.0.0.0/0(即所有目的地址)的包通过回环发给本机
ip rule add fwmark 0x15 lookup 100 ip route add local 0.0.0.0/0 dev lo table 100 |
3. 代码中使用raw socket 监听icmp的数据包
//ICMP协议 if((protocol = getprotobyname("icmp")) == NULL) { perror("getprotobyname"); exit(1); } //raw socket监听icmp协议,需要root权限 if((sockfd = socket(AF_INET,SOCK_RAW,protocol->p_proto)) < 0) { perror("socket error"); exit(1); } //回收root权限 setuid(getuid()); |
3.1 setsockopt选项 IP_RECVORIGDSTADDR或者IP_PKTINFO
int opt = 1;
setsockopt(sockfd,IPPROTO_IP, IP_PKTINFO, &opt, sizeof(opt));
setsockopt(sockfd, SOL_IP, IP_RECVORIGDSTADDR, &opt, sizeof(opt));
|
3.2 使用recvmsg,遍历msg的cmsghdr,选取特定的cmsg
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; 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); //方法1:使用IP_PKTINFO 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 } //方法2:使用IP_RECVORIGDSTADDR 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); |