TCP/IP - Linux数据链路层的包解析

Linux数据链路层的包解析

仅以此文作为学习笔记,初学者,如有错误欢迎批评指正,但求轻喷。

一般而言,Linux系统截获数据包后,会通过协议栈,按照TCP/IP层次进行解析,那我们如何直接获得更为底层的数据报文呢,这里用到一个类型SOCK_PACKET类型。

int sockfd = socket(AF_INET,SOCK_PACKET,htons(0x0003));  

通过上面这个函数可以获得一个特殊的套接字,其中:

  • AF_INET: 表示因特网协议族
  • SOCK_PACKET: 表示数据包截取在物理层
  • 0x0003: 表示数据帧类型不确定

修改网络接口结构:

struct ifreq ifr;
strcpy(ifr.ifr_name,"eth0");
ioctl(sockfd,SIOCGIFFLAGS,&ifr);

设置混杂模式

ifr.ifr_flags| = IFR_PROMISC;
ioctl(sockfd,SIOCSIFFLAGS,&ifr);

注意:进行标志位设定时,遵循以下步骤:

  1. 取出原标识位;
  2. 与待设定标志位进行位或运算(“|”);
  3. 重新写入;

一个小小的抓包程序

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<iostream>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<sys/ioctl.h>
#include<net/if.h>
#include<arpa/inet.h>
#include<netinet/if_ether.h>
#include<netinet/in.h>
#include<netinet/ip.h>
#include<netinet/tcp.h>
#include<netinet/udp.h>

using namespace std;

char ethernet_frame[ETH_FRAME_LEN];//定义一个数据帧的长度
struct iphdr *ipheader;//定义ip头部指针

int socketcreate()
{
    int sockfd = socket(AF_INET,SOCK_PACKET,htons(0x0003));    
    //构建了一个数据链路层的数据包;
    if(sockfd == -1)
    {
        cout<<"Socket init error!"<<endl;    
        return -1;
    }
    /*
        下面设置接口结构
    */
    char *ifname = "eth0";
    struct ifreq ifr;
    strcpy(ifr.ifr_name,ifname);
    int result = ioctl(sockfd,SIOCGIFFLAGS,&ifr);
    if(result == -1)
    {
        cout<<"Can't get flags!"<<endl;
        close(sockfd);
        return -2;
    }
    ifr.ifr_flags|= IFF_PROMISC;
    /*
    一般而言,Linux系统截获数据包后,会通过协议栈,按照TCP/IP层次进行解析,那我们如何直接获得更为底层的数据报文呢,这里用到一个类型SOCK_PACKET类型。
                   int sockfd = socket(AF_INET,SOCK_PACKET,htons(0x0003));
                   通过上面这个函数可以获得一个特殊的套接字,其中:
                   AF_INET:                              表示因特网协议族
                   SOCK_PACKET:                   表示数据包截取在物理层
                   0x0003:                                 表示数据帧类型不确定

                   修改网络接口结构:
                   struct ifreq ifr;
                   strcpy(ifr.ifr_name,"eth0");
                   ioctl(sockfd,SIOCGIFFLAGS,&ifr);

                   设置混杂模式
                   ifr.ifr_flags| = IFR_PROMISC;
                   ioctl(sockfd,SIOCSIFFLAGS,&ifr);
                   注意:
                          进行标志位设定时,遵循以下步骤:
                        (1)取出原标识位;
                        (2)与待设定标志位进行位或运算(“|”);
                        (3)重新写入;
    */
    result = ioctl(sockfd,SIOCSIFFLAGS,&ifr);
    if(result == -1)
    {
        cout<<"Can't set flags!"<<endl;
        close(sockfd);
        return -3;
    }
    
    
    
    return sockfd;
}
int getframe(int sockfd,int num)
{
    struct ethhdr* fheader;
    fheader = (struct ethhdr*)ethernet_frame;
    memset(ethernet_frame,0,ETH_FRAME_LEN);
    int size = read(sockfd,ethernet_frame,ETH_FRAME_LEN);
    if(size <= 0)
    {
        cout<<"No packet or packet error!"<<endl;    
        return -1;
    }
    cout<<"************************Packet"<<num<<"received from eth0 START!************************"<<endl;
    printf("DST MAC: ");
    for(int i=0;i<ETH_ALEN-1;i++)
    {
        printf("%2x-",fheader->h_dest[i]);    
    }
    printf("%2x\n",fheader->h_dest[ETH_ALEN-1]);
    printf("SRC MAC: ");
    for(int i=0;i<ETH_ALEN-1;i++)
    {
        printf("%2x-",fheader->h_source[i]);    
    }
    printf("%2x\n",fheader->h_source[ETH_ALEN-1]);
    if(ntohs(fheader->h_proto) == 0x0800)
    {
        cout<<"Protocol: IP"<<endl;    
    }
    if(ntohs(fheader->h_proto) == 0x0806)
    {
        cout<<"Protocol: RAP"<<endl;    
    }
    if(ntohs(fheader->h_proto) == 0x8035)
    {
        cout<<"Protocol: RARP"<<endl;    
    }
    int ret = ntohs(fheader->h_proto);
    return ret;
}

int getip(int protocol,int num)
{
        if(protocol != 0x0800)
        {
            cout<<"NO IP Packet!"<<endl;
            cout<<"************************Packet"<<num<<"received from eth0 END!**************************"<<endl;
            return 0;    
        }
        ipheader = (struct iphdr*)(ethernet_frame+ETH_HLEN);
        printf("Version: 4");
        cout<<endl;
        in_addr *p,*q;
        p = (struct in_addr*)&ipheader->saddr;
        printf("SRC IP: %s",inet_ntoa(*p));
        cout<<endl;
        q = (struct in_addr*)&ipheader->daddr;
        printf("DST IP: %s",inet_ntoa(*q));
        cout<<endl;
        if(ipheader->protocol == 1)
        {
            cout<<"PROTOCOL: ICMP"<<endl;    
        }
        if(ipheader->protocol == 6)
        {
            cout<<"PROTOCOL: TCP"<<endl;    
        }
        if(ipheader->protocol == 17)
        {
            cout<<"PROTOCOL: UDP"<<endl;    
        }
        return ipheader->protocol;
}

int gettcp(int protocol)
{
    if(protocol != 6)
    {
        return -1;    
    }
    struct tcphdr* tcph;
    tcph = (struct tcphdr*)(ipheader+((ipheader->ihl)*4));
    printf("SRC PORT: %d",ntohs(tcph->source));
    cout<<endl;
    printf("DST PORT: %d",ntohs(tcph->dest));
    cout<<endl;
    return 0;
}

int getudp(int protocol)
{
    if(protocol != 17)
    {
        return -1;    
    }
    struct udphdr* udph;
    udph = (struct udphdr*)(ipheader+((ipheader->ihl)*4));
    printf("SRC PORT: %d",ntohs(udph->source));
    cout<<endl;
    printf("DST PORT: %d",ntohs(udph->dest));
    cout<<endl;
    return 0;
}

int main(int argc,char *argv[])
{        
    if(argc < 2)
    {
        cout<<"Please input the nummber of packet that you want to catch!"<<endl;
        return 0;
    }
    int num = (int)argv[1][0];
    int sock = socketcreate();
    for(int i=1;i<num;i++)
    {
        int ip_protocol = getframe(sock,i);
        int trasnport_protocol = getip(ip_protocol,i);
        gettcp(trasnport_protocol);
        getudp(trasnport_protocol);
        cout<<"************************Packet"<<num<<"received from eth0 END!**************************"<<endl;
        cout<<endl;
        cout<<endl;
        cout<<endl;
        cout<<endl;
    }
    return 0;
}
    原文作者:Jonathan
    原文地址: https://segmentfault.com/a/1190000019821819
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞