Linux网络编程- 原始套接字(Raw Socket)

这篇具有很好参考价值的文章主要介绍了Linux网络编程- 原始套接字(Raw Socket)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

基本概念

原始套接字(Raw Socket)提供了一种机制,允许应用程序直接访问底层传输协议,绕过操作系统提供的传输层接口。这种套接字通常用于实现新的协议或对现有协议进行低级别的操作。

以下是对原始套接字的详细介绍:

  1. 定义与用途:

    • 原始套接字是直接基于网络层(如IP)的。当使用原始套接字发送数据时,应用程序负责构建完整的协议头。
    • 它常常被用于构造和发送自定义的IP包,如在ping、traceroute等工具中,它们使用ICMP协议构建消息。
  2. 创建:

    • 创建原始套接字与创建其他类型的套接字相似。例如,创建一个用于IPv4和ICMP的原始套接字:socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
  3. 特权:

    • 由于原始套接字允许直接访问底层协议,并可能被用于伪造数据包,所以它通常需要特殊权限(如root权限)。
  4. 工作方式:

    • 发送: 当发送数据,需要提供完整的传输层头部(如TCP、UDP或ICMP头部)。这给了我们控制头部字段的能力,例如伪造源IP地址。
    • 接收: 当使用原始套接字接收数据时,会得到底层协议的完整头部。
  5. 用途与限制:

    • 用途: 原始套接字经常被用于网络诊断工具(如ping和traceroute)、网络攻击和防御、以及某些类型的网络测试。
    • 限制: 大多数操作系统默认会处理某些协议,这可能会导致原始套接字不能接收到这些协议的数据包。例如,操作系统可能会自动处理ICMP回显请求和回显应答,这意味着原始套接字可能无法看到这些数据包。
  6. 注意事项:

    • 由于原始套接字跳过了常规的协议处理,错误的使用可能导致不可预期的网络行为。
    • 操作系统可能提供了某种形式的保护,以防止滥用原始套接字,例如对其使用进行限制。
  7. 跨平台的差异:

    • 不同的操作系统可能对原始套接字的实现和行为有所不同。例如,Windows和Linux在处理和访问原始套接字时存在细微差别。

总的来说,原始套接字是一个非常强大的工具,但也需要谨慎使用。正确使用它需要对网络协议有深入的理解,而滥用它可能导致网络问题或被视为恶意活动。

链路层上的原始套接字

创建链路层的原始套接字允许我们直接与链路层设备(例如以太网适配器)交互,从而可以发送和接收链路层帧,例如以太网帧。这在某些网络工具和应用中非常有用,例如包捕获工具、桥接和交换应用程序。

以下是如何在Linux中创建链路层的原始套接字:

  1. 包括必要的头文件:

    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <netinet/ether.h>
    #include <netpacket/packet.h>
    #include <net/ethernet.h>  /* 需要以太网协议宏 */
    
  2. 创建套接字:
    使用socket()系统调用创建一个原始套接字,具体地,使用AF_PACKET作为地址族和SOCK_RAW作为套接字类型。

    int sockfd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
    if (sockfd == -1) {
        perror("socket");
        exit(EXIT_FAILURE);
    }
    

    注意:htons(ETH_P_ALL)表示该套接字将接收所有类型的以太网帧。

  3. 绑定到具体的网络接口:
    如果想指定从哪个接口接收帧,可以使用bind()函数。首先,需要查找网络接口的索引号。

    char *interface_name = "eth0";  // 例如
    struct sockaddr_ll sa;
    memset(&sa, 0, sizeof(struct sockaddr_ll));
    sa.sll_family = AF_PACKET;
    sa.sll_protocol = htons(ETH_P_ALL);
    sa.sll_ifindex = if_nametoindex(interface_name);
    if (bind(sockfd, (struct sockaddr*)&sa, sizeof(struct sockaddr_ll)) == -1) {
        perror("bind");
        exit(EXIT_FAILURE);
    }
    
  4. 发送和接收帧:
    一旦套接字被创建(并可能被绑定),可以使用标准的sendto()recvfrom()函数发送和接收数据。接收到的数据将包括完整的链路层帧。

  5. 关闭套接字:
    使用close(sockfd);关闭套接字。

需要注意的是,访问链路层通常需要特权,因此上述代码通常需要以root权限运行。

示例

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netpacket/packet.h>
#include <net/ethernet.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/if.h>

#define BUFFER_SIZE 2048

int main() {
    int sockfd, n;
    char buffer[BUFFER_SIZE];
    struct sockaddr_ll sa;
    struct ifreq ifr; // For setting promiscuous mode
    char *interface_name = "eth0";

    sockfd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
    if (sockfd == -1) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    // Enable promiscuous mode
    strncpy(ifr.ifr_name, interface_name, IFNAMSIZ);
    if (ioctl(sockfd, SIOCGIFFLAGS, &ifr) == -1) {
        perror("ioctl SIOCGIFFLAGS");
        close(sockfd);
        exit(EXIT_FAILURE);
    }
    ifr.ifr_flags |= IFF_PROMISC;
    if (ioctl(sockfd, SIOCSIFFLAGS, &ifr) == -1) {
        perror("ioctl SIOCSIFFLAGS");
        close(sockfd);
        exit(EXIT_FAILURE);
    }

    memset(&sa, 0, sizeof(struct sockaddr_ll));
    sa.sll_family = AF_PACKET;
    sa.sll_protocol = htons(ETH_P_ALL);
    sa.sll_ifindex = if_nametoindex(interface_name);
    if (bind(sockfd, (struct sockaddr*)&sa, sizeof(struct sockaddr_ll)) == -1) {
        perror("bind");
        close(sockfd);
        exit(EXIT_FAILURE);
    }
    
    printf("Listening on %s in promiscuous mode...\n", interface_name);
    
    while (1) {
        n = recvfrom(sockfd, buffer, BUFFER_SIZE, 0, NULL, NULL);
        if (n == -1) {
            perror("recvfrom");
            break;
        }
        printf("Received a frame of length: %d bytes\n", n);
    }
    
    close(sockfd);
    return 0;
}

这个程序的主要目的是使用原始套接字在Linux环境中捕获网络接口上的数据帧。这种原始套接字的功能允许应用程序直接处理网络层以下的协议,如链路层。下面是对程序的详细分析:

  1. 创建原始套接字:

    • 使用socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL))创建一个原始套接字,这允许程序接收所有类型的数据帧。
    • 如果套接字创建失败,程序会打印错误并退出。
  2. 启用混杂模式:

    • 为了捕获在网络接口上的所有流量(不仅仅是发送给该主机的流量),程序启用了混杂模式。
    • 使用ioctl系统调用与SIOCGIFFLAGS命令获取当前接口的标志。
    • 设置IFF_PROMISC标志,以启用混杂模式。
    • 再次使用ioctl系统调用与SIOCSIFFLAGS命令来更新接口的标志。
    • 如果在启用混杂模式的过程中出现任何错误,程序会打印错误信息并退出。
  3. 绑定套接字到特定接口:

    • 为了确保从特定的网络接口(在此例中为eth0)接收数据帧,程序绑定了原始套接字到这个接口。
    • 使用if_nametoindex函数获取接口的索引。
    • 使用bind函数绑定套接字。
    • 如果绑定失败,程序会打印错误信息并退出。
  4. 捕获循环:

    • 程序进入一个无限循环,在该循环中,它使用recvfrom函数来接收数据帧。
    • 对于每个接收到的数据帧,程序打印其长度。
    • 如果在接收数据时出现错误,程序将打印错误信息并跳出循环。
  5. 资源清理:

    • 在程序结束之前,它使用close函数关闭原始套接字以释放资源。

总体来说,这个程序创建并绑定了一个原始套接字,启用了混杂模式,并持续监听指定网络接口上的所有数据帧。这种捕获能力使它非常适合于网络监控和分析任务。


补充

启用混杂模式和绑定到特定接口

启用混杂模式和绑定到特定接口是两个相互独立但在某些用例中都必需的操作。

  1. 启用混杂模式 (Promiscuous Mode):

    • 当一个网络接口处于混杂模式时,它会接收到其上所有传输的数据帧,而不仅仅是那些目标地址与其MAC地址匹配的数据帧。这种模式对于一些特定的应用程序(如网络嗅探器和分析器)是非常有用的,因为它们通常需要捕获所有经过的流量,而不仅仅是发送给本机的流量。
    • 混杂模式是在网卡级别设置的,意味着启用后,接口会将所有流量传递给操作系统。但是,如果有多个网络接口(例如,eth0、eth1等),仅仅启用混杂模式并不会告诉我们应该从哪个接口捕获流量。
  2. 绑定到特定接口:

    • 绑定操作确保原始套接字只从指定的接口(在此例中为eth0)接收数据帧。没有这个绑定,套接字可能会从任何启用了混杂模式的接口接收数据帧。
    • 绑定是在套接字级别设置的,它确保只从特定的接口获取数据,而不是从所有接口。

总结来说,启用混杂模式是为了能够看到所有的流量,而绑定到特定接口是为了确定从哪个接口接收这些流量。两者结合使用,允许我们专门监视特定接口上的所有流量。

struct sockaddr_ll

struct sockaddr_ll 是Linux特有的数据结构,用于定义在数据链路层(Layer 2)上的套接字地址,特别是当使用PF_PACKET协议族时。这个结构提供了原始套接字用来发送和接收数据包所需的所有信息。我们来详细分析它的各个字段:

  1. sll_family: 这是地址族字段。对于这个特定的结构,它总是设置为AF_PACKET

  2. sll_protocol: 这是以网络字节序表示的物理层协议。例如,要接收所有的以太网帧,可以设置为htons(ETH_P_ALL)

  3. sll_ifindex: 这是接口索引,用于标识要绑定的网络接口。通常,可以使用if_nametoindex函数将接口名(如 “eth0”)转换为其索引。

  4. sll_hatype: 这是ARP硬件类型。例如,以太网的硬件类型是ARPHRD_ETHER

  5. sll_pkttype: 描述了数据包的类型,可以是如下类型之一:

    • PACKET_HOST: 目标是本地主机。
    • PACKET_BROADCAST: 这是一个广播数据包。
    • PACKET_MULTICAST: 这是一个多播数据包。
    • PACKET_OTHERHOST: 目标是其他主机,但由于某种原因被本机接收。
    • PACKET_OUTGOING: 由本机生成的输出数据包。
      …等等。
  6. sll_halen: 物理层地址的长度。例如,对于以太网,这个长度是6。

  7. sll_addr: 存储物理层地址(通常是MAC地址)的数组。由于MAC地址的长度为6字节而这个字段为8字节,所以通常后两个字节保持为0。

总之,struct sockaddr_ll结构提供了在数据链路层上发送和接收数据包所需的所有信息,特别是当使用原始套接字和PF_PACKET协议族时。

PF_PACKET 和 AF_PACKET 实际上是同一个值,只是在不同的上下文中使用了不同的名称。在套接字编程中,通常使用 AF_ 前缀来表示地址族,而使用 PF_ 前缀来表示协议族。在 Linux 中,PF_PACKET 和 AF_PACKET 的值是相同的,因此在 socket() 调用中使用哪个取决于个人习惯。文章来源地址https://www.toymoban.com/news/detail-728087.html

到了这里,关于Linux网络编程- 原始套接字(Raw Socket)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Linux网络】网络编程套接字(TCP)

    目录 地址转换函数 字符串IP转整数IP 整数IP转字符串IP 关于inet_ntoa 简单的单执行流TCP网络程序 TCP socket API 详解及封装TCP socket  服务端创建套接字  服务端绑定  服务端监听  服务端获取连接  服务端处理请求 客户端创建套接字 客户端连接服务器 客户端发起请求 服务器测试

    2024年03月21日
    浏览(71)
  • linux【网络编程】之网络套接字预备

    在【网络基础】中我们提到了IP地址,接下来了解一下网络通信中其他方面的知识 端口号是一个2字节16位的整数; 端口号用来标识一个进程, 告诉操作系统, 当前的这个数据要交给哪一个进程来处理; 一个端口号只能被一个进程占用 通信原理 (公网)IP唯一标识一台主机,这样两台

    2024年02月05日
    浏览(91)
  • 【Linux】网络---->套接字编程(TCP)

    TCP的编程流程:大致可以分为五个过程,分别是准备过程、连接建立过程、获取新连接过程、消息收发过程和断开过程。 1.准备过程:服务端和客户端需要创建各自的套接字,除此之外服务端还需要绑定自己的地址信息和进行监听。注意:服务端调用listen函数后,处理监听状

    2024年02月04日
    浏览(67)
  • Linux网络编程——tcp套接字

    本章Gitee仓库:tcp套接字 客户端: 客户端: 关于构造和初始化,可以直接在构造的时候,将服务器初始化,那为什么还要写到 init 初始化函数里面呢? 构造尽量简单一点,不要做一些“有风险”的操作。 tcp 是面向连接的,通信之前要建立连接,服务器处于等待连接到来的

    2024年02月20日
    浏览(57)
  • 【Linux】网络基础+UDP网络套接字编程

    只做自己喜欢做的事情,不被社会和时代裹挟着前进,是一件很奢侈的事。 1. 首先计算机是人类设计出来提高生产力的工具,而人类的文明绵延至今一定离不开人类之间互相的协作,既然人类需要协作以完成更为复杂的工作和难题,所以计算机作为人类的工具自然也一定需要

    2024年02月08日
    浏览(63)
  • 【Linux网络编程】网络编程套接字(TCP服务器)

    作者:爱写代码的刚子 时间:2024.4.4 前言:本篇博客主要介绍TCP及其服务器编码 只介绍基于IPv4的socket网络编程,sockaddr_in中的成员struct in_addr sin_addr表示32位 的IP地址 但是我们通常用点分十进制的字符串表示IP地址,以下函数可以在字符串表示和in_addr表示之间转换 字符串转in

    2024年04月14日
    浏览(79)
  • 【Linux网络】网络编程套接字(预备知识+UDP)

    目录 预备知识 1. 理解源IP地址和目的IP地址 2. 理解源MAC地址和目的MAC地址 3. 认识端口号  4. 理解源端口号和目的端口号 5. 端口号(port) vs 进程pid 6. 认识TCP协议和认识UDP协议 7. 网络字节序 socket编程接口  1. socket 常见API 2. sockaddr结构  简单的UDP网络程序  1. 服务端创建udp

    2024年02月19日
    浏览(58)
  • 【Linux Network】网络编程套接字(代码练习)—UDP

    目录 1. 常用接口 2. C/S 回声模拟 3. C/S myshell 的制作  Linux网络编程✨ 1. 常用接口 socket:创建套接字: 返回值: 套接字创建成功返回一个文件描述符 ,创建失败返回-1,同时错误码会被设置。 参数: domain: 网络通信 设置为 AF_INET(IPv4)或AF_INET6(IPv6) ; type:基于 UDP的网

    2024年02月03日
    浏览(95)
  • 【Linux网络】网络编程套接字 -- 基于socket实现一个简单UDP网络程序

    我们把数据从A主机发送到B主机,是目的吗?不是,真正通信的不是这两个机器!其实是这两台机器上面的软件(人) 数据有 IP(公网) 标识一台唯一的主机 ,用谁来标识各自主机上客户或者服务进程的唯一性呢? 为了更好的表示一台主机上服务进程的唯一性,我们采用 端口号

    2024年02月12日
    浏览(158)
  • 【探索Linux】P.25(网络编程套接字基本概念 —— 预备知识)

    在上一篇文章中,我们深入探讨了Linux网络的基础知识和它的发展历史,为读者揭开了Linux网络技术演变的序幕。我们了解到,Linux网络技术的发展不仅促进了操作系统本身的成熟,还对整个互联网的进步产生了深远的影响。随着网络技术的不断进步,Linux系统在网络通信方面

    2024年04月27日
    浏览(48)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包