• socket原始套接字编程


    一、原始套接字概述
    协议栈的原始套接字从实现上可以分为“链路层原始套接字”和“网络层原始套接字”两大类。
    链路层原始套接字可以直接用于接收和发送链路层的MAC帧,在发送时需要由调用者自行构造和封装MAC首部。
    网络层原始套接字可以直接用于接收和发送IP层的报文数据,在发送时需要自行构造IP报文头(取决是否设置IP_HDRINCL选项)。

    原始套接字(SOCK_RAW)可以用来自行组装数据包,可以接收本机网卡上所有的数据帧(数据包),对于监听网络流量和分析网络数据很有作用。
    原始套接字是基于IP 数据包的编程(SOCK_PACKET 是基于数据链路层的编程)。另外,必须在管理员权限下才能使用原始套接字。
    原始套接字(SOCK_RAW)与标准套接字(SOCK_STREAM、SOCK_DGRAM)的区别在于原始套接字直接置“根”于操作系统网络核心(Network Core),

    而 SOCK_STREAM、SOCK_DGRAM 则“悬浮”于 TCP 和 UDP 协议的外围。
    流式套接字只能收发TCP协议的数据,数据报套接字只能收发UDP协议的数据,原始套接字可以收发没经过内核协议栈的数据包。
    原始套接字,指在传输层下面使用的套接字。流式套接字和数据报套接字这两种套接字工作在传输层,主要为应用层的应用程序提供服务,

    并且在接收和发送时只能操作数据部分,而不能对IP首部或TCP和UDP首部进行操作,通常把这两种套接字称为标准套接字。
    但是,如果我们开发的是更底层的应用,比如发送一个自定义的IP包、UDP包、TCP包或ICMP包,捕获所有经过本机网卡的数据包,

    伪装本机的IP,想要操作IP首部或传输层协议首部,等等,这些功能对于这两种套接字就无能为力了。

    这些功能需要使用另一种套接字来实现,这种套接字叫作原始套接字,功能更强大,更底层。

    原始套接字可作的功能:

    •   发送一个自定义的IP 包;
    •   发送一个ICMP 协议包;
    •   分析所有经过网络的包,而不管这样包是否是发给自己的;
    •   伪装本地的IP 地址;

    1、 链路层原始套接字
    链路层原始套接字调用socket()函数创建。

    第一个参数指定协议族类型为PF_PACKET,第二个参数type可以设置为SOCK_RAW或SOCK_DGRAM,第三个参数是协议类型(该参数只对报文接收有意义)。

    socket(PF_PACKET, type, htons(protocol))

    a) 参数type设置为SOCK_RAW时,套接字接收和发送的数据都是从MAC首部开始的。

    在发送时需要由调用者从MAC首部开始构造和封装报文数据。type设置为SOCK_RAW的情况应用是比较多的,因为某些项目会使用到自定义的二层报文类型。

    socket(PF_PACKET, SOCK_RAW, htons(protocol))

    b) 参数type设置为SOCK_DGRAM时,套接字接收到的数据报文会将MAC首部去掉。同时在发送时也不需要再手动构造MAC首部,

    只需要从IP首部(或ARP首部,取决于封装的报文类型)开始构造即可,而MAC首部的填充由内核实现的。若对于MAC首部不关心的场景,可以使用这种类型,这种用法用得比较少。

    socket(PF_PACKET, SOCK_DGRAM, htons(protocol))

    2、网络层原始套接字
    创建面向连接的TCP和创建面向无连接的UDP套接字,在接收和发送时只能操作数据部分,而不能对IP首部或TCP和UDP首部进行操作。

    如果想要操作IP首部或传输层协议首部,就需要调用如下socket()函数创建网络层原始套接字。

    第一个参数指定协议族的类型为PF_INET,第二个参数为SOCK_RAW,第三个参数protocol为协议类型。产品线有使用OSPF和RSVP等协议,需要使用这种类型的套接字。

        socktet(PF_INET, SOCK_RAW, protocol)

    a) 接收报文
      网络层原始套接字接收到的报文数据是从IP首部开始的,即接收到的数据包含了IP首部, TCP/UDP/ICMP等首部, 以及数据部分。     
    b) 发送报文
      网络层原始套接字发送的报文数据,在默认情况下是从IP首部之后开始的,即需要由调用者自行构造和封装TCP/UDP等协议首部。
    这种套接字也提供了发送时从IP首部开始构造数据的功能,通过setsockopt()给套接字设置上IP_HDRINCL选项,就需要在发送时自行构造IP首部。

    原始套接字的创建

    int socket ( int family, int type, int protocol );

    参数:
      family:协议族 这里写 PF_PACKET
      type: 套接字类,这里写 SOCK_RAW
      protocol:协议类别,指定可以接收或发送的数据包类型,不能写 “0”,取值如下,注意,传参时需要用 htons() 进行字节序转换。

    ETH_P_IP:IPV4数据包
    ETH_P_ARP:ARP数据包
    ETH_P_ALL:任何协议类型的数据包

    返回值:

      成功( >0 ):套接字,这里为链路层的套接字
      失败( <0 ):出错

    二、socket之ioctl
    1、struct ifreq结构体
    ifreq结构定义在/usr/include/net/if.h,用来配置ip地址,激活接口,配置MTU等接口信息的。

    其中包含了一个接口的名字和具体内容——(是个共用体,有可能是IP地址,广播地址,子网掩码,MAC号,MTU或其他内容)。

    ifreq包含在ifconf结构中。而ifconf结构通常是用来保存所有接口的信息的。

    // if.h
    /*
     * Interface request structure used for socket
     * ioctl's.  All interface ioctl's must have parameter
     * definitions which begin with ifr_name.  The
     * remainder may be interface specific.
     */
    struct ifreq {
    #define IFHWADDRLEN    6
        union
        {
            char    ifrn_name[IFNAMSIZ];        /* if name, e.g. "en0" */
        } ifr_ifrn;
        
        union {
            struct    sockaddr ifru_addr;
            struct    sockaddr ifru_dstaddr;
            struct    sockaddr ifru_broadaddr;
            struct    sockaddr ifru_netmask;
            struct  sockaddr ifru_hwaddr;
            short    ifru_flags;
            int    ifru_ivalue;
            int    ifru_mtu;
            struct  ifmap ifru_map;
            char    ifru_slave[IFNAMSIZ];    /* Just fits the size */
            char    ifru_newname[IFNAMSIZ];
            void __user *    ifru_data;
            struct    if_settings ifru_settings;
        } ifr_ifru;
    };
    #define ifr_name    ifr_ifrn.ifrn_name    /* interface name     */
    #define ifr_hwaddr    ifr_ifru.ifru_hwaddr    /* MAC address         */
    #define    ifr_addr    ifr_ifru.ifru_addr    /* address        */
    #define    ifr_dstaddr    ifr_ifru.ifru_dstaddr    /* other end of p-p lnk    */
    #define    ifr_broadaddr    ifr_ifru.ifru_broadaddr    /* broadcast address    */
    #define    ifr_netmask    ifr_ifru.ifru_netmask    /* interface net mask    */
    #define    ifr_flags    ifr_ifru.ifru_flags    /* flags        */
    #define    ifr_metric    ifr_ifru.ifru_ivalue    /* metric        */
    #define    ifr_mtu        ifr_ifru.ifru_mtu    /* mtu            */
    #define ifr_map        ifr_ifru.ifru_map    /* device map        */
    #define ifr_slave    ifr_ifru.ifru_slave    /* slave device        */
    #define    ifr_data    ifr_ifru.ifru_data    /* for use by interface    */
    #define ifr_ifindex    ifr_ifru.ifru_ivalue    /* interface index    */
    #define ifr_bandwidth    ifr_ifru.ifru_ivalue    /* link bandwidth    */
    #define ifr_qlen    ifr_ifru.ifru_ivalue    /* Queue length     */
    #define ifr_newname    ifr_ifru.ifru_newname    /* New name        */
    #define ifr_settings    ifr_ifru.ifru_settings    /* Device/proto settings*/
    // if.h
    /*
     * Structure used in SIOCGIFCONF request.
     * Used to retrieve interface configuration
     * for machine (useful for programs which
     * must know all networks accessible).
     */
    struct ifconf  {
        int    ifc_len;            /* size of buffer    */
        union {
            char __user *ifcu_buf;
            struct ifreq __user *ifcu_req;
        } ifc_ifcu;
    };
    #define    ifc_buf    ifc_ifcu.ifcu_buf        /* buffer address    */
    #define    ifc_req    ifc_ifcu.ifcu_req        /* array of structures    */

    2、用法

    3、获取ip地址举例

    #include <string.h>
    #include <sys/socket.h>
    #include <sys/ioctl.h>
    #include <net/if.h>
    #include <stdio.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    int main()
    {
            int inet_sock;
            struct ifreq ifr;
            inet_sock = socket(AF_INET, SOCK_DGRAM, 0);
     
            strcpy(ifr.ifr_name, "eth0");
            //SIOCGIFADDR标志代表获取接口地址
            if (ioctl(inet_sock, SIOCGIFADDR, &ifr) <  0)
                    perror("ioctl");
            printf("%s
    ", inet_ntoa(((struct sockaddr_in*)&(ifr.ifr_addr))->sin_addr));
            return 0;
    }

    三、原始套接字编程举例

    #include <sys/types.h>
    #include <sys/socket.h>
    #include <linux/if.h>
    #include <linux/if_packet.h>
    #include <linux/if_ether.h>
    #include <netinet/in.h>
    #include <netinet/if_ether.h>
    #include <net/ethernet.h>
    #include <arpa/inet.h>
    #include <sys/ioctl.h>
    #include <stdio.h>
    #include <string.h>
    
    int main()
    {
        int sockfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ARP) );
        if (sockfd == -1)
        {
            printf("socket error
    "); return 0;
        }
    
        //获取网卡信息
        sockaddr_ll addr_ll;
        memset(&addr_ll, 0, sizeof(sockaddr_ll));
        addr_ll.sll_family = PF_PACKET;
    
        ifreq ifr;
        strcpy(ifr.ifr_name, "ens33");
        if (ioctl(sockfd, SIOCGIFINDEX, &ifr) == -1)
        {
            printf("error ioctl SIOCGIFINDEX
    "); return 0;
        }
        addr_ll.sll_ifindex = ifr.ifr_ifindex; //接口索引
    
    
        if (ioctl(sockfd, SIOCGIFADDR, &ifr) == -1)
        {
            printf("error ioctl SIOCGIFADDR
    "); return 0;
        }
        char* ipSrc = inet_ntoa(((struct sockaddr_in*)(&(ifr.ifr_addr)))->sin_addr);
        printf("ip address : %s
    ", ipSrc); //source ip
    
        if (ioctl(sockfd, SIOCGIFHWADDR, &ifr) == -1)
        {
            printf("error ioctl SIOCGIFHWADDR
    "); return 0;
        }
        unsigned char macSrc[ETH_ALEN];
        memcpy(macSrc, ifr.ifr_hwaddr.sa_data, ETH_ALEN); //mac address
        printf("mac address");
        for (int i = 0; i < ETH_ALEN; i++)
            printf(":%02x", macSrc[i]);
        printf("
    ");
    
        //填充以太网首部 和 ARP信息
        unsigned char macDst[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
        ether_header header;
        memcpy(header.ether_dhost, macDst, ETH_ALEN);
        memcpy(header.ether_shost, macSrc, ETH_ALEN);
        header.ether_type = htons(ETHERTYPE_ARP);
    
        ether_arp arp;
        arp.arp_hrd = htons(ARPHRD_ETHER);
        arp.arp_pro = htons(ETHERTYPE_IP);
        arp.arp_hln = ETH_ALEN;
        arp.arp_pln = 4; //IPv4
        arp.arp_op = htons(ARPOP_REQUEST);
    
        in_addr src_in_addr, dst_in_addr;
        inet_pton(AF_INET, ipSrc, &src_in_addr);
        inet_pton(AF_INET, "192.168.182.132", &dst_in_addr);
    
        memcpy(arp.arp_sha, macSrc, ETH_ALEN);
        memcpy(arp.arp_spa, &src_in_addr, 4);
        memcpy(arp.arp_tha, macDst, ETH_ALEN);
        memcpy(arp.arp_tpa, &dst_in_addr, 4);
    
        unsigned char sendBuf[sizeof(ether_header) + sizeof(ether_arp) ];
        memcpy(sendBuf, &header, sizeof(ether_header) );
        memcpy(sendBuf + sizeof(ether_header), &arp, sizeof(ether_arp));
        int len = sendto(sockfd, sendBuf, sizeof(sendBuf), 0, (const sockaddr*)&addr_ll, sizeof(addr_ll) );
        if (len > 0)
        {
            printf("send success
    ");
        }
    
        return 0;
    }

    参考:
    https://blog.csdn.net/zhu114wei/article/details/6927513
    https://www.icode9.com/content-4-401910.html
    https://www.it610.com/article/1297978265012609024.htm
    https://www.bbsmax.com/A/KE5Qg6YkzL/
    https://blog.csdn.net/pashanhu6402/article/details/96428887
    https://blog.csdn.net/panker2008/article/details/46502783
    https://baike.baidu.com/item/%E5%A5%97%E6%8E%A5%E5%AD%97/9637606?fromtitle=socket&fromid=281150&fr=aladdin
    https://www.jianshu.com/p/066d99da7cbd
    https://www.cnblogs.com/huqian23456/archive/2011/02/22/1961822.html
    https://blog.csdn.net/will130/article/details/53326740
    https://blog.51cto.com/mingtangduyao/1721604
    https://blog.csdn.net/giantpoplar/article/details/47657303
    https://blog.csdn.net/ljianhui/article/details/10477427#
    https://www.cnblogs.com/dapaitou2006/p/6502195.html
    https://blog.csdn.net/weixin_43206704/article/details/89327572
    https://blog.csdn.net/weixin_43206704/article/details/89293187
    http://blog.chinaunix.net/uid-27074062-id-3388166.html
    https://blog.csdn.net/aebdm757009/article/details/101498118
    https://tennysonsky.blog.csdn.net/article/details/44676377
    https://blog.csdn.net/q623702748/article/details/52063019
    https://blog.csdn.net/weixin_43288201/article/details/106266418

  • 相关阅读:
    Microsoft.NET Framework 全面开源
    SqlCommandBuilder笔记[原]
    C#几种排序方法的整理[原]
    无聊用js写了一个模拟的购物车[原]
    Visio2005数据库建模视频[原]
    JavaScript读取XML文件[整理]
    IIS中对NetFrameWork进行注册
    时间不等人 创业准备开始了
    用CSS和HTML标签来截取过长的文字部分变成省略号
    C#在SQl中存取图片image[原]
  • 原文地址:https://www.cnblogs.com/ggzhangxiaochao/p/15095242.html
Copyright © 2020-2023  润新知