在電腦網路中, 原始通訊端(raw socket)是一種網路通訊端,允許直接傳送/接收IP協定封包而不需要任何傳輸層協定格式。
簡介
對於標準的通訊端,通常資料按照選定的傳輸層協定(例如TCP、UDP)自動封裝,socket使用者並不知道在網路媒介上廣播的資料包含了這種協定包頭。
從原始通訊端讀取資料包含了傳輸層協定包頭。用原始通訊端傳送資料,是否自動增加傳輸層協定包頭是可選的。
原始通訊端用於安全相關的應用程式,如nmap。原始通訊端一種可能的用例是在使用者空間實現新的傳輸層協定。[1] 原始通訊端常在網路裝置上用於路由協定,例如IGMPv4、開放式最短路徑優先協定 (OSPF)、網際網路控制訊息協定 (ICMP)。Ping就是傳送一個ICMP回應請求包然後接收ICMP回應回覆.[2]
實現
大部分通訊端API都支援原始通訊端功能。Winsock自2001年起在Windows XP上支援原始通訊端。但由於安全原因,2004年微軟限制了Winsock的原始通訊端功能。[3]
例子
下例在Linux上實現了Ping程式的主要功能:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <arpa/inet.h>
#include <netdb.h>
/* 校验和計算 */
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 < 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->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 < 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, &icmphdr);
/* ICMP包的送信 */
n = sendto(soc, (char *)&icmphdr, sizeof(icmphdr), 0, (struct sockaddr *)&addr, sizeof(addr));
if (n < 1) {
perror("sendto");
return 1;
}
/* ICMP包的受信 */
n = recv(soc, buf, sizeof(buf), 0);
if (n < 1) {
perror("recv");
return 1;
}
recv_iphdr = (struct iphdr *)buf;
/* 从IP包头获取IP包的长度,从而确定icmp包头的开始位置 */
recv_icmphdr = (struct icmphdr *)(buf + (recv_iphdr->ihl << 2));
insaddr.s_addr = recv_iphdr->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<stdio.h>
#include<stdlib.h>
#include<sys/socket.h>
#include<features.h>
#include<linux/if_packet.h>
#include<linux/if_ether.h>
#include<errno.h>
#include<sys/ioctl.h>
#include<net/if.h>
#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;
}
參見
參考文獻
外部連結
Wikiwand in your browser!
Seamless Wikipedia browsing. On steroids.
Every time you click a link to Wikipedia, Wiktionary or Wikiquote in your browser's search results, it will show the modern Wikiwand interface.
Wikiwand extension is a five stars, simple, with minimum permission required to keep your browsing private, safe and transparent.