原始套接字

在计算机网络中, 原始套接字（raw socket）是一种网络套接字，允许直接发送/接收IP协议数据包而不需要任何传输层协议格式。
简介.
对于标准的套接字，通常数据按照选定的传输层协议(例如TCP、UDP)自动封装，socket用户并不知道在网络介质上广播的数据包含了这种协议包头。
从原始套接字读取数据包含了传输层协议包头。用原始套接字发送数据，是否自动增加传输层协议包头是可选的。
原始套接字用于安全相关的应用程序，如nmap。原始套接字一种可能的用例是在用户空间实现新的传输层协议。 原始套接字常在网络设备上用于路由协议，例如IGMPv4、开放式最短路径优先协议 (OSPF)、互联网控制消息协议 (ICMP)。Ping就是发送一个ICMP响应请求包然后接收ICMP响应回复.
实现.
大部分套接字API都支持原始套接字功能。Winsock自2001年起在Windows XP上支持原始套接字。但由于安全原因，2004年微软限制了Winsock的原始套接字功能。
例子.
下例在Linux上实现了Ping程序的主要功能：
/* 校验和计算 */
u_int16_t checksum(unsigned short *buf, int size)
 unsigned long sum = 0;
 while (size &gt; 1) {
 sum += *buf;
 buf++;
 size -= 2;
 if (size == 1)
 sum += *(unsigned char *)buf;
 sum = (sum &amp; 0xffff) + (sum » 16);
 sum = (sum &amp; 0xffff) + (sum » 16);
 return ~sum;
/* protocol指定的raw socket创建 */
int make_raw_socket(int protocol)
 int s = socket(AF_INET, SOCK_RAW, protocol);
 if (s &lt; 0) {
 perror("socket");
 exit(EXIT_FAILURE);
 return s;
/* ICMP头部的作成 */
void setup_icmphdr(u_int8_t type, u_int8_t code, u_int16_t id, u_int16_t seq, struct icmphdr *icmphdr)
 memset(icmphdr, 0, sizeof(struct icmphdr));
 icmphdr-&gt;type = type;
 icmphdr-&gt;code = code;
 icmphdr-&gt;checksum = 0;
 icmphdr-&gt;un.echo.id = id;
 icmphdr-&gt;un.echo.sequence = seq;
 icmphdr-&gt;checksum = checksum((unsigned short *)icmphdr, sizeof(struct icmphdr));
int main(int argc, char **argv)
 int n, soc;
 char buf[1500];
 struct sockaddr_in addr;
 struct in_addr insaddr;
 struct icmphdr icmphdr;
 struct iphdr *recv_iphdr;
 struct icmphdr *recv_icmphdr;
 if (argc &lt; 2) {
 printf("Usage : %s IP_ADDRESS\n", argv[0]);
 return 1;
 addr.sin_family = AF_INET;
 addr.sin_addr.s_addr = inet_addr(argv[1]);
 soc = make_raw_socket(IPPROTO_ICMP);
 setup_icmphdr(ICMP_ECHO, 0, 0, 0, &amp;icmphdr);
 /* ICMP包的送信 */
 n = sendto(soc, (char *)&amp;icmphdr, sizeof(icmphdr), 0, (struct sockaddr *)&amp;addr, sizeof(addr));
 if (n &lt; 1) {
 perror("sendto");
 return 1;
 /* ICMP包的受信 */
 n = recv(soc, buf, sizeof(buf), 0);
 if (n &lt; 1) {
 perror("recv");
 return 1;
 recv_iphdr = (struct iphdr *)buf;
 /* 从IP包头获取IP包的长度，从而确定icmp包头的开始位置 */
 recv_icmphdr = (struct icmphdr *)(buf + (recv_iphdr-&gt;ihl « 2));
 insaddr.s_addr = recv_iphdr-&gt;saddr;
 /* 检查送信包的源地址匹配受信包的目的地址 */
 if (!strcmp(argv[1], inet_ntoa(insaddr)) &amp;&amp; recv_icmphdr-&gt;type == ICMP_ECHOREPLY)
 printf("icmp echo reply from %s\n", argv[1]);
 close(soc);
 return 0;
下例是binding一个原始套接字并使用：
int CreateRawSocket(int protocol_to_sniff)
 int rawsock;
 if((rawsock = socket(PF_PACKET, SOCK_RAW, htons(protocol_to_sniff)))== -1) {
 perror("Error creating raw socket: ");
 exit(-1);
 return rawsock;
 
int BindRawSocketToInterface(char *device, int rawsock, int protocol)
 struct sockaddr_ll sll;
 struct ifreq ifr;
 memset(&amp;sll, 0, sizeof(sll));
 memset(&amp;ifr, 0, sizeof(ifr));
 /* First Get the Interface Index */
 strncpy((char *)ifr.ifr_name, device, IFNAMSIZ);
 if((ioctl(rawsock, SIOCGIFINDEX, &amp;ifr)) == -1) {
 printf("Error getting Interface index !\n");
 exit(-1);
 /* Bind our raw socket to this interface */
 sll.sll_family = AF_PACKET;
 sll.sll_ifindex = ifr.ifr_ifindex;
 sll.sll_protocol = htons(protocol);
 if((bind(rawsock, (struct sockaddr *)&amp;sll, sizeof(sll)))== -1) {
 perror("Error binding raw socket to interface\n");
 exit(-1);
 return 1;
 
int SendRawPacket(int rawsock, unsigned char *pkt, int pkt_len)
 int sent= 0;
 /* A simple write on the socket ..thats all it takes ! */
 if((sent = write(rawsock, pkt, pkt_len)) != pkt_len) {
 return 0;
 return 1;
/* argv[1] is the device e.g. eth0
 argv[2] is the number of packets to send */
main(int argc, char **argv)
 int raw;
 unsigned char packet[PACKET_LENGTH];
 int num_of_pkts;
 /* Set the packet to all A's */
 memset(packet, 'A', PACKET_LENGTH);
 /* Create the raw socket */
 raw = CreateRawSocket(ETH_P_ALL);
 /* Bind raw socket to interface */
 BindRawSocketToInterface(argv[1], raw, ETH_P_ALL);
 num_of_pkts = atoi(argv[2]);
 while((num_of_pkts--)&gt;0) {
 if(!SendRawPacket(raw, packet, PACKET_LENGTH)) {
 perror("Error sending packet");
 } else {
 printf("Packet sent successfully\n");
 close(raw);
 return 0;