原始套接字

在计算机网络中, 原始套接字(raw socket)是一种网络套接字,允许直接发送/接收IP协议数据包而不需要任何传输层协议格式。

简介
对于标准的套接字,通常数据按照选定的传输层协议(例如TCP、UDP)自动封装,socket用户并不知道在网络介质上广播的数据包含了这种协议包头。

从原始套接字读取数据包含了传输层协议包头。用原始套接字发送数据,是否自动增加传输层协议包头是可选的。

原始套接字用于安全相关的应用程序,如nmap。原始套接字一种可能的用例是在用户空间实现新的传输层协议。 原始套接字常在网络设备上用于路由协议,例如IGMPv4、开放式最短路径优先协议 (OSPF)、互联网控制消息协议 (ICMP)。Ping就是发送一个ICMP响应请求包然后接收ICMP响应回复.

实现
大部分套接字API都支持原始套接字功能。Winsock自2001年起在Windows XP上支持原始套接字。但由于安全原因,2004年微软限制了Winsock的原始套接字功能。
Ian Griffiths for IanG on Tap. 12 August, 2004. [http://www.interact-sw.co.uk/iangblog/2004/08/12/norawsockets Raw Sockets Gone in XP SP2]

例子
下例在Linux上实现了Ping程序的主要功能:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

/ 校验和計算 /
u_int16_t checksum(unsigned short *buf, int size)
{
unsigned long sum = 0;
while (size > 1) {
sum += *buf;
buf++;
size -= 2;
}
if (size == 1)
sum += (unsigned char )buf;
sum = (sum & 0xffff) + (sum >> 16);
sum = (sum & 0xffff) + (sum >> 16);
return ~sum;
}

/ protocol指定的raw socket创建 /
int make_raw_socket(int protocol)
{
int s = socket(AF_INET, SOCK_RAW, protocol);
if (s type = type;
icmphdr->code = code;
icmphdr->checksum = 0;
icmphdr->un.echo.id = id;
icmphdr->un.echo.sequence = seq;
icmphdr->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 ihl saddr;
/ 检查送信包的源地址匹配受信包的目的地址 /
if (!strcmp(argv[1], inet_ntoa(insaddr)) && recv_icmphdr->type == ICMP_ECHOREPLY)
printf("icmp echo reply from %s\n", argv[1]);
close(soc);
return 0;
}

下例是binding一个原始套接字并使用:

#include
#include
#include
#include
#include
#include
#include
#include
#include

#define PACKET_LENGTH 1024

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(&sll, 0, sizeof(sll));
memset(&ifr, 0, sizeof(ifr));
/ First Get the Interface Index /
strncpy((char *)ifr.ifr_name, device, IFNAMSIZ);
if((ioctl(rawsock, SIOCGIFINDEX, &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 *)&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--)>0) {
if(!SendRawPacket(raw, packet, PACKET_LENGTH)) {
perror("Error sending packet");
} else {
printf("Packet sent successfully\n");
}
}
close(raw);
return 0;
}

参见

  • IPv4包格式
  • IPv6包格式

参考文献
外部链接

*[http://www.pdbuchan.com/rawsock/rawsock.html C language examples of Linux raw sockets for IPv4 and IPv6] - David Buchan's C language examples of IPv4 and IPv6 raw sockets for Linux.

评论 (0)

  • 还没有评论,来抢沙发吧。