【RawSocket】RawSocket的使用方法

1.RawSocket简介

RawSocket是数据链路层的socket

Raw socket(原始套接字)是一种特殊的网络套接字类型,它允许应用程序直接发送和接收底层的网络数据包,而不需要经过标准的传输层协议(如TCP或UDP)的处理。以下是raw socket的一些关键特点:

  1. 直接访问网络层
  • Raw socket允许应用程序直接与网络层交互,发送和接收IP数据包。这意味着应用程序可以绕过传输层协议,直接处理网络数据。

2.灵活性

  • 由于raw socket提供了对网络数据包的直接控制,它为网络编程提供了极大的灵活性。应用程序可以自定义数据包的内容和格式,实现特定的网络功能。

3.低级访问

  • Raw socket提供了对网络协议栈的低级访问。应用程序可以访问和修改IP头、TCP头、UDP头等网络协议头部,实现复杂的网络操作。

4.安全性

  • Raw socket的使用需要较高的权限,通常只有具有管理员权限的用户才能创建raw socket。这是因为raw socket可以发送任意类型的网络数据包,可能会被用于恶意目的。

5.应用场景

  • Raw socket常用于网络工具和诊断工具的开发,如网络嗅探器、防火墙、入侵检测系统等。它们需要直接访问网络数据包来实现特定的功能。

6.编程复杂性

  • 由于raw socket提供了底层的网络访问,使用raw socket进行网络编程通常比使用标准的套接字更加复杂。开发者需要对网络协议有深入的理解。

7.操作系统支持

  • 不同的操作系统对raw socket的支持程度不同。大多数Unix-like系统(如Linux和macOS)支持raw socket,但可能需要特定的权限或配置

2.RawSocket收发底层网络数据包

2.1 创建raw socket

创建一个raw socket:

int sock = socket(PF_PACKET,SOCK_RAW,htons(ETH_P_ALL));
if(sock < 0)
{
   
   
	perror("sock");
	return -1;
}

在Linux系统中,使用 socket函数创建原始套接字(raw socket)时,可以捕获和发送任意类型的网络数据包。以下是 socket函数调用 socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));中各个参数的说明:

1.PF_PACKET

  • PF_PACKET(Packet Family)是一个常量,表示要创建的套接字用于处理原始数据包。这个参数指定了套接字的地址族,告诉内核这个套接字将用于处理低级的数据包。

2.SOCK_RAW

  • SOCK_RAW是一个常量,表示套接字类型为原始套接字。这种类型的套接字允许应用程序直接发送和接收原始的网络数据包,绕过标准的传输层协议(如TCP或UDP)。

3.htons(ETH_P_ALL)

  • htons是“host to network short”的缩写,是一个函数,用于将16位的主机字节顺序值转换为网络字节顺序。这对于网络通信是必要的,因为网络协议要求使用大端字节顺序。
  • ETH_P_ALL是一个宏,定义了以太网协议类型,其值是0x0003。使用 htons(ETH_P_ALL)表示这个原始套接字将捕获所有类型的以太网数据包,无论它们的协议类型是什么。
此外还有以下宏定义:
ETH_P_IP:表示 IPv4 协议,即以太网帧包含一个 IP 数据包。
ETH_P_ARP:表示 ARP(地址解析协议)数据包,用于将网络层的 IP 地址解析为链路层的 MAC 地址。
ETH_P_RARP:表示 RARP(反向地址解析协议),用于将链路层的 MAC 地址解析为网络层的 IP 地址。这个协议现在已经很少使用,被 DHCP 所取代。
ETH_P_IPV6:表示 IPv6 协议,即以太网帧包含一个 IPv6 数据包。
ETH_P_8021Q:表示 802.1Q VLAN(虚拟局域网)标记的以太网帧。
ETH_P_PPP_DISC:表示 PPPoE(以太网上的 PPP)发现阶段的数据包。
ETH_P_PPP_SES:表示 PPPoE 会话阶段的数据包。
ETH_P_MPLS_UC:表示 MPLS(多协议标签交换)单播数据包。
ETH_P_MPLS_MC:表示 MPLS 多播数据包。
ETH_P_802_3:表示标准的以太网帧,不包含任何额外的协议信息。

2.2 发送数据包

2.2.1 发送数据包函数原型

int sendto(int s,const void *buf,int len,unsigned int flags,
	const struct sockaddr *to,int tolen);

2.2.2 返回值

成功则返回实际传送出去的字符数,失败返回-1,错误原因会存在于errno中

2.2.3 参数说明

s:socket描述符;

buf:rawsocket数据包缓存区(包含待发送数据)

len:rawsocket数据包的长度

flags:调用方式标志位(一般设置为0)

to:指向接收数据的主机地址信息的结构体(sockaddr_in需要进行类型转换)

tolen: to所指结构体的长度

2.2.4 使用方法

struct sockaddr_ll sll;
memset(&sll,0,sizeof(sll));
sll.sll_ifindex = 2;     // 指定网卡
if(sendto(sock,packet_start,sizeof(packet_start),0,&sll,sizeof(sll)) < 0)
{
   
   
	perror("sendto");
	return 1;
}

sendto发送原始数据包,只需用struct sockaddr_ll的sll_ifindex指定网卡。

2.2.5 代码实例

发送raw socket(rawsocket_send.c):

#include <sys/socket.h>  
#include <string.h>  
#include <sys/types.h>  
#include <arpa/inet.h>  
#include <features.h>    /* for the glibc version number */  
#include <asm/types.h>  
#include <linux/if_packet.h>  
#include <linux/if_ether.h>   /* The L2 protocols */  
#include <stdio.h>  
#include <netinet/in.h>  
#include <net/if.h>  
#include <sys/ioctl.h>  
#include <errno.h>  
  
#define _PATH_PROCNET_DEV               "/proc/net/dev"  
  
static char *get_name(char *name, char *p)  
{
   
     
    while (isspace(*p))  
            p++;  
  
    while (*p) {
   
     
            if (isspace(*p))  
        break;  
            if (*p == ':') {
   
       /* could be an alias */  
        char *dot = p, *dotname = name;  
        *name++ = *p++;  
        while (isdigit(*p))  
        *name++ = *p++;  
        if (*p != ':') {
   
       /* it wasn't, backup */  
        p = dot;  
        name = dotname;  
        }  
        if (*p == '\0')  
        return NULL;  
        p++;  
        break;  
    }  
    *name++ = *p++;  
    }  
    *name++ = '\0';  
    return p;  
}  
  
/** 
 * read_netdev_proc - read net dev names form proc/net/dev 
 * @devname: where to store dev names, devname[num][len] 
 */  
static int read_netdev_proc(void *devname, const int num, const int len)  
{
   
     
    FILE *fh;  
    char buf[512];  
        int cnt = 0;  
        char *dev = (char *)devname;  
  
        if(devname == NULL || num < 1 || len < 4){
   
     
            printf("read_netdev_proc: para error\n");  
            return -1;  
        }  
  
        memset(devname, 0, len * num);  
  
    fh = fopen(_PATH_PROCNET_DEV, "r");  
    if (!fh) {
   
     
        fprintf(stderr, "Warning: cannot open %s (%s). Limited output.\n",  
            _PATH_PROCNET_DEV, strerror(errno));   
        return -1;  
      }  
  
    fgets(buf, sizeof buf, fh); /* eat two line */  
    fgets(buf, sizeof buf, fh);  
  
        cnt = 0;  
    while (fgets(buf, sizeof buf, fh) && cnt < num) {
   
     
            char *s, name[IFNAMSIZ];  
            s = get_name(name, buf);  
  
            strncpy(devname, name, len);  
            devname += len;  
            printf("get_name: %s\n", name);  
    }  
  
    if (ferror(fh)) {
   
     
            perror(_PATH_PROCNET_DEV);  
    }  
  
    fclose(fh);  
    return 0;  
}  
  
/** 
 * get_hwaddr - get netdevice mac addr  
 * @name: device name, e.g: eth0 
 * @hwaddr: where to save mac, 6 byte hwaddr[6] 
 * @return: 0 on success, -1 on failure 
 */  
int get_hwaddr(char *name, unsigned char *hwaddr)  
{
   
     
    struct ifreq ifr;  
    unsigned char memzero[6];  
    int sock;  
  
    if(name == NULL || hwaddr == NULL){
   
     
        printf("get_hwaddr: NULL para\n");  
        return -1;  
    }  
  
 sock = socket(AF_INET, SOCK_STREAM, 0);  
    if(sock < 0){
   
     
        printf("get_hwaddr: socket error\n");  
        //return -1;  
    }  
  
    //get eth1 mac addr  
    memset(hwaddr, 0, 6);  
    memset(&ifr, 0, sizeof(ifr));  
    strncpy(ifr.ifr_name, name, 6);  
    if (ioctl(sock, SIOCGIFHWADDR, &ifr) < 0){
   
     
            perror("get_hwaddr ioctl:");  
            close(sock);  
            return -1;  
    } else {
   
     
            memcpy(hwaddr, ifr.ifr_hwaddr.sa_data, 6);  
            //printf("hwaddr: %2x : %2x : %2x : %2x : %2x : %2x\n", hwaddr[0], hwaddr[1],hwaddr[2], hwaddr[3],hwaddr[4], hwaddr[5]);  
    }  
  
    memset(memzero, 0, 6);  
    if(memcmp(memzero, hwaddr, 6) == 0){
   
     
        printf<
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值