icmp 流量抓取 转发 代理(2)

摘要:
从客户端C到服务器S的icmp数据包在通过本地P时被拦截。在上一篇文章中,已经描述了如何获得原始目的地地址。您必须将数据转发到原始目标地址S,并在收到来自原始目标地址的响应后将其转发给客户端。无法使用IP,因为使用了rawsocket_ TRANSPARENT的套接字选项用于绑定非本地地址,因此使用了IP _ HDRINCL套接字选项。手动填写IP标头,并将源地址设置为S,将目标地址设置为C.socklen_tsender_len;结构消息hdrmsg;结构向量;结构sockaddr_insender_addr;sender_len=大小;charbuf[2048]={0};整数=0;charcmsg_buf[2048]={0};消息。消息_名称=发件人地址;消息。msg_namelen=发送方len;消息。消息_ iov=&iov;消息。消息_ iovlen=1;消息。msg_ iov-˃iov_base=buf;消息。消息_ iov-˃iov_len=2048;消息。消息_控制=cmsg_ buf;消息。消息_控制器=2048;消息。消息_标志=0;//从客户端接收icmp数据包len=recvmsg;如果{perror;}elseif{printf;}否则{printf;print_data;structcmsghdr*cmsg=NULL;for(cmsg=cmsg_FIRSTHDR(&msg);cmsg!
客户端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 ");
    } else {
        printf("Read from Client: len [%d] --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 ", 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
        }
        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);


        // 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 ");
        sendto(sockfd, icmp, len, 0, (struct sockaddr *)&orig_addr, sizeof(struct sockaddr));
        //从服务端接收icmp响应
        printf("recvfrom server ");
        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 ");
        sendto(sock_fd, buf, len, 0, (struct sockaddr *)&sender_addr, sizeof(struct sockaddr));
        close(sock_fd);

免责声明:文章转载自《icmp 流量抓取 转发 代理(2)》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇阿里云ecs下使用upupw的全功能版Kangle虚拟主机实现https[转]如何利用ndk-stack工具查看so库的调用堆栈【代码示例】?下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Linux基础(15)多线程编程

Linux的内核中没有thread的概念,线程是第三方库libpthread实现的, 和vfork(轻量级进程,只有部分copy)有点像(进程的创建fork会完全copy主进程资源 ,而线程会共享资源,子线程创建新资源时其作用域只在当前子线程,而子线程非新新创建的资源会和创建前的主线程共享这些资源) , 线程和进程的创建在内核里都是系统调用copy_proc...

将一个数的字节顺序逆置

将一个数的字节顺序逆置 1 #include<stdio.h> 2 #include<stdlib.h> 3 4 int main() 5 { 6 int ival;//逆置的数 7 int res;//存储逆置后的数 8 9 while(fflush(stdin),printf("En...

pcre 使用

1、主页地址:http://www.pcre.org/下载pcre-7.8.tar.bz22、解压缩:tar xjpf pcre-7.8.tar.bz23、配置:cd pcre-7.8./configure --prefix=/usr/local/pcre-7.8 --libdir=/usr/local/lib/pcre --includedir=/usr...

Linux内核结构体--kfifo 环状缓冲区

转载链接:http://blog.csdn.net/yusiguyuan/article/details/41985907 1、前言   最近项目中用到一个环形缓冲区(ring buffer),代码是由Linux内核的kfifo改过来的。缓冲区在文件系统中经常用到,通过缓冲区缓解cpu读写内存和读写磁盘的速度。例如一个进程A产生数据发给另外一个进程B,进程...

linux系统socket通信编程1

Linux下的Socket编程大体上包括Tcp Socket、Udp Socket即Raw Socket这三种,其中TCP和UDP方式的Socket编程用于编写应用层的socket程序,是我们用得比较多的,而Raw Socket则用得相对较少,不在本文介绍范围之列。 TCP Socket 基于TCP协议的客户端/服务器程序的一般流程一般如下: 它基本上可...

libpcap编程实例

1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <pcap.h> 4 #include <errno.h> 5 #include <sys/socket.h> 6 #include <netinet/in...