ネットワークプログラミング(オリジナルソケット)

6768 ワード

元のソケットには、次の機能があります.
1、読み書きICMPv 4、IGMPv 4及びICMPv 6パケット.pingプログラムのように、元のソケットインタフェースを使用してICMPエコー要求を送信し、ICMPエコー応答を受信する.
2、特殊なIPv 4データを読み書きする.ほとんどのカーネル処理値は、1(ICMP)、2(IGMP)、6(TCP)、および17(UDP)のデータ・レポートです.プロトコル・フィールドは、他の値にもなります.
3、IP_HDRINCLソケットオプションを使用して、自分のIPv 4ヘッダを構築することができます.
元のソケットの作成
一般的に以下のステップに分けられます
1、2番目のパラメータはSOCK_RAWで、socket関数を呼び出して元のソケットを作成します.3番目のパラメータは一般的に0ではありません.protocolはIPPROTO_ICMPまたはIPPROTO_IGMPで使用できます.元のソケットを作成する権限はスーパーユーザーのみです.
2、IP_HDRINCLソケットのオプションを設定できます.
const int on = 1;
setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on));
3、元のセットに対してbind関数を呼び出すことができますが、よく使いません.これはローカルアドレスを設定するために使用され、ポート番号には意味がありません. 
4、元のソケットでconnect関数を呼び出すこともできますし、あまり使われません.宛先アドレスを設定するだけで、ポートにも意味がありません.connectを呼び出すと、宛先アドレスが指定されているので、sendtoではなくwriteやsendを呼び出すことができます.
オリジナルソケット出力
出力には次のルールがあります.
1、通常出力はsendtoまたはsendmsgを呼び出し、目的のIPアドレスを指定して完了する.ソケットインタフェースが接続されている場合、write、writevまたはsendを使用することができる.
2、IP_HDRINCLが設定されていない場合、書き込みデータの先頭アドレスはIPヘッダの後の第1バイトであり、ヘッダプロトコルフィールドはsocket呼び出し時の第3パラメータとして記入される
3.IP_HDRINCLが設定されている場合、書き込みデータの先頭アドレスがIPヘッダの最初のバイトである場合、ユーザーが提供するデータサイズの値はヘッダのバイト数を含む必要がある.このプロセスは、識別フィールドと検査とフィールドのほか、プロセスによって設定される可能性がある.検査とは、カーネル計算によって埋め込まれる
4.外出インタフェースMTUを超えたパケットについては、カーネルをスライスする.
オリジナルソケット入力
受信した次のパケットは、元のソケットインタフェースに渡されるかどうか、次のルールがあります.
1、TCPとUDPパケットは元のセットインタフェースに渡さない
2.カーネルがICMPメッセージを処理した後、ほとんどのICMPパケットが元のソケットに伝達される
3、カーネルがIGMPメッセージを処理した後、すべてのIGMPパケットが元のソケットに渡される
4、カーネルが識別できないプロトコルフィールドのIPデータ報告はすべて元のインタフェースに伝達される.カーネルはこれらのパケットに対して唯一したのはIPヘッダのいくつかのフィールドを検査することである:IPバージョン、IPv 4ヘッダ検査と、ヘッダ長と目的のIPアドレス
5.データがフラグメント形式で到着した場合、パケットは元のすべてのフラグメントを再編成に到着した後、元のセットインタフェースに伝達する
次は元のソケットで書かれたPingのようなプログラムです.
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <arpa/inet.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netdb.h>
#include <signal.h>
#include <sys/time.h>
#include <stdio.h>

#define BUFSIZE 1500

char recvbuf[BUFSIZE];
char sendbuf[BUFSIZE];
//int datalen;
char *host;
int nsent;
pid_t pid;
int sockfd;
int verbose;

void proc_v4(char *, ssize_t, struct timeval *);
//void proc_v6(char *, ssize_t, struct timeval *);
void send_v4();
//void send_v6();
void readloop();
void sig_alrm(int);
void tv_sub(struct timeval*, struct timeval*);

struct proto
{
	void (*fproc)(char *, ssize_t, struct timeval*);
	void (*fsend)(void);
	struct sockaddr *sasend;
	struct sockaddr *sarecv;
	socklen_t salen;
	int icmpproto;
}*pr;

char *sock_ntop(struct sockaddr *sa, socklen_t len)
{
	char portstr[7];	
	static char str[128];
	
	switch (sa->sa_family) {
	case AF_INET:
	{
		struct sockaddr_in *sin = (struct sockaddr_in*)sa;
		if (inet_ntop(AF_INET, &sin->sin_addr, str, sizeof(str)) == NULL) return NULL;
		if (ntohs(sin->sin_port) != 0) {
			snprintf(portstr, sizeof(portstr), "port=%d", ntohs(sin->sin_port));	
			strcat(str, portstr);
		}

		return str;
	}
	}	
}

struct addrinfo *host_serv(const char *host, const char *serv, int family, int socktype)
{
	struct addrinfo hints, *res;
	int n;
		
	bzero(&hints, sizeof(hints));
	hints.ai_flags = AI_CANONNAME;
	hints.ai_family = family;
	hints.ai_socktype = socktype;


	if ((n = getaddrinfo(host, serv, &hints, &res)) != 0) return NULL;

	return res;
}

struct proto proto_v4 = {proc_v4, send_v4, NULL, NULL, 0, IPPROTO_ICMP};
int datalen = 56;

int main(int argc, char **argv)
{
	int c;
	struct addrinfo *ai;
	
	pid = getpid();
	signal(SIGALRM, sig_alrm);

	ai = host_serv(argv[1], NULL, 0, 0);
	pr = &proto_v4;

	printf("ICMP_ECHO=%d
", ICMP_ECHO); pr->sasend = ai->ai_addr; pr->sarecv = calloc(1, ai->ai_addrlen); pr->salen = ai->ai_addrlen; readloop(); exit(0); } void readloop(void) { int size; char recvbuf[BUFSIZE]; socklen_t len; ssize_t n; struct timeval tval; sockfd = socket(pr->sasend->sa_family, SOCK_RAW, pr->icmpproto); setuid(getuid()); size = 60 * 1024; setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)); sig_alrm(SIGALRM); for (;;) { len = pr->salen; n = recvfrom(sockfd, recvbuf, sizeof(recvbuf), 0, pr->sarecv, &len); if (n < 0) { if (errno == EINTR) continue; else { printf("recvfrom error:%s
", strerror(errno)); return; } } gettimeofday(&tval, NULL); (*pr->fproc)(recvbuf, n, &tval); } } void tv_sub(struct timeval* out, struct timeval *in) { if ((out->tv_usec -= in->tv_usec) < 0) { --out->tv_sec; out->tv_usec += 1000000; } out->tv_sec -= in->tv_sec; } void proc_v4(char *ptr, ssize_t len, struct timeval *tvrecv) { int hlen1, icmplen; double rtt; struct ip *ip; struct icmp *icmp; struct timeval *tvsend; ip = (struct ip*)ptr; hlen1 = ip->ip_hl << 2; icmp = (struct icmp*)(ptr + hlen1); if ((icmplen = len - hlen1) < 8) { fprintf(stderr, "icmp len error
"); return; } if (icmp->icmp_type == ICMP_ECHOREPLY) { if (icmp->icmp_id != pid) return; if (icmplen < 16) { fprintf(stderr, "icmplen (%d) < 16
", icmplen); return; } tvsend = (struct timeval *)icmp->icmp_data; tv_sub(tvrecv, tvsend); rtt = tvrecv->tv_sec * 1000 + tvrecv->tv_usec / 1000; printf("%d bytes from %s:seq=%u, ttl=%d, rtt=%.3f ms
", icmplen, sock_ntop(pr->sarecv, pr->salen), icmp->icmp_seq, ip->ip_ttl, rtt); } else if (verbose) { printf("%d bytes from %s:type=%d, code=%d
", icmplen, sock_ntop(pr->sarecv, pr->salen), icmp->icmp_type, icmp->icmp_code); } } void sig_alrm(int signo) { (*pr->fsend)(); alarm(1); return; } unsigned short in_cksum(unsigned short *addr, int len) { int nleft = len; int sum = 0; unsigned short *w = addr; unsigned short answer = 0; while (nleft > 1) { sum += *w++; nleft -= 2; } if (nleft == 1) { *(unsigned char *)(&answer) = *(unsigned char *)w; sum += answer; } sum = (sum >> 16) + (sum & 0xffff); sum += (sum >> 16); answer = ~sum; return answer; } void send_v4() { int len; struct icmp *icmp; icmp = (struct icmp *)sendbuf; icmp->icmp_type = ICMP_ECHO; icmp->icmp_code = 0; icmp->icmp_id = pid; icmp->icmp_seq = nsent++; gettimeofday((struct timeval*)icmp->icmp_data, NULL); len = 8 + datalen; icmp->icmp_cksum = 0; icmp->icmp_cksum = in_cksum((unsigned short*)icmp, len); sendto(sockfd, sendbuf, len, 0, pr->sasend, pr->salen); }