1/*
2 * Copyright 2008-2010, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Yin Qiu
7 */
8
9
10#include <errno.h>
11#include <netdb.h>
12#include <netinet/in.h>
13#include <netinet/ip.h>
14#include <netinet/udp.h>
15#include <stdio.h>
16#include <stdlib.h>
17#include <string.h>
18#include <sys/socket.h>
19#include <sys/types.h>
20
21
22#define DGRM_SIZE 1600 // ought to be large enough
23
24
25struct pseudo_header {
26	struct in_addr src;
27	struct in_addr dst;
28	u_int8_t zero;
29	u_int8_t protocol;
30	u_int16_t len;
31};
32
33
34static u_int16_t
35cksum(u_int16_t* buf, int nwords)
36{
37	u_int32_t sum;
38	for (sum = 0; nwords > 0; nwords--)
39		sum += *(buf++);
40	sum = (sum >> 16) + (sum & 0xffff);
41	sum += (sum >> 16);
42	return ~sum;
43}
44
45
46static struct addrinfo*
47host_serv(const char* host, const char* serv, int family, int socktype)
48{
49	struct addrinfo hints, *res;
50	memset(&hints, 0, sizeof(struct addrinfo));
51	hints.ai_flags = AI_CANONNAME;
52	hints.ai_family = family;
53	hints.ai_socktype = socktype;
54
55	int n = getaddrinfo(host, serv, &hints, &res);
56	if (n != 0)
57		return NULL;
58
59	return res;
60}
61
62
63int
64main(int argc, char** argv)
65{
66	if (argc < 3) {
67		printf("Usage: %s <ip-address> <port> [nbytes]\n", argv[0]);
68		return 1;
69	}
70
71	size_t size;
72	if (argc == 3)
73		size = DGRM_SIZE;
74	else
75		size = atoi(argv[3]);
76
77	if (size - sizeof(struct ip) - sizeof(struct udphdr) < 0) {
78		fprintf(stderr, "Datagram size is too small\n");
79		return 1;
80	}
81
82	struct addrinfo* ai = host_serv(argv[1], NULL, 0, 0);
83	if (ai == NULL) {
84		fprintf(stderr, "Cannot resolve %s\n", argv[1]);
85		return 1;
86	}
87
88	printf("Using datagram size %zu\n", size);
89
90	char* datagram = (char*)malloc(size);
91	if (datagram == NULL) {
92		fprintf(stderr, "Out of memory.\n");
93		return 1;
94	}
95
96	memset(datagram, 0, size);
97
98	struct ip* iph = (struct ip*)datagram;
99	iph->ip_hl = 4;
100	iph->ip_v = 4;
101	iph->ip_tos = 0;
102	iph->ip_len = htons(size);
103	iph->ip_id = htonl(54321);
104	iph->ip_off = 0;
105	iph->ip_ttl = 255;
106	iph->ip_p = IPPROTO_ICMP;
107	iph->ip_sum = 0;
108	iph->ip_src.s_addr = INADDR_ANY;
109	iph->ip_dst.s_addr = ((struct sockaddr_in*)ai->ai_addr)->sin_addr.s_addr;
110	// Calculates IP checksum
111	iph->ip_sum = cksum((unsigned short*)datagram, iph->ip_len >> 1);
112
113	struct pseudo_header pHeader; // used to calculate udp checksum
114
115	struct udphdr* udp = (struct udphdr*)(datagram + sizeof(struct ip));
116	udp->uh_sport = 0;
117	udp->uh_dport = htons(atoi(argv[2]));
118	udp->uh_ulen = htons(size - sizeof(struct ip));
119	pHeader.src = iph->ip_src;
120	pHeader.dst = iph->ip_dst;
121	pHeader.zero = 0;
122	pHeader.protocol = iph->ip_p;
123	pHeader.len = udp->uh_ulen;
124	udp->uh_sum = cksum((u_int16_t*)&pHeader, 3);
125
126	// Send it via a raw socket
127	int sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
128	if (sockfd < 0) {
129		fprintf(stderr, "Failed to create raw socket: %s\n", strerror(errno));
130		free(datagram);
131		return 1;
132	}
133
134	int one = 1;
135	const int* val = &one;
136	if (setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, val, sizeof(int)) != 0) {
137		fprintf(stderr, "Failed to set IP_HDRINCL on socket: %s\n",
138			strerror(errno));
139		free(datagram);
140		return 1;
141	}
142
143	int bytesSent = sendto(sockfd, datagram, size, 0, ai->ai_addr,
144		ai->ai_addrlen);
145	if (bytesSent < 0) {
146		fprintf(stderr, "Failed to send the datagram via the raw socket: %s\n",
147			strerror(errno));
148		free(datagram);
149		return 1;
150	}
151
152	printf("Sent %d bytes.\n", bytesSent);
153	return 0;
154}
155
156