链路层原始套接字调用socket()函数来创建。第一个参数指定协议族类型为AF_PACKET,第二个参数可以设置为SOCK_RAW或SOCK_DGRAM,第三个参数是协议类型。协议类型protocol不用取值的意义如下表10.2所示。
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
参数type设置为SOCK_RAW时,套接字接收和发送的数据都是从MAC首部开始的。在发送时需要由调用者从MAC首部开启构造和封装报文数据。type设置为SOCK_RAW的情况应用是比较多的,因为某些项目会使用到自定义的二层报文类型。
socket(AF_PACKET,SOCK_RAW,htons(protocol));
参数type设置为SOCK_DGRAM时,套接字接收到的数据报文会将MAC头部去掉。同时在发送时也不需要再手动构造MAC头部。只需要从IP头部(不固定,封装的报文类型不同则不同,例如ARP头部)开始构造数据即可,而MAC头部的填充由内核实现。若对于MAC头部不关心的场景,可以使用这种类型,这种用法使用较少。
socket(AF_PACKET,SOCK_DGRAM,htons(protocol));
|protocol 值 作用
ETH_P_ALL 0x0003 接收本机收到的所有二层报文
ETH_P_IP 0x0800 接收本机收到的所有IP报文
ETH_P_ARP 0x0806 接收本机收到的所有ARP报文
ETH_P_RARP 0X8035 接收本机收到的所有RARP报文
自定义协议 例如,0x0820 接收本机收到的所有类型为0x0820的二层报文
不指定 0 默认值
…… …… ……
…… …… ……
链路层原始套接字创建代码示例如下所示。
1 #include <stdio.h>
2 #include <sys/socket.h>
3 #include <stdlib.h>
4 #include <arpa/inet.h>
5 #include <unistd.h>
6 #include <netinet/if_ether.h>
7
8 int main(int argc, const char *argv[])
9 {
10 //创建原始套接字
11 int sockfd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
12 if(sockfd < 0)
13 {
14 perror("fail to socket");
15 exit(1);
16 }
17
18 printf("sockfd = %d\n", sockfd);
19
20 close(sockfd);
21
22 return 0;
23 }
运行结果如下,如果以普通用户权限执行程序,则运行失败,由于原始套接字可以直接访问底层协议(IP、ICMP等),因此必须在管理员权限下才能使用原始套接字。
linux@Master:~/1000phone/net/net$ ./a.out
fail to socket: Operation not permitted
linux@Master:~/1000phone/net/net$ sudo ./a.out
sockfd = 3