Linux网络编程——原始套接字编程

  原始套接字编程和之前的 UDP 编程差不多,无非就是创建一个套接字后,通过这个套接字接收数据或者发送数据。区别在于,原始套接字可以自行组装数据包(伪装本地 IP,本地 MAC),可以接收本机网卡上所有的数据帧(数据包)。另外,必须在管理员权限下才能使用原始套接字。

原始套接字的创建:

int socket ( int family, int type, int protocol );

参数:
  family:协议族 这里写 PF_PACKET
  type:  套接字类,这里写 SOCK_RAW
  protocol:协议类别,指定可以接收或发送的数据包类型,不能写 “0”,取值如下,注意,传参时需要用 htons() 进行字节序转换。

  ETH_P_IP:IPV4数据包
  ETH_P_ARP:ARP数据包
  ETH_P_ALL:任何协议类型的数据包

返回值:
  成功( >0 ):套接字,这里为链路层的套接字
  失败( <0 ):出错

实例如下:

 1 // 所需头文件
 2 #include <sys/socket.h>
 3 #include <netinet/ether.h>
 4 #include <stdio.h>  // perror
 5
 6 int main(int argc,char *argv[])
 7 {
 8     int sock_raw_fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL) );
 9
10     if(sock_raw_fd < 0){
11         perror("socket");
12         return -1;
13     }
14
15     return 0;
16 }

获取链路层的数据包:

ssize_t recvfrom( int sockfd,
          void *buf,
          size_t nbytes,
          int flags,
          struct sockaddr *from,
          socklen_t *addrlen );

参数:

  sockfd: 原始套接字
  buf: 接收数据缓冲区
  nbytes: 接收数据缓冲区的大小

  flags: 套接字标志(常为0)

  from: 这里没有用,写 NULL

  addrlen:这里没有用,写 NULL

返回值:
  成功:接收到的字符数
  失败:-1

实例如下:

 1 #include <stdio.h>
 2 #include <netinet/in.h>
 3 #include <sys/socket.h>
 4 #include <netinet/ether.h>
 5
 6 int main(int argc,char *argv[])
 7 {
 8     unsigned char buf[1024] = {0};
 9     int sock_raw_fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
10
11     //获取链路层的数据包
12     int len = recvfrom(sock_raw_fd, buf, sizeof(buf), 0, NULL, NULL);
13     printf("len = %d\n", len);
14
15     return 0;
16 }

混杂模式

  默认的情况下,我们接收数据,目的地址是本地地址,才会接收。有时候我们想接收所有经过网卡的所有数据流,而不论其目的地址是否是它,这时候我们需要设置网卡为混杂模式

  网卡的混杂模式一般在网络管理员分析网络数据作为网络故障诊断手段时用到,同时这个模式也被网络黑客利用来作为网络数据窃听的入口。在 Linux 操作系统中设置网卡混杂模式时需要管理员权限。在 Windows 操作系统和 Linux 操作系统中都有使用混杂模式的抓包工具,比如著名的开源软件 Wireshark。

通过命令给 Linux 网卡设置混杂模式(需要管理员权限)

设置混杂模式:ifconfig eth0 promisc

取消混杂模式:ifconfig eth0 -promisc

通过代码给 Linux 网卡设置混杂模式

代码如下:

 1 struct ifreq ethreq;    //网络接口地址
 2
 3 strncpy(ethreq.ifr_name, "eth0", IFNAMSIZ);            //指定网卡名称
 4 if(-1 == ioctl(sock_raw_fd, SIOCGIFINDEX, &ethreq))    //获取网络接口
 5 {
 6     perror("ioctl");
 7     close(sock_raw_fd);
 8     exit(-1);
 9 }
10
11 ethreq.ifr_flags |= IFF_PROMISC;
12 if(-1 == ioctl(sock_raw_fd, SIOCSIFINDEX, &ethreq))    //网卡设置混杂模式
13 {
14     perror("ioctl");
15     close(sock_raw_fd);
16     exit(-1);
17 }

发送自定义的数据包:

ssize_t sendto( int sockfd,
        const void *buf,
        size_t nbytes,int flags,
        const struct sockaddr *to,
        socklen_t addrlen );

参数:

  sockfd: 原始套接字
  buf: 发送数据缓冲区
  nbytes: 发送数据缓冲区的大小

  flags: 一般为 0
  to: 本机网络接口,指发送的数据应该从本机的哪个网卡出去,而不是以前的目的地址
  addrlen:to 所指向内容的长度

返回值:
  成功:发送数据的字符数
  失败: -1

本机网络接口的定义

发送完整代码如下:

 1 struct sockaddr_ll sll;                    //原始套接字地址结构
 2 struct ifreq ethreq;                    //网络接口地址
 3
 4 strncpy(ethreq.ifr_name, "eth0", IFNAMSIZ);            //指定网卡名称
 5 if(-1 == ioctl(sock_raw_fd, SIOCGIFINDEX, ðreq))    //获取网络接口
 6 {
 7     perror("ioctl");
 8     close(sock_raw_fd);
 9     exit(-1);
10 }
11
12 /*将网络接口赋值给原始套接字地址结构*/
13 bzero(&sll, sizeof(sll));
14 sll.sll_ifindex = ethreq.ifr_ifindex;
15
16 // 发送数据
17 // send_msg, msg_len 这里还没有定义,模拟一下
18 int len = sendto(sock_raw_fd, send_msg, msg_len, 0 , (struct sockaddr *)&sll, sizeof(sll));
19 if(len == -1)
20 {
21     perror("sendto");
22 }

这里头文件情况如下:

1 #include <net/if.h>// struct ifreq
2 #include <sys/ioctl.h> // ioctl、SIOCGIFADDR
3 #include <sys/socket.h> // socket
4 #include <netinet/ether.h> // ETH_P_ALL
5 #include <netpacket/packet.h> // struct sockaddr_ll

转自:http://blog.csdn.net/tennysonsky/article/details/44676377

时间: 2024-11-19 20:37:16

Linux网络编程——原始套接字编程的相关文章

Linux网络编程——原始套接字实例:简单版网络数据分析器

通过<Linux网络编程--原始套接字编程>得知,我们可以通过原始套接字以及 recvfrom( ) 可以获取链路层的数据包,那我们接收的链路层数据包到底长什么样的呢? 链路层封包格式 MAC 头部(有线局域网) 注意:CRC.PAD 在组包时可以忽略 链路层数据包的其中一种情况: unsigned char msg[1024] = { //--------------组MAC--------14------ 0xb8, 0x88, 0xe3, 0xe1, 0x10, 0xe6, // dst

Linux网络编程——原始套接字实例:MAC 头部报文分析

通过<Linux网络编程——原始套接字编程>得知,我们可以通过原始套接字以及 recvfrom( ) 可以获取链路层的数据包,那我们接收的链路层数据包到底长什么样的呢? 链路层封包格式 MAC 头部(有线局域网) 注意:CRC.PAD 在组包时可以忽略 链路层数据包的其中一种情况: 1 unsigned char msg[1024] = { 2 //--------------组MAC--------14------ 3 0xb8, 0x88, 0xe3, 0xe1, 0x10, 0xe6,

Linux Socket 原始套接字编程

对于linux网络编程来说,可以简单的分为标准套接字编程和原始套接字编程,标准套接字主要就是应用层数据的传输,原始套接字则是可以获得不止是应用层的其他层不同协议的数据.与标准套接字相区别的主要是要开发之自己构建协议头.对于原始套接字编程有些细节性的东西还是需要注意的. 1. 原始套接字创建 原始套接字的编程和udp网络编程的流程有点类似,但是原始套接字编程中不需要bind操作,因为在数据接收和发送过程中使用sendto和recvfrom函数实现数据的接收和发送.不过不是说原始套接字不能使用bin

网络通讯之套接字编程

#include<stdio.h> #include<sys/socket.h> #include<netinet/in.h> static char out_ip[15] = "52.0.10.188"; static int out_port = 8888; int main() { char sSendBuf[2049], sRecvBuf[2049]; int connfd = 0, iRet = 0, iSendLen = 0; struc

Linux网络编程——原始套接字能干什么?

通常情况下程序员接所接触到的套接字(Socket)为两类: (1)流式套接字(SOCK_STREAM):一种面向连接的 Socket,针对于面向连接的TCP 服务应用: (2)数据报式套接字(SOCK_DGRAM):一种无连接的 Socket,对应于无连接的 UDP 服务应用. 从用户的角度来看,SOCK_STREAM.SOCK_DGRAM 这两类套接字似乎的确涵盖了 TCP/IP 应用的全部,因为基于 TCP/IP 的应用,从协议栈的层次上讲,在传输层的确只可能建立于 TCP 或 UDP 协议

Linux 网络编程——原始套接字实例:MAC 地址扫描器

如果 A (192.168.1.1 )向 B (192.168.1.2 )发送一个数据包,那么需要的条件有 ip.port.使用的协议(TCP/UDP)之外还需要 MAC 地址,因为在以太网数据包中 MAC 地址是必须要有的.那么怎样才能知道对方的 MAC 地址?答案是:它通过 ARP 协议来获取对方的 MAC 地址. ARP(Address Resolution Protocol,地址解析协议),是 TCP/IP 协议族中的一个,主要用于查询指定 ip 所对应的的 MAC(通过 ip 找 MA

Linux 网络编程——原始套接字实例:发送 UDP 数据包

以太网报文格式: 详细的说明,请看<MAC 头部报文分析>. IP 报文格式: 详细的说明,请看<IP 数据报格式详解>. UDP 报文格式: 详细的说明,请看<UDP 数据报格式详解>. 校验和函数: /******************************************************* 功能: 校验和函数 参数: buf: 需要校验数据的首地址 nword: 需要校验数据长度的一半 返回值: 校验和 ********************

LINUX 网络编程 原始套接字

一 原始套接字 原始套接字(SOCK_RAW)是一种不同于SOCK_STREAM.SOCK_DGRAM的套接字,它实现于系统核心.然而,原始套接字能做什么呢?首先来说,普通的套接字无法处理ICMP.IGMP等网络报文,而SOCK_RAW可以:其次,SOCK_RAW也可以处理特殊的IPv4报文:此外,利用原始套接字,可以通过IP_HDRINCL套接字选项由用户构造IP头.总体来说,SOCK_RAW可以处理普通的网络报文之外,还可以处理一些特殊协议报文以及操作IP层及其以上的数据. 既然SOCK_R

关于linux 原始套接字编程

关于linux 网络编程最权威的书是<<unix网络编程>>,但是看这本书时有些内容你可能理解的不是很深刻,或者说只知其然而不知其所以然,那么如果你想搞懂的话那么我建议你可以看看网络协议栈的实现. 函数原型是 int socket(int domain, int type, int protocol); 其中domain 中AF_INET , AF_UNIT 较为常用,分别创建inet 域套接字和unix域套接字,unix套接字与文件相关.平时80%用的套接字都是AF_INET.这