icmpaddrmask.c

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>

#include <unistd.h>
#include <signal.h>

#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <netinet/ip_icmp.h>

#define DEFDATALEN	12
#define MAXIPLEN	60
#define MAXICMPLEN	76
#define MAXPACKET	(65536 - 60 - 8)

char *hostname;
int sock_icmp;
struct addrinfo *addrinfo;

int datalen = DEFDATALEN;
uint8_t outpack[MAXPACKET];

int response = 0;

void sender(void);
uint16_t calc_cksum(uint16_t *data, int len);
void procpack(uint8_t *data, int len, struct sockaddr_in *from);
void sig_alrm(int signo);

int main(int argc, char *argv[])
{
	int res;
	struct addrinfo *item;
	struct addrinfo hints;

	if (argc != 2) {
		fprintf(stderr, "Usage: %s destination\n", argv[0]);
		exit(EXIT_FAILURE);
	}

	memset(&hints, 0, sizeof hints);
	hints.ai_flags    = AI_CANONNAME;
	hints.ai_family   = AF_INET;
	hints.ai_socktype = SOCK_RAW;
	hints.ai_protocol = IPPROTO_ICMP;
	if ((res = getaddrinfo(argv[1], NULL, &hints, &addrinfo)) != 0) {
		fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(res));
		exit(EXIT_FAILURE);
	}

	item = addrinfo;
	struct sockaddr_in *addr;
	char ip[INET_ADDRSTRLEN];
	while (item) {
		addr = (struct sockaddr_in *)item->ai_addr;
		inet_ntop(item->ai_family, &addr->sin_addr, ip, INET_ADDRSTRLEN);
		printf(
"\n"
"ai_flags = %d\n"
"ai_family = %d\n"
"ai_socktype = %d\n"
"ai_protocol = %d\n"
"ai_addrlen = %d\n"
"ai_addr.sin_family = %d, ai_addr.sin_port = %d, ai_addr.sin_addr.s_addr = %s\n"
"ai_canonname = %s\n",
item->ai_flags, 
item->ai_family, 
item->ai_socktype, 
item->ai_protocol, 
item->ai_addrlen,
addr->sin_family,
addr->sin_port,
ip,
item->ai_canonname);

		item = item->ai_next;
	}

	if ((sock_icmp = socket(addrinfo->ai_family, addrinfo->ai_socktype, addrinfo->ai_protocol)) < 0) {
		perror("socket");
		exit(EXIT_FAILURE);
	}

	sender();

	int packlen = datalen + MAXICMPLEN + MAXIPLEN;
	uint8_t *packet = (uint8_t *)malloc(packlen);
	if (!packet) {
		fprintf(stderr, "malloc packet");
		exit(EXIT_FAILURE);
	}

	signal(SIGALRM, sig_alrm);
	alarm(5);

	for (;;) {
		struct sockaddr_in from;
		socklen_t fromlen = sizeof from;
		int n;
		if ((n = recvfrom(sock_icmp, (char *)packet, packlen, 0, (struct sockaddr *)&from, &fromlen)) < 0) {
			if (errno == EINTR) {
				printf("recvfrom: errno = EINTR\n");
				continue;
			}	
			perror("recvfrom");
			continue;
		}	
		procpack(packet, n, &from);
	}

	return 0;
}

void sender(void)
{
	struct icmp *icp;
	int n;

	icp = (struct icmp *)outpack;
	icp->icmp_type	= ICMP_MASKREQ;
	icp->icmp_code 	= 0;
	icp->icmp_cksum = 0; // will calc checksum below
	icp->icmp_id	= getpid();
	icp->icmp_seq	= 12345; 

	icp->icmp_mask = 0;

	// calc ICMP checksum here
	// ICMP_MASKLEN: 12 = 8 bytes of header, 4 bytes of mask
	icp->icmp_cksum = calc_cksum((uint16_t *)icp, ICMP_MASKLEN);
	
	n = sendto(sock_icmp, outpack, ICMP_MASKLEN, 0, addrinfo->ai_addr, addrinfo->ai_addrlen);
	if (n < 0 || n != ICMP_MASKLEN) {
		if (n < 0) {
			perror("sendto");
		} else {
			fprintf(stderr, "wrote %s %d chars, ret=%d\n", addrinfo->ai_canonname, ICMP_MASKLEN, n);
		}
		exit(EXIT_FAILURE);
	}
}

uint16_t calc_cksum(uint16_t *data, int len)
{
	int nleft = len;
	uint16_t *w = data;
	int sum = 0;
	uint16_t answer = 0;

	while (nleft > 1) {
		sum += *w;
		w++;
		nleft -= 2;
	}

	if (nleft == 1) {
		*(u_int8_t *)(&answer)= *(uint8_t *)w;
		sum += answer;
	}

	sum = (sum >> 16) + (sum & 0xffff);
	sum += (sum >> 16);
	answer = ~sum;

	return answer;
}

void procpack(uint8_t *data, int len, struct sockaddr_in *from)
{
	struct ip *ip;
	struct icmp *icp;
	int hlen;
	char fromIP[INET_ADDRSTRLEN];
	char mask[INET_ADDRSTRLEN];

	inet_ntop(AF_INET, &from->sin_addr, fromIP, INET_ADDRSTRLEN);

	ip = (struct ip *)data;
	hlen = ip->ip_hl * 4;
	if (len < hlen + ICMP_MINLEN) {
		fprintf(stderr, "packet too short (%d bytes) from %s", len, fromIP);
		return;
	}

	len -= hlen;
	icp = (struct icmp *)(data + hlen);
	if (icp->icmp_type == ICMP_MASKREPLY) {
		if (len != ICMP_MASKLEN) {
			printf("len = %d, expected len = %d\n", len, ICMP_MASKLEN);
			return;
		}
		if (icp->icmp_seq != 12345) {
			printf("received sequence # %d\n", icp->icmp_seq);
			return;
		}
		if (icp->icmp_id != getpid()) {
			printf("received id %d\n", icp->icmp_id);
			return;
		}
		inet_ntop(AF_INET, &icp->icmp_mask, mask, INET_ADDRSTRLEN);
		printf("received mask = %08x (%s), from %s\n", ntohl(icp->icmp_mask), mask, fromIP);
		response++;
	}
}

void sig_alrm(int signo)
{
	if (response == 0) {
		printf("timeout\n");
		exit(1);
	}	
	exit(0);
}