Linux 原始套接字抓包实例

    学习网络编程,调试各种连接和数据很麻烦,也有很多现成工具可以使用,不过还是喜欢再造个*来自己玩玩.

header.h 定义了IP和TCP包头格式:

Linux 原始套接字抓包实例
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if.h>

/*
    定义TCP和IP的包头格式
*/


/* IP header */
struct ip {
        u_char  ip_vhl;                 /* version << 4 | header length >> 2 */
        u_char  ip_tos;                 /* type of service */
        u_short ip_len;                 /* total length */
        u_short ip_id;                  /* identification */
        u_short ip_off;                 /* fragment offset field */
        #define IP_RF 0x8000            /* reserved fragment flag */
        #define IP_DF 0x4000            /* dont fragment flag */
        #define IP_MF 0x2000            /* more fragments flag */
        #define IP_OFFMASK 0x1fff       /* mask for fragmenting bits */
        u_char  ip_ttl;                 /* time to live */
        u_char  ip_p;                   /* protocol */
        u_short ip_sum;                 /* checksum */
        struct  in_addr ip_src,ip_dst;  /* source and dest address */
};
#define IP_HL(ip)               (((ip)->ip_vhl) & 0x0f)
#define IP_V(ip)                (((ip)->ip_vhl) >> 4)

/* TCP header */
typedef u_int tcp_seq;

struct tcp {
        u_short th_sport;               /* source port */
        u_short th_dport;               /* destination port */
        tcp_seq th_seq;                 /* sequence number */
        tcp_seq th_ack;                 /* acknowledgement number */
        u_char  th_offx2;               /* data offset, rsvd */
#define TH_OFF(th)      (((th)->th_offx2 & 0xf0) >> 4)
        u_char  th_flags;
        #define TH_FIN  0x01
        #define TH_SYN  0x02
        #define TH_RST  0x04
        #define TH_PUSH 0x08
        #define TH_ACK  0x10
        #define TH_URG  0x20
        #define TH_ECE  0x40
        #define TH_CWR  0x80
        #define TH_FLAGS        (TH_FIN|TH_SYN|TH_RST|TH_ACK|TH_URG|TH_ECE|TH_CWR)
        u_short th_win;                 /* window */
        u_short th_sum;                 /* checksum */
        u_short th_urp;                 /* urgent pointer */
};
Linux 原始套接字抓包实例

main.c定义了主要功能:

Linux 原始套接字抓包实例
#include "header.h"
#include <string.h>
#include <sys/ioctl.h>

int Open_Raw_Socket(void);
int Set_Promisc(char *interface, int sock);
void dump(const unsigned char*data_buffer, const unsigned int length);

int main() {
    int sock;
    sock = Open_Raw_Socket();
    printf("raw socket is %d\n", sock);

    char buffer[65535];
    
    int bytes_recieved;
    size_t fromlen;
    struct  sockaddr_in from;
    
    struct ip *ip;
    struct tcp *tcp;
    
    // 设置网卡eth0为混杂模式
    Set_Promisc("eth0", sock);
    // 输出TCP/IP报头的长度
    printf("IP header is %d \n", sizeof(struct ip));
    printf("TCP header is %d \n", sizeof(struct tcp));
    
    while (1) {
        fromlen = sizeof(from);
        bytes_recieved = recvfrom(sock, buffer, sizeof(buffer),
                0, (struct sockaddr*)&from, &fromlen);
        
        printf("\nBytes recieved: %5d\n", bytes_recieved);
        printf("Source address: %s\n", inet_ntoa(from.sin_addr));
        
        ip = (struct ip*)buffer;
        if (ip->ip_p == 6) {
            printf("Dest address is: %s\n", inet_ntoa(ip->ip_dst));
            printf("IP header Length is :%d\n", ip->ip_len);
            printf("Protocol: %d\n", ip->ip_p);
            printf("Type of Server: %d\n", ip->ip_tos);
            printf("Time to live is : %d\n",ip->ip_ttl);
            printf("Check Sum is : %d\n", ip->ip_sum);
            tcp = (struct tcp*)(buffer + 20);        // IP数据包和TCP数据包有20个字节的距离
            printf("Dest port is: %d\n", ntohs(tcp->th_dport));
            printf("Source port is: %d\n", ntohs(tcp->th_sport));
            printf("Seq number is : %d\n", tcp->th_seq);
            printf("Ack number is : %d\n", tcp->th_ack);
            printf("Flags is %d\n", tcp->th_flags);
            
            dump((const unsigned char*)buffer, bytes_recieved);
            
        }
    }
    return 0;
    
}


// 建立一个原始socket句柄
int Open_Raw_Socket(void) {
    int sock;
    if ((sock = socket(AF_INET, SOCK_RAW, IPPROTO_TCP)) < 0) {
        perror("raw socket error\n");
        exit(1);
    }
    return sock;
}

// 设置eth0为混杂模式

int Set_Promisc(char *interface, int sock) {
    struct ifreq ifr;
    
    strncpy(ifr.ifr_name, interface, strnlen(interface, sizeof(interface)) + 1);
    if (ioctl(sock, SIOCGIFFLAGS, &ifr) == -1) {
        perror("set promisc error one if\n");
        exit(2);
    }
    
    printf("The interface is %s\n", interface);
    printf("Retrieved flags from interface is ok\n");
    
    ifr.ifr_flags |= IFF_PROMISC;
    
    if (ioctl(sock, SIOCGIFFLAGS, &ifr) == -1) {
        perror("Can not set PROMISC flag:");
        exit(-1);
    }
    
    printf("Set Promisc ok\n");
    
    return 0;
    
    
}

// 输出buffer内容
void dump(const unsigned char*data_buffer, const unsigned int length) {

    unsigned char byte;
    unsigned int i, j;
    
    for (i = 0; i < length; i++) {
        byte = data_buffer[i];
        printf("%02x ", data_buffer[i]);
        if ((i % 16 == 15) || (i == length -1)) {
            for (j = 0; j < 15 -(i % 16); j++) {
                printf("   ");
            }
            printf("|");
            for (j = (i - (i % 16)); j <= i; j++) {
                byte = data_buffer[j];
                if (byte > 31 && byte < 127)
                    printf("%c", byte);
                else
                    printf(".");
            }
            printf("\n");
        }
    }
    
}
Linux 原始套接字抓包实例

使用时,需要root权限,在Ubuntu下需要sudo一下,使用实例, 利用了twsited的echo服务:

Linux 原始套接字抓包实例

可以用于调试简单的本机socket编程。

Linux 原始套接字抓包实例

上一篇:阿里云DTS和DG助力未来油站 实现安全、稳定的云上云下数据库同步


下一篇:数据中台从何而来