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 ");
    } else {
        printf("Read from Client: len [%d] --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 ", inet_ntoa(pi->ipi_spec_dst));
            printf("ipi_addr:%s ", 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 ",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 ", orig_ip, orig_port);

 
部分代码参考:http://www.oschina.net/code/snippet_80184_1511
原文地址:https://www.cnblogs.com/cklxmu/p/3965063.html