ICMP
エコーリクエストをCプログラムで生成したい、linuxのposixスレッドで生成する。スレッド対プロセス:スレッド内でicmpパケットの作成が失敗する
試しに、私はmain()
にサンプルコードを書いています。 ICMP
エコーとその応答が期待通りに機能しました。パケット長は28(20バイトIP
ヘッダー+ 8バイトICMP
ヘッダー)でした。
私はスレッドにコードをシフトしました。今度はmain()
がスレッドを作成し、スレッドが終了するのを待ちます。
しかしtcpdump
で観察した場合、このパケットは、長さ48で示されているが、スレッドで、sendto()
、28戻り、ECHO
要求が適切でなかったことを意味IP不良HLEN 0、以下のようにライン。 IP
ヘッダーの合計長フィールドは、0x1c(28バイト)の代わりに0x30(48バイト)を示します。次に、tcpdumpのスナップショットを示します。
成功のtcpdump、
06:30:58.139476 IP (tos 0x0, ttl 64, id 19213, offset 0, flags [none], proto ICMP (1), length 28)
192.168.11.34 > 192.168.11.32: ICMP echo request, id 0, seq 0, length 8
0x0000: 4500 001c 4b0d 0000 4001 9841 c0a8 0b22 [email protected]"
0x0010: c0a8 0b20 0800 f7ff 0000 0000 ............
06:30:58.139819 IP (tos 0x0, ttl 64, id 6830, offset 0, flags [none], proto ICMP (1), length 28)
192.168.11.32 > 192.168.11.34: ICMP echo reply, id 0, seq 0, length 8
0x0000: 4500 001c 1aae 0000 4001 c8a0 c0a8 0b20 [email protected]
0x0010: c0a8 0b22 0000 ffff 0000 0000 0000 0000 ..."............
0x0020: 0000 0000 0000 0000 0000 0000 0000 ..............
誤ったヘッダ長/データ/いくつかのゴーストとパケットを処理コードを使用。
06:33:14.513597 IP (tos 0x0, ttl 64, id 22998, offset 0, flags [DF], proto ICMP (1), length 48)
192.168.11.34 > 192.168.11.32: ICMP type-#69, length 28
IP bad-hlen 0
0x0000: 4500 0030 59d6 4000 4001 4964 c0a8 0b22 [email protected]@.Id..."
0x0010: c0a8 0b20 4500 1c00 4b0d 0000 4001 7c5d [email protected]|]
0x0020: c0a8 0b22 c0a8 0b20 0800 f7ff 0000 0000 ..."............
この結果、recv()
に失敗します。
トラブルシューティングの一環として、ファイルに送信するために使用されたバッファをダンプし、hexdump
によって検証しました。両方のコードは同じパケットを生成します。 16進数値も表示されます。同じ結果。スレッドを作成する代わりに、フォークを試みました。出来た。
2つのコードの唯一の違いは、スレッドとプロセスです。考えられる問題がなくなった。
ディストリビューションはCentOS 7.1(カーネル3.10)とFedora 13(カーネル2.6.39)です。
ここにプロセスコードがあります。
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <netdb.h>
#include <linux/ip.h>
#include <linux/icmp.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <string.h>
#include <fcntl.h>
#include <net/if.h>
#include <pthread.h>
unsigned short in_cksum(unsigned short *addr, int len)
{
register int sum = 0;
u_short answer = 0;
register u_short *w = addr;
register int nleft = len;
/*
* * Our algorithm is simple, using a 32 bit accumulator (sum), we add
* * sequential 16 bit words to it, and at the end, fold back all the
* * carry bits from the top 16 bits into the lower 16 bits.
* */
while (nleft > 1)
{
sum += *w++;
nleft -= 2;
}
/* mop up an odd byte, if necessary */
if (nleft == 1)
{
*(u_char *) (&answer) = *(u_char *) w;
sum += answer;
}
/* add back carry outs from top 16 bits to low 16 bits */
sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
sum += (sum >> 16); /* add carry */
answer = ~sum; /* truncate to 16 bits */
return (answer);
}
int main()
{
struct iphdr *ip, *ip_reply;
struct icmphdr *icmp, *icmp_reply;
struct sockaddr_in connection;
char *dst_addr="192.168.11.32";
unsigned char *packet, *buffer;
int sockfd, optval, ret=-1;
socklen_t addrlen;
/* open ICMP socket */
if ((sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) == -1) {
perror("socket");
exit(EXIT_FAILURE);
}
printf("Socket is %d\n", sockfd) ;
packet = (unsigned char*)malloc(sizeof(struct iphdr) + sizeof(struct icmphdr));
buffer = (unsigned char*)malloc(sizeof(struct iphdr) + sizeof(struct icmphdr));
if(packet == NULL || buffer == NULL)
{
perror("Error in malloc") ;
}
memset(packet, 0, sizeof(struct iphdr) + sizeof(struct icmphdr));
memset(buffer, 0, sizeof(struct iphdr) + sizeof(struct icmphdr));
ip = (struct iphdr*) packet;
icmp = (struct icmphdr*) ((char*)packet + sizeof(struct iphdr));
ip->ihl = 5;
ip->version = 4;
ip->tot_len = sizeof(struct iphdr) + sizeof(struct icmphdr);
//ip->tot_len = 48;
ip->id = random()%5985;
ip->protocol = IPPROTO_ICMP;
ip->saddr = inet_addr("192.168.11.34");
ip->daddr = inet_addr(dst_addr);
// ip->daddr = inet_addr("8.8.8.8");
ip->ttl = 64;
ip->check = in_cksum((unsigned short *)ip, sizeof(struct iphdr));
icmp->type = ICMP_ECHO;
icmp->code = 0;
icmp->un.echo.id = 0;
icmp->un.echo.sequence = 0;
icmp->checksum = 0;
icmp->checksum = in_cksum((unsigned short *)icmp, sizeof(struct icmphdr));
//Dumping headers to a file, to be viewed using hexdump
int ip_file = open("working_header",O_CREAT|O_RDWR);
if(ip_file == -1)
{
perror("Error in file opening");
}
ret = write(ip_file, packet, sizeof(struct iphdr) + sizeof(struct icmphdr));
if(ret == -1)
{
perror("Error in write");
}
else
{
printf("Wrote %d bytes\n", ret) ;
}
close(ip_file);
//binding to a specific interface
struct ifreq ifr;
memset(&ifr, 0, sizeof (ifr));
snprintf (ifr.ifr_name, sizeof (ifr.ifr_name), "enp1s0");
if (ioctl (sockfd, SIOCGIFINDEX, &ifr) < 0)
{
//Failed to find interface on device
printf("Failed to find interface on device\n");
return -1;
}
if (setsockopt (sockfd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof (ifr)) < 0)
{
//Failed to bind to interface enp2s0
printf("Failed to bind to interface %s\n",ifr.ifr_name);
return -1;
}
struct timeval tv;
tv.tv_sec = 3;
tv.tv_usec = 0;
if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0)
{
printf("Unable to set timeout\n");
return -1;
}
/* IP_HDRINCL must be set on the socket so that the kernel does not attempt
* * to automatically add a default ip header to the packet*/
ret = setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, &optval, sizeof(int));
if(ret == -1)
{
perror("Error in setsockopt");
}
connection.sin_family = AF_INET;
connection.sin_addr.s_addr = ip->daddr;
printf("Packet length is: %d\n",ip->tot_len);
//printing packet, byte by byte, in hex, before sending
unsigned char ch = 0;
while (ch<28)
{
//printf("%x ",packet[ch]);
printf("0x%02x ", packet[ch]);
ch++;
}
printf("\n");
ret = sendto(sockfd, (void*)packet, ip->tot_len, 0, (struct sockaddr *)&connection, sizeof(struct sockaddr));
printf("Sent %d byte packet to %s ret = %d\n", ip->tot_len, dst_addr, ret);
// }
addrlen = sizeof(connection);
if (recvfrom(sockfd, buffer, sizeof(struct iphdr) + sizeof(struct icmphdr), 0, (struct sockaddr *)&connection, &addrlen) < 0)
{
perror("recv");
}
else
{
ip_reply = (struct iphdr*) buffer;
icmp_reply = (struct icmphdr*) (buffer + sizeof(struct iphdr));
printf("Received type %d\n", icmp_reply->type);
printf("icmp code %d\n", icmp_reply->code);
printf("TTL: %d\n", ip_reply->ttl);
printf("CheckSum: %d,%d\n", ip_reply->check,icmp_reply->checksum);
}
free(packet);
free(buffer);
close(sockfd);
return 0 ;
}
以下はスレッドコードです。 setsockopt
IP_HDRINCL
コールで
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <netdb.h>
#include <linux/ip.h>
#include <linux/icmp.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <string.h>
#include <fcntl.h>
#include <net/if.h>
#include <pthread.h>
unsigned short in_cksum(unsigned short *addr, int len)
{
register int sum = 0;
u_short answer = 0;
register u_short *w = addr;
register int nleft = len;
/*
* * Our algorithm is simple, using a 32 bit accumulator (sum), we add
* * sequential 16 bit words to it, and at the end, fold back all the
* * carry bits from the top 16 bits into the lower 16 bits.
* */
while (nleft > 1)
{
sum += *w++;
nleft -= 2;
}
/* mop up an odd byte, if necessary */
if (nleft == 1)
{
*(u_char *) (&answer) = *(u_char *) w;
sum += answer;
}
/* add back carry outs from top 16 bits to low 16 bits */
sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
sum += (sum >> 16); /* add carry */
answer = ~sum; /* truncate to 16 bits */
return (answer);
}
void* thread_for_icmp(void* arg)
{
struct iphdr *ip, *ip_reply;
struct icmphdr *icmp, *icmp_reply;
struct sockaddr_in connection;
char *dst_addr="192.168.11.32";
unsigned char *packet, *buffer;
int sockfd, optval, ret=-1;
socklen_t addrlen;
arg = arg;
/* open ICMP socket */
if ((sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) == -1) {
perror("socket");
exit(EXIT_FAILURE);
}
printf("Socket is %d in thread\n", sockfd) ;
packet = (unsigned char*)malloc(sizeof(struct iphdr) + sizeof(struct icmphdr));
buffer = (unsigned char*)malloc(sizeof(struct iphdr) + sizeof(struct icmphdr));
if(packet == NULL || buffer == NULL)
{
perror("Error in malloc") ;
}
memset(packet, 0, sizeof(struct iphdr) + sizeof(struct icmphdr));
memset(buffer, 0, sizeof(struct iphdr) + sizeof(struct icmphdr));
ip = (struct iphdr*) packet;
icmp = (struct icmphdr*) ((char*)packet + sizeof(struct iphdr));
ip->ihl = 5;
ip->version = 4;
ip->tot_len = sizeof(struct iphdr) + sizeof(struct icmphdr);
//ip->tot_len = 48;
ip->id = random()%5985;
ip->protocol = IPPROTO_ICMP;
ip->saddr = inet_addr("192.168.11.34");
ip->daddr = inet_addr(dst_addr);
// ip->daddr = inet_addr("8.8.8.8");
ip->ttl = 64;
ip->check = in_cksum((unsigned short *)ip, sizeof(struct iphdr));
icmp->type = ICMP_ECHO;
icmp->code = 0;
icmp->un.echo.id = 0;
icmp->un.echo.sequence = 0;
icmp->checksum = 0;
icmp->checksum = in_cksum((unsigned short *)icmp, sizeof(struct icmphdr));
//Dumping headers to a file, to be viewed using hexdump
int ip_file = open("header",O_CREAT|O_RDWR);
if(ip_file == -1)
{
perror("Error in file opening");
}
ret = write(ip_file, packet, sizeof(struct iphdr) + sizeof(struct icmphdr));
if(ret == -1)
{
perror("Error in write");
}
else
{
printf("Wrote %d bytes\n", ret) ;
}
close(ip_file);
//binding to a specific interface
struct ifreq ifr;
memset(&ifr, 0, sizeof (ifr));
snprintf (ifr.ifr_name, sizeof (ifr.ifr_name), "enp1s0");
if (ioctl (sockfd, SIOCGIFINDEX, &ifr) < 0)
{
//Failed to find interface on device
printf("Failed to find interface on device\n");
return NULL;
}
if (setsockopt (sockfd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof (ifr)) < 0)
{
//Failed to bind to interface enp2s0
printf("Failed to bind to interface %s\n",ifr.ifr_name);
return NULL;
}
struct timeval tv;
tv.tv_sec = 3;
tv.tv_usec = 0;
if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0)
{
printf("Unable to set timeout\n");
return NULL;
}
/* IP_HDRINCL must be set on the socket so that the kernel does not attempt
* * to automatically add a default ip header to the packet*/
ret = setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, &optval, sizeof(int));
if(ret == -1)
{
perror("Error in setsockopt");
}
connection.sin_family = AF_INET;
connection.sin_addr.s_addr = ip->daddr;
printf("Packet length is: %d\n",ip->tot_len);
//printing packet, byte by byte, in hex, before sending
unsigned char ch = 0;
while (ch<28)
{
//printf("%x ",packet[ch]);
printf("0x%02x ", packet[ch]);
ch++;
}
printf("\n");
ret = sendto(sockfd, (void*)packet, ip->tot_len, 0, (struct sockaddr *)&connection, sizeof(struct sockaddr));
printf("Sent %d byte packet to %s ret = %d\n", ip->tot_len, dst_addr, ret);
// }
addrlen = sizeof(connection);
if (recvfrom(sockfd, buffer, sizeof(struct iphdr) + sizeof(struct icmphdr), 0, (struct sockaddr *)&connection, &addrlen) < 0)
{
perror("recv");
}
else
{
ip_reply = (struct iphdr*) buffer;
icmp_reply = (struct icmphdr*) (buffer + sizeof(struct iphdr));
printf("Received type %d\n", icmp_reply->type);
printf("icmp code %d\n", icmp_reply->code);
printf("TTL: %d\n", ip_reply->ttl);
printf("CheckSum: %d,%d\n", ip_reply->check,icmp_reply->checksum);
}
free(packet);
free(buffer);
close(sockfd);
pthread_exit(NULL);
}
int main()
{
pthread_t thread;
int ret;
ret = pthread_create(&thread, NULL, thread_for_icmp, NULL);
if(ret == -1)
{
perror("Error in thread create");
}
ret = pthread_join(thread,NULL);
if(ret == -1)
{
perror("Error in thread join");
}
else
{
printf("Thread exited succesfully\n") ;
}
return 0;
}
愚かな質問:スレッディングなしで 'main()'から 'HandleFailoverStrategy'を呼び出すのと同じ動作をしていますか?どのようにコンパイルしますか? – purplepsycho
@purplepsychoスレッディングなしでは、期待通りに動作します。 コンパイルはgcc filename -o executable_nameです。スレッドコードに-lpthreadを使用しました。 – Dhruv
$ dhruv '-lpthread'の代わりに' -pthread'を試して、 '-Wall'を追加することがあります – purplepsycho