歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> 原始套接字學習筆記

原始套接字學習筆記

日期:2017/3/1 9:18:28   编辑:Linux編程

一般來說,我們會用到如下三種套接字:

TCP:SOCK_STREAM套接字

UDP:SOCK_DGRAM套接字

原始套接字:SOCK_RAW套接字

對於TCP和UDP兩種套接字,相對來說只要配置好IP地址和端口號就可以了,比較簡單,這裡我們主要介紹原始套接字的使用。

1.原始套接字簡介

  原始套接字的強大之處在於,不同與UDP和TCP套接字只能訪問傳輸層和傳輸層以上的數據包,原始套接字可以訪問傳輸層以下的數據包,實現上至應用層下至鏈路層的數據操作,尤其適合用來進行抓包等工作。

2.原始套接字的建立

  常用的原始套接字的建立方式有如下兩種:

int sockfd=socket(PF_PACKET,SOCK_PACKET,htons(ETH_P_ALL));//這個socket可以訪問處理鏈路層及以上所有的數據包
int sockfd=socket(PF_PACKET,SOCK_RAW,htons(ETH_P_IP));//這個socket可以訪問處理鏈路層及以上所有的IP數據包

3.原始套接字權限

  原始套接字需要root權限即管理員權限才能夠創建,所以需要sudo和su進入root模式,而且在使用原始套接字進行抓包的過程中需要設置網卡為混雜模式。

  下面給出兩個例子,供大家參考:

  第一個,利用原始套接字進行發包,使用wireshark抓包查看;

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <time.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/errno.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <sys/stat.h>

#include <net/if.h>
#include <net/ethernet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/if_ether.h>
#include <netpacket/packet.h>
#include <netdb.h>

#include <arpa/inet.h>
#include <unistd.h>
#include <pthread.h>
#include <time.h>
#include <errno.h>
#include <fcntl.h>
#include <dirent.h>
#include <resolv.h>
#include <signal.h>
#include <getopt.h>
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

//首先定義網卡端口
#define PHYSICALPORT "eth0"
#define PHYSICALPORT_LEN 30

//創建實際網卡端口數組
char physical_port[30];

//定義緩存大小
#define BUFSIZE 1024*5
char sendbuf[BUFSIZE]={0};

int main()
{
    memcpy(physical_port,PHYSICALPORT,PHYSICALPORT_LEN);
     int sock_send;
    sock_send=socket(PF_PACKET,SOCK_PACKET,htons(ETH_P_ALL));
    if(sock_send<0)
    {
        perror("scoket created");   
    }
    
    //設置發包地址
    struct sockaddr send_addr;
    memset(&send_addr,0,sizeof(send_addr));
    strcpy(send_addr.sa_data,physical_port);

    //創建發送程序
    while(fgets(sendbuf,sizeof(sendbuf),stdin)!=0)
    {
        int len=sendto(sock_send,&sendbuf,strlen(sendbuf),0,&send_addr,sizeof(send_addr));
        memset(sendbuf,0,sizeof(sendbuf));
    }
    return 0;
}

  使用wireshark抓包可以得到如下結果:

   第二個,使用原始套接字建立一個接收程序,抓取本機的數據進行分析:

   代碼如下:

#include <stdio.h>
#include<stdlib.h>
#include<string.h>
#include<stdarg.h>
#include<time.h>

#include<sys/types.h>
#include<sys/socket.h>
#include<sys/errno.h>
#include<sys/time.h>
#include<sys/ioctl.h>
#include<sys/stat.h>

#include<net/if.h>
#include<net/ethernet.h>
#include<netinet/in.h>
#include<netinet/ip.h>
#include<netinet/if_ether.h>
#include<netpacket/packet.h>
#include<netdb.h>

#include<arpa/inet.h>
#include<unistd.h>
#include<pthread.h>
#include<time.h>
#include<errno.h>
#include<fcntl.h>
#include<dirent.h>
#include<resolv.h>
#include<signal.h>
#include<getopt.h>
#ifdef HAVE_CONFIG_H
#include<config.h>
#endif

//首先定義網卡端口
#define PHYSICALPORT "eth0"
#define PHYSICALPORT_LEN 30

//創建實際網卡端口數組
char physical_port[30];

//定義緩存大小
#define BUFSIZE 1024*5
char recvbuf[BUFSIZE]={0};

void ethernet_setpormisc(int fd,int i_flags);


int main()
{
memcpy(physical_port,PHYSICALPORT,PHYSICALPORT_LEN);
//首先創建一個原始套接字
int sock_recv;
sock_recv=socket(PF_PACKET,SOCK_RAW,htons(ETH_P_ALL));
if(sock_recv<0)
{
perror("physicl socket created");
}

//設置網卡為混雜模式;
ethernet_setpormisc(sock_recv,1);

//setsockopt
int recvbuf_size=BUFSIZE;
setsockopt(sock_recv,SOL_SOCKET,SO_RCVBUF,&recvbuf,sizeof(char));

//獲取物理網卡接口索,用以傳輸數據
struct ifreq ifr_recv;
strcpy(ifr_recv.ifr_name,physical_port);
if(ioctl(sock_recv,SIOCGIFINDEX,&ifr_recv)<0)
{
perror("[3]get interface index");
}
//綁定物理網卡
struct sockaddr_ll local_addr;
local_addr.sll_family=PF_PACKET;
local_addr.sll_ifindex=ifr_recv.ifr_ifindex;
local_addr.sll_protocol=htons(ETH_P_ALL);
if((bind(sock_recv,(struct sockaddr *)&local_addr,sizeof(local_addr)))<0)
{
perror("[4]bind physical address");
}

//開始接收數據包
while(1)
{
recvfrom(sock_recv,recvbuf,BUFSIZE,0,NULL,NULL);
printf("%s",recvbuf);
memset(recvbuf,0,sizeof(recvbuf));
}
close(sock_recv);
}


//創建設置網卡混雜模式函數
void ethernet_setpormisc(int fd,int i_flags)
{
//首先獲取網卡接口標志位
struct ifreq ifr_s;
memcpy(ifr_s.ifr_name,physical_port,sizeof(physical_port));
if(ioctl(fd,SIOCGIFFLAGS,&ifr_s)<0)
{
perror("[1]get interface flags");
}
if(i_flags==0)
{
//取消混雜模式
ifr_s.ifr_flags &= ~IFF_PROMISC;
}
else
{
//設置為混雜模式
ifr_s.ifr_flags |= IFF_PROMISC;
}

//將接口設置為相應的模式
if(ioctl(fd,SIOCSIFFLAGS,&ifr_s)<0)
{
perror("[2]set interface flags");
}
}

  抓取到的數據包如下所示: 

 

  因為以%s字符串形式打印出來,所以有很多亂碼,這裡需要再寫包解析函數進行解析!

更多詳情見請繼續閱讀下一頁的精彩內容: http://www.linuxidc.com/Linux/2016-03/128821p2.htm

Copyright © Linux教程網 All Rights Reserved