Raw socket

raw socket はコンピュータネットワークにおいて、生のネットワークパケットのダイレクトな送信と受信を可能にするインターネットソケット（英語版）である。 標準のソケットでは、送信するペイロードは選択したトランスポート層のプロトコル（例: TCP、UDP）によってカプセル化される。 対照的にraw socketは通常ヘッダを含んだ生のパケットを受信する。パケットを送るとき、ヘッダの自動的な追加はソケットの変更可能なオプションとなるだろう。 raw socketのひとつの可能な用途としてユーザー空間での新しいトランスポート層の実装が挙げられる。 raw socketは典型的にネットワーク装置において利用でき、Internet Group Management Protocol (IGMP) や Open Shortest Path First (OSPF) のようなルーティングプロトコル、そして Internet Control Message Protocol (ICMP, Pingとしてよく知られている) に使用されている。 ほとんどのソケットAPI、特にBSDソケットを基本にしたものでは、raw socketがサポートされている。 下記は第一引数のIPアドレスで指定されたホストにicmp echoパケットを送信し、replyが返ってくるのを待つプログラムである。 #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ヘッダからヘッダ長を求め、icmpヘッダの開始位置を調べる */ recv_icmphdr = (struct icmphdr *)(buf + (recv_iphdr->ihl << 2)); insaddr.s_addr = recv_iphdr->saddr; /* 送信先と受信したパケットの送信源が一致するかと受信したパケットがICMP ECHO REPLYか確認 */ 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; } 実行例 以下はwikipedia.org (208.80.152.201) を指定した出力例である。 [ping@icmp] # ./ping 208.80.152.201 icmp echo reply from 208.80.152.201 マイクロソフトが2001年にWindows XPをリリースした際、Winsock APIにてraw socketのサポートを実装していた。このときメディアは、raw socketはハッカーがTCPリセット攻撃（英語版）を行う役にしか立たないと主張してマイクロソフトを非難した。[要出典] Windows XPのリリースから3年後、マイクロソフトは取消不可能なホットフィックスの中でWinsockにおけるraw socketサポートを黙って制限し、それまでこれを利用していたアプリケーションに対してサポートや回避策を何ら提供しなかった。なお、raw socketサポートの制限について、のちにサポート技術情報が公開されていた。 Windows XP SP2以降のOSにおいてもraw socketにはいくつかの制限事項があるが、Windows Server 2003などのサーバー系OSにおいては適用されない。Winsockアプリケーションにおけるraw socketの使用は非推奨とされている。