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);

转载于:https://www.cnblogs.com/cklxmu/p/3976422.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值