-
Notifications
You must be signed in to change notification settings - Fork 13
/
net.cpp
350 lines (301 loc) · 9.24 KB
/
net.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
/****************************************************************************
Program: $Id: net.cpp 14 2014-12-30 02:33:44Z laalt $
Date: $Date: 2014-12-29 18:33:44 -0800 (Mon, 29 Dec 2014) $
Description: networking routines
****************************************************************************/
#include "yarrp.h"
/**
* Prints a range of data in hex.
*
* @param buf Start of the data range
* @param len Length of the data range
* @param brk Number of bytes to print per line
* @param tabs Number of tabs to insert at the beginning of each new line
*/
void
print_binary(const unsigned char *buf, int len, int brk, int tabs) {
int i, j;
for (i = 0; i < len; i++) {
if ((i > 0) && (i % brk == 0)) {
printf("\n");
for (j = 0; j < tabs; j++)
printf("\t");
}
printf("%02X ", buf[i]);
}
printf("\n");
}
/**
* Prints each header (net, trnspt, data) of a packet in hex.
*
* @param packet Start of the buffer containing the packet
* @param tot_len Total length of the packet buffer
*/
void
print_packet(const unsigned char *packet, int tot_len) {
struct ip *ip;
struct tcphdr *tcp;
int iph_len, tcph_len, hdrs_len;
ip = (struct ip *)packet;
iph_len = ip->ip_hl << 2;
tcp = (struct tcphdr *)(packet + iph_len);
tcph_len = tcp->th_off << 2;
hdrs_len = iph_len + tcph_len;
printf("\tIP Header:\t");
print_binary((unsigned char *)ip, iph_len, 10, 3);
printf("\tTCP Header:\t");
print_binary((unsigned char *)tcp, tcph_len, 10, 3);
/* only print a data header if the packet has data */
if (tot_len > hdrs_len) {
printf("\tData:\t\t");
print_binary(packet + hdrs_len, tot_len - hdrs_len, 10, 3);
}
}
/**
* Create a new raw IPv4 socket.
*
* @param sin_orig Source port and address family (only used on PlanetLab)
* @return Descriptor for the newly created socket
*/
int
raw_sock(struct sockaddr_in *sin_orig) {
int sock, one = 1;
struct sockaddr_in sin;
/* create a new sin without the address */
memset(&sin, 0, sizeof sin);
sin.sin_family = sin_orig->sin_family;
sin.sin_port = sin_orig->sin_port;
/* establish raw socket */
if ((sock = socket(PF_INET, SOCK_RAW, IPPROTO_RAW)) < 0)
fatal("Create socket failed: %s", strerror(errno));
if (setsockopt(sock, 0, IP_HDRINCL, &one, sizeof one) < 0)
warn("setsockopt failed: %s", strerror(errno));
return sock;
}
/**
* Determine our public-facing IPv4 address.
*
* @param mei Where to save the address we find
* @return 1 for success, -1 for failure
*/
int
infer_my_ip(struct sockaddr_in *mei) {
struct sockaddr_in me, serv;
socklen_t len = sizeof me;
int sock, err;
memset(&serv, 0, sizeof serv);
serv.sin_family = AF_INET;
serv.sin_addr.s_addr = inet_addr("8.8.8.8");
serv.sin_port = htons(53);
sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock == -1)
return -1;
err = connect(sock, (const struct sockaddr *)&serv, sizeof serv);
if (err == -1)
return -1;
err = getsockname(sock, (struct sockaddr *)&me, &len);
if (err == -1)
return -1;
mei->sin_addr = me.sin_addr;
return 1;
}
/**
* Determine our public-facing IPv6 address.
*
* @param mei6 Where to save the address we find
* @return 1 for success, -1 for failure
*/
int
infer_my_ip6(struct sockaddr_in6 *mei6) {
struct sockaddr_in6 me6, serv6;
socklen_t len = sizeof me6;
int sock6, err;
memset(&serv6, 0, sizeof serv6);
serv6.sin6_family = AF_INET6;
inet_pton(AF_INET6, "2001:4860:4860::8888",
&serv6.sin6_addr.s6_addr);
serv6.sin6_port = htons(53);
sock6 = socket(AF_INET6, SOCK_DGRAM, 0);
if (sock6 == -1)
return -1;
err = connect(sock6, (const struct sockaddr *)&serv6, sizeof serv6);
if (err == -1)
return -1;
err = getsockname(sock6, (struct sockaddr *)&me6, &len);
if (err == -1)
return -1;
mei6->sin6_addr = me6.sin6_addr;
return 1;
}
/**
* Lookup the given address in DNS.
*
* @param url DNS or IPv4 address in string form
* @param target Where to save the IPv4 address we find
*/
void
resolve_target_ip(char *url, struct sockaddr_in *target) {
int error;
struct addrinfo hints, *result;
/* hints allows us to tell getaddrinfo what kind of answer we want */
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET; /* we want IPv4 addresses only */
/*
* we use getaddrinfo() because it can handle either DNS addresses or IPs
* as strings
*/
error = getaddrinfo(url, NULL, &hints, &result);
if (error)
fatal("Error in getaddrinfo: %s", gai_strerror(error));
/* just grab the first address in the linked list */
target->sin_addr = ((struct sockaddr_in *)result->ai_addr)->sin_addr;
freeaddrinfo(result);
}
/**
* Compute an IP checksum.
*
* @param addr Start of the data range
* @param len Length of the data range
* @return 2-byte long IP checksum value
*/
unsigned short
in_cksum(unsigned short *addr, int len) {
int nleft = len;
int sum = 0;
unsigned short *w = addr;
unsigned short answer = 0;
/*
* 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.
*/
assert(addr);
while (nleft > 1) {
sum += *w++;
nleft -= 2;
}
/* 4mop up an odd byte, if necessary */
if (nleft == 1) {
*(unsigned char *)(&answer) = *(unsigned char *)w;
sum += answer;
}
/* 4add 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;
}
/*
* struct ipovly seems to be a BSD only item... Defining it here for now, but
* there should be a better, more cross-platform way of doing this.
*/
#ifndef _BSD
struct ipovly {
u_char ih_x1;
u_char ih_pr;
u_short ih_len;
struct in_addr ih_src;
struct in_addr ih_dst;
};
#endif
struct ip6ovly {
struct in6_addr ip6_src;
struct in6_addr ip6_dst;
uint32_t ip6_len;
u_char ip6_zeros[3];
u_char ip6_pr;
};
/*
* Checksum routine for UDP and TCP headers.
*/
u_short
p_cksum(struct ip *ip, u_short * data, int len) {
static struct ipovly ipo;
u_short sumh, sumd;
u_long sumt;
ipo.ih_pr = ip->ip_p;
ipo.ih_len = htons(len);
ipo.ih_src = ip->ip_src;
ipo.ih_dst = ip->ip_dst;
sumh = in_cksum((u_short *) & ipo, sizeof(ipo)); /* pseudo ip hdr cksum */
sumd = in_cksum((u_short *) data, len); /* payload data cksum */
sumt = (sumh << 16) | (sumd);
return ~in_cksum((u_short *) & sumt, sizeof(sumt));
}
/*
* IPv6 checksum routine for UDP/TCP/ICMP (Section 8.1 of RFC 2460).
*/
u_short
p_cksum(struct ip6_hdr *ip6, u_short * data, int len) {
static struct ip6ovly ipo;
u_short sumh, sumd;
u_long sumt;
ipo.ip6_pr = ip6->ip6_nxt;
ipo.ip6_len = htons(len);
ipo.ip6_src = ip6->ip6_src;
ipo.ip6_dst = ip6->ip6_dst;
sumh = in_cksum((u_short *) & ipo, sizeof(ipo)); /* pseudo ip hdr cksum */
sumd = in_cksum((u_short *) data, len); /* payload data cksum */
sumt = (sumh << 16) | (sumd);
return ~in_cksum((u_short *) & sumt, sizeof(sumt));
}
/*
* Compute packet payload needed to make UDP checksum correct
* (Used to ensure Paris-style load balancing
*/
unsigned short compute_data(unsigned short start_cksum, unsigned short target_cksum) {
unsigned short answer = 0x0000;
/* per RFC, if computed checksum is 0, the value 0xFFFF is transmitted */
if (target_cksum == 0xFFFF)
target_cksum = 0x0000;
/* if the ones' complement of the target checksum is greater than
* the ones' complement of the starting checksum, use the overflow
* in the computation of IP/UDP checksum to keep result positive
*/
if (~target_cksum > ~start_cksum)
answer = ~target_cksum - (~start_cksum);
else
answer = 0xFFFF - (~start_cksum) + (~target_cksum);
return answer;
}
/* @@ RB: Gaston v6 code only supports Linux */
#ifdef _LINUX
/**
* Create a new raw IPv6 socket.
*
* @param sin_orig Source port and address family
* @return Descriptor for the newly created socket
*/
int
raw_sock6(struct sockaddr_in6 *sin6_orig) {
int sock, one = 1;
struct sockaddr_in6 sin6;
/* create a new sin without the address */
memset(&sin6, 0, sizeof sin6);
sin6.sin6_family = sin6_orig->sin6_family;
sin6.sin6_port = sin6_orig->sin6_port;
/* establish raw socket */
if ((sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL))) < 0)
fatal("Create socket failed: %s", strerror(errno));
return sock;
}
#endif
/* BPF dev finder/opener */
int bpfget() {
char dev[32];
int fd = -1;
for (int i=0; i < 255; i++) {
snprintf(dev, sizeof(dev), "/dev/bpf%u", i);
//printf("trying to open %s\n", dev);
fd = open(dev, O_RDWR);
if (fd > -1) return fd;
switch (errno) {
case EBUSY:
break;
default:
return -1;
}
}
errno = ENOENT;
return -1;
}