1 |
/* |
2 |
* Copyright (C) 2004-2007 Anders Gavare. All rights reserved. |
3 |
* |
4 |
* Redistribution and use in source and binary forms, with or without |
5 |
* modification, are permitted provided that the following conditions are met: |
6 |
* |
7 |
* 1. Redistributions of source code must retain the above copyright |
8 |
* notice, this list of conditions and the following disclaimer. |
9 |
* 2. Redistributions in binary form must reproduce the above copyright |
10 |
* notice, this list of conditions and the following disclaimer in the |
11 |
* documentation and/or other materials provided with the distribution. |
12 |
* 3. The name of the author may not be used to endorse or promote products |
13 |
* derived from this software without specific prior written permission. |
14 |
* |
15 |
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
16 |
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
17 |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
18 |
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
19 |
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
20 |
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
21 |
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
22 |
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
23 |
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
24 |
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
25 |
* SUCH DAMAGE. |
26 |
* |
27 |
* |
28 |
* $Id: net_ip.c,v 1.5 2006/12/30 13:31:02 debug Exp $ |
29 |
* |
30 |
* Internet Protocol related networking stuff. |
31 |
*/ |
32 |
|
33 |
#include <stdio.h> |
34 |
#include <stdlib.h> |
35 |
#include <string.h> |
36 |
#include <unistd.h> |
37 |
#include <sys/socket.h> |
38 |
#include <sys/time.h> |
39 |
#include <sys/types.h> |
40 |
#include <errno.h> |
41 |
#include <fcntl.h> |
42 |
|
43 |
#include "misc.h" |
44 |
#include "net.h" |
45 |
|
46 |
|
47 |
/* #define debug fatal */ |
48 |
|
49 |
|
50 |
/* |
51 |
* net_ip_checksum(): |
52 |
* |
53 |
* Fill in an IP header checksum. (This works for ICMP too.) |
54 |
* chksumoffset should be 10 for IP headers, and len = 20. |
55 |
* For ICMP packets, chksumoffset = 2 and len = length of the ICMP packet. |
56 |
*/ |
57 |
void net_ip_checksum(unsigned char *ip_header, int chksumoffset, int len) |
58 |
{ |
59 |
int i; |
60 |
uint32_t sum = 0; |
61 |
|
62 |
for (i=0; i<len; i+=2) |
63 |
if (i != chksumoffset) { |
64 |
uint16_t w = (ip_header[i] << 8) + ip_header[i+1]; |
65 |
sum += w; |
66 |
while (sum > 65535) { |
67 |
int to_add = sum >> 16; |
68 |
sum = (sum & 0xffff) + to_add; |
69 |
} |
70 |
} |
71 |
|
72 |
sum ^= 0xffff; |
73 |
ip_header[chksumoffset + 0] = sum >> 8; |
74 |
ip_header[chksumoffset + 1] = sum & 0xff; |
75 |
} |
76 |
|
77 |
|
78 |
/* |
79 |
* net_ip_tcp_checksum(): |
80 |
* |
81 |
* Fill in a TCP header checksum. This differs slightly from the IP |
82 |
* checksum. The checksum is calculated on a pseudo header, the actual |
83 |
* TCP header, and the data. This is what the pseudo header looks like: |
84 |
* |
85 |
* uint32_t srcaddr; |
86 |
* uint32_t dstaddr; |
87 |
* uint16_t protocol; (= 6 for tcp) |
88 |
* uint16_t tcp_len; |
89 |
* |
90 |
* tcp_len is length of header PLUS data. The psedo header is created |
91 |
* internally here, and does not need to be supplied by the caller. |
92 |
*/ |
93 |
void net_ip_tcp_checksum(unsigned char *tcp_header, int chksumoffset, |
94 |
int tcp_len, unsigned char *srcaddr, unsigned char *dstaddr, |
95 |
int udpflag) |
96 |
{ |
97 |
int i, pad = 0; |
98 |
unsigned char pseudoh[12]; |
99 |
uint32_t sum = 0; |
100 |
|
101 |
memcpy(pseudoh + 0, srcaddr, 4); |
102 |
memcpy(pseudoh + 4, dstaddr, 4); |
103 |
pseudoh[8] = 0x00; |
104 |
pseudoh[9] = udpflag? 17 : 6; |
105 |
pseudoh[10] = tcp_len >> 8; |
106 |
pseudoh[11] = tcp_len & 255; |
107 |
|
108 |
for (i=0; i<12; i+=2) { |
109 |
uint16_t w = (pseudoh[i] << 8) + pseudoh[i+1]; |
110 |
sum += w; |
111 |
while (sum > 65535) { |
112 |
int to_add = sum >> 16; |
113 |
sum = (sum & 0xffff) + to_add; |
114 |
} |
115 |
} |
116 |
|
117 |
if (tcp_len & 1) { |
118 |
tcp_len ++; |
119 |
pad = 1; |
120 |
} |
121 |
|
122 |
for (i=0; i<tcp_len; i+=2) |
123 |
if (i != chksumoffset) { |
124 |
uint16_t w; |
125 |
if (!pad || i < tcp_len-2) |
126 |
w = (tcp_header[i] << 8) + tcp_header[i+1]; |
127 |
else |
128 |
w = (tcp_header[i] << 8) + 0x00; |
129 |
sum += w; |
130 |
while (sum > 65535) { |
131 |
int to_add = sum >> 16; |
132 |
sum = (sum & 0xffff) + to_add; |
133 |
} |
134 |
} |
135 |
|
136 |
sum ^= 0xffff; |
137 |
tcp_header[chksumoffset + 0] = sum >> 8; |
138 |
tcp_header[chksumoffset + 1] = sum & 0xff; |
139 |
} |
140 |
|
141 |
|
142 |
/* |
143 |
* net_ip_icmp(): |
144 |
* |
145 |
* Handle an ICMP packet. |
146 |
* |
147 |
* The IP header (at offset 14) could look something like |
148 |
* |
149 |
* ver=45 tos=00 len=0054 id=001a ofs=0000 ttl=ff p=01 sum=a87e |
150 |
* src=0a000005 dst=03050607 |
151 |
* |
152 |
* and the ICMP specific data (beginning at offset 34): |
153 |
* |
154 |
* type=08 code=00 chksum=b8bf |
155 |
* 000c0008d5cee94089190c0008090a0b |
156 |
* 0c0d0e0f101112131415161718191a1b |
157 |
* 1c1d1e1f202122232425262728292a2b |
158 |
* 2c2d2e2f3031323334353637 |
159 |
*/ |
160 |
static void net_ip_icmp(struct net *net, void *extra, |
161 |
unsigned char *packet, int len) |
162 |
{ |
163 |
int type; |
164 |
struct ethernet_packet_link *lp; |
165 |
|
166 |
type = packet[34]; |
167 |
|
168 |
switch (type) { |
169 |
case 8: /* ECHO request */ |
170 |
debug("[ ICMP echo ]\n"); |
171 |
lp = net_allocate_ethernet_packet_link(net, extra, len); |
172 |
|
173 |
/* Copy the old packet first: */ |
174 |
memcpy(lp->data + 12, packet + 12, len - 12); |
175 |
|
176 |
/* Switch to and from ethernet addresses: */ |
177 |
memcpy(lp->data + 0, packet + 6, 6); |
178 |
memcpy(lp->data + 6, packet + 0, 6); |
179 |
|
180 |
/* Switch to and from IP addresses: */ |
181 |
memcpy(lp->data + 26, packet + 30, 4); |
182 |
memcpy(lp->data + 30, packet + 26, 4); |
183 |
|
184 |
/* Change from echo REQUEST to echo REPLY: */ |
185 |
lp->data[34] = 0x00; |
186 |
|
187 |
/* Decrease the TTL to a low value: */ |
188 |
lp->data[22] = 2; |
189 |
|
190 |
/* Recalculate ICMP checksum: */ |
191 |
net_ip_checksum(lp->data + 34, 2, len - 34); |
192 |
|
193 |
/* Recalculate IP header checksum: */ |
194 |
net_ip_checksum(lp->data + 14, 10, 20); |
195 |
|
196 |
break; |
197 |
default: |
198 |
fatal("[ net: ICMP type %i not yet implemented ]\n", type); |
199 |
} |
200 |
} |
201 |
|
202 |
|
203 |
/* |
204 |
* tcp_closeconnection(): |
205 |
* |
206 |
* Helper function which closes down a TCP connection completely. |
207 |
*/ |
208 |
static void tcp_closeconnection(struct net *net, int con_id) |
209 |
{ |
210 |
close(net->tcp_connections[con_id].socket); |
211 |
net->tcp_connections[con_id].state = TCP_OUTSIDE_DISCONNECTED; |
212 |
net->tcp_connections[con_id].in_use = 0; |
213 |
net->tcp_connections[con_id].incoming_buf_len = 0; |
214 |
} |
215 |
|
216 |
|
217 |
/* |
218 |
* net_ip_tcp_connectionreply(): |
219 |
* |
220 |
* When changing from state _TRYINGTOCONNECT to _CONNECTED, then this |
221 |
* function should be called with connecting set to 1. |
222 |
* |
223 |
* To send a generic ack reply, set connecting to 0. |
224 |
* |
225 |
* To send data (PSH), set data to non-NULL and datalen to the length. |
226 |
* |
227 |
* This creates an ethernet packet for the guest OS with an ACK to the |
228 |
* initial SYN packet. |
229 |
*/ |
230 |
void net_ip_tcp_connectionreply(struct net *net, void *extra, |
231 |
int con_id, int connecting, unsigned char *data, int datalen, int rst) |
232 |
{ |
233 |
struct ethernet_packet_link *lp; |
234 |
int tcp_length, ip_len, option_len = 20; |
235 |
|
236 |
if (connecting) |
237 |
net->tcp_connections[con_id].outside_acknr = |
238 |
net->tcp_connections[con_id].inside_seqnr + 1; |
239 |
|
240 |
net->tcp_connections[con_id].tcp_id ++; |
241 |
tcp_length = 20 + option_len + datalen; |
242 |
ip_len = 20 + tcp_length; |
243 |
lp = net_allocate_ethernet_packet_link(net, extra, 14 + ip_len); |
244 |
|
245 |
/* Ethernet header: */ |
246 |
memcpy(lp->data + 0, net->tcp_connections[con_id].ethernet_address, 6); |
247 |
memcpy(lp->data + 6, net->gateway_ethernet_addr, 6); |
248 |
lp->data[12] = 0x08; /* IP = 0x0800 */ |
249 |
lp->data[13] = 0x00; |
250 |
|
251 |
/* IP header: */ |
252 |
lp->data[14] = 0x45; /* ver */ |
253 |
lp->data[15] = 0x10; /* tos */ |
254 |
lp->data[16] = ip_len >> 8; |
255 |
lp->data[17] = ip_len & 0xff; |
256 |
lp->data[18] = net->tcp_connections[con_id].tcp_id >> 8; |
257 |
lp->data[19] = net->tcp_connections[con_id].tcp_id & 0xff; |
258 |
lp->data[20] = 0x40; /* don't fragment */ |
259 |
lp->data[21] = 0x00; |
260 |
lp->data[22] = 0x40; /* ttl */ |
261 |
lp->data[23] = 6; /* p = TCP */ |
262 |
memcpy(lp->data + 26, net->tcp_connections[con_id]. |
263 |
outside_ip_address, 4); |
264 |
memcpy(lp->data + 30, net->tcp_connections[con_id]. |
265 |
inside_ip_address, 4); |
266 |
net_ip_checksum(lp->data + 14, 10, 20); |
267 |
|
268 |
/* TCP header and options at offset 34: */ |
269 |
lp->data[34] = net->tcp_connections[con_id].outside_tcp_port >> 8; |
270 |
lp->data[35] = net->tcp_connections[con_id].outside_tcp_port & 0xff; |
271 |
lp->data[36] = net->tcp_connections[con_id].inside_tcp_port >> 8; |
272 |
lp->data[37] = net->tcp_connections[con_id].inside_tcp_port & 0xff; |
273 |
lp->data[38] = (net->tcp_connections[con_id]. |
274 |
outside_seqnr >> 24) & 0xff; |
275 |
lp->data[39] = (net->tcp_connections[con_id]. |
276 |
outside_seqnr >> 16) & 0xff; |
277 |
lp->data[40] = (net->tcp_connections[con_id]. |
278 |
outside_seqnr >> 8) & 0xff; |
279 |
lp->data[41] = net->tcp_connections[con_id]. |
280 |
outside_seqnr & 0xff; |
281 |
lp->data[42] = (net->tcp_connections[con_id]. |
282 |
outside_acknr >> 24) & 0xff; |
283 |
lp->data[43] = (net->tcp_connections[con_id]. |
284 |
outside_acknr >> 16) & 0xff; |
285 |
lp->data[44] = (net->tcp_connections[con_id]. |
286 |
outside_acknr >> 8) & 0xff; |
287 |
lp->data[45] = net->tcp_connections[con_id].outside_acknr & 0xff; |
288 |
|
289 |
/* Control */ |
290 |
lp->data[46] = (option_len + 20) / 4 * 0x10; |
291 |
lp->data[47] = 0x10; /* ACK */ |
292 |
if (connecting) |
293 |
lp->data[47] |= 0x02; /* SYN */ |
294 |
if (net->tcp_connections[con_id].state == TCP_OUTSIDE_CONNECTED) |
295 |
lp->data[47] |= 0x08; /* PSH */ |
296 |
if (rst) |
297 |
lp->data[47] |= 0x04; /* RST */ |
298 |
if (net->tcp_connections[con_id].state >= TCP_OUTSIDE_DISCONNECTED) |
299 |
lp->data[47] |= 0x01; /* FIN */ |
300 |
|
301 |
/* Window */ |
302 |
lp->data[48] = 0x10; |
303 |
lp->data[49] = 0x00; |
304 |
|
305 |
/* no urgent ptr */ |
306 |
|
307 |
/* options */ |
308 |
/* TODO: HAHA, this is ugly */ |
309 |
lp->data[54] = 0x02; |
310 |
lp->data[55] = 0x04; |
311 |
lp->data[56] = 0x05; |
312 |
lp->data[57] = 0xb4; |
313 |
lp->data[58] = 0x01; |
314 |
lp->data[59] = 0x03; |
315 |
lp->data[60] = 0x03; |
316 |
lp->data[61] = 0x00; |
317 |
lp->data[62] = 0x01; |
318 |
lp->data[63] = 0x01; |
319 |
lp->data[64] = 0x08; |
320 |
lp->data[65] = 0x0a; |
321 |
lp->data[66] = (net->timestamp >> 24) & 0xff; |
322 |
lp->data[67] = (net->timestamp >> 16) & 0xff; |
323 |
lp->data[68] = (net->timestamp >> 8) & 0xff; |
324 |
lp->data[69] = net->timestamp & 0xff; |
325 |
lp->data[70] = (net->tcp_connections[con_id]. |
326 |
inside_timestamp >> 24) & 0xff; |
327 |
lp->data[71] = (net->tcp_connections[con_id]. |
328 |
inside_timestamp >> 16) & 0xff; |
329 |
lp->data[72] = (net->tcp_connections[con_id]. |
330 |
inside_timestamp >> 8) & 0xff; |
331 |
lp->data[73] = net->tcp_connections[con_id]. |
332 |
inside_timestamp & 0xff; |
333 |
|
334 |
/* data: */ |
335 |
if (data != NULL) { |
336 |
memcpy(lp->data + 74, data, datalen); |
337 |
net->tcp_connections[con_id].outside_seqnr += datalen; |
338 |
} |
339 |
|
340 |
/* Checksum: */ |
341 |
net_ip_tcp_checksum(lp->data + 34, 16, tcp_length, |
342 |
lp->data + 26, lp->data + 30, 0); |
343 |
|
344 |
#if 0 |
345 |
{ |
346 |
int i; |
347 |
fatal("[ net_ip_tcp_connectionreply(%i): ", connecting); |
348 |
for (i=0; i<ip_len+14; i++) |
349 |
fatal("%02x", lp->data[i]); |
350 |
fatal(" ]\n"); |
351 |
} |
352 |
#endif |
353 |
|
354 |
if (connecting) |
355 |
net->tcp_connections[con_id].outside_seqnr ++; |
356 |
} |
357 |
|
358 |
|
359 |
/* |
360 |
* net_ip_tcp(): |
361 |
* |
362 |
* Handle a TCP packet comming from the emulated OS. |
363 |
* |
364 |
* The IP header (at offset 14) could look something like |
365 |
* |
366 |
* ver=45 tos=00 len=003c id=0006 ofs=0000 ttl=40 p=11 sum=b798 |
367 |
* src=0a000001 dst=c1abcdef |
368 |
* |
369 |
* TCP header, at offset 34: |
370 |
* |
371 |
* srcport=fffe dstport=0015 seqnr=af419a1d acknr=00000000 |
372 |
* control=a002 window=4000 checksum=fe58 urgent=0000 |
373 |
* and then "options and padding" and then data. |
374 |
* (020405b4010303000101080a0000000000000000) |
375 |
* |
376 |
* See the following URLs for good descriptions of TCP: |
377 |
* |
378 |
* http://www.networksorcery.com/enp/protocol/tcp.htm |
379 |
* http://www.tcpipguide.com/free/t_TCPIPTransmissionControlProtocolTCP.htm |
380 |
*/ |
381 |
static void net_ip_tcp(struct net *net, void *extra, |
382 |
unsigned char *packet, int len) |
383 |
{ |
384 |
int con_id, free_con_id, i, res; |
385 |
int srcport, dstport, data_offset, window, checksum, urgptr; |
386 |
int syn, ack, psh, rst, urg, fin; |
387 |
uint32_t seqnr, acknr; |
388 |
struct sockaddr_in remote_ip; |
389 |
fd_set rfds; |
390 |
struct timeval tv; |
391 |
int send_ofs; |
392 |
|
393 |
#if 0 |
394 |
fatal("[ net: TCP: "); |
395 |
for (i=0; i<26; i++) |
396 |
fatal("%02x", packet[i]); |
397 |
fatal(" "); |
398 |
#endif |
399 |
|
400 |
srcport = (packet[34] << 8) + packet[35]; |
401 |
dstport = (packet[36] << 8) + packet[37]; |
402 |
|
403 |
seqnr = (packet[38] << 24) + (packet[39] << 16) |
404 |
+ (packet[40] << 8) + packet[41]; |
405 |
acknr = (packet[42] << 24) + (packet[43] << 16) |
406 |
+ (packet[44] << 8) + packet[45]; |
407 |
|
408 |
#if 0 |
409 |
fatal("%i.%i.%i.%i:%i -> %i.%i.%i.%i:%i, seqnr=%lli acknr=%lli ", |
410 |
packet[26], packet[27], packet[28], packet[29], srcport, |
411 |
packet[30], packet[31], packet[32], packet[33], dstport, |
412 |
(long long)seqnr, (long long)acknr); |
413 |
#endif |
414 |
|
415 |
data_offset = (packet[46] >> 4) * 4 + 34; |
416 |
/* data_offset is now data offset within packet :-) */ |
417 |
|
418 |
urg = packet[47] & 32; |
419 |
ack = packet[47] & 16; |
420 |
psh = packet[47] & 8; |
421 |
rst = packet[47] & 4; |
422 |
syn = packet[47] & 2; |
423 |
fin = packet[47] & 1; |
424 |
window = (packet[48] << 8) + packet[49]; |
425 |
checksum = (packet[50] << 8) + packet[51]; |
426 |
urgptr = (packet[52] << 8) + packet[53]; |
427 |
|
428 |
#if 0 |
429 |
fatal(urg? "URG " : ""); |
430 |
fatal(ack? "ACK " : ""); |
431 |
fatal(psh? "PSH " : ""); |
432 |
fatal(rst? "RST " : ""); |
433 |
fatal(syn? "SYN " : ""); |
434 |
fatal(fin? "FIN " : ""); |
435 |
|
436 |
fatal("window=0x%04x checksum=0x%04x urgptr=0x%04x ", |
437 |
window, checksum, urgptr); |
438 |
|
439 |
fatal("options="); |
440 |
for (i=34+20; i<data_offset; i++) |
441 |
fatal("%02x", packet[i]); |
442 |
|
443 |
fatal(" data="); |
444 |
for (i=data_offset; i<len; i++) |
445 |
fatal("%02x", packet[i]); |
446 |
|
447 |
fatal(" ]\n"); |
448 |
#endif |
449 |
|
450 |
net_ip_tcp_checksum(packet + 34, 16, len - 34, |
451 |
packet + 26, packet + 30, 0); |
452 |
if (packet[50] * 256 + packet[51] != checksum) { |
453 |
debug("TCP: dropping packet because of checksum mismatch " |
454 |
"(0x%04x != 0x%04x)\n", packet[50] * 256 + packet[51], |
455 |
checksum); |
456 |
|
457 |
return; |
458 |
} |
459 |
|
460 |
/* Does this packet belong to a current connection? */ |
461 |
con_id = free_con_id = -1; |
462 |
for (i=0; i<MAX_TCP_CONNECTIONS; i++) { |
463 |
if (!net->tcp_connections[i].in_use) |
464 |
free_con_id = i; |
465 |
if (net->tcp_connections[i].in_use && |
466 |
net->tcp_connections[i].inside_tcp_port == srcport && |
467 |
net->tcp_connections[i].outside_tcp_port == dstport && |
468 |
memcmp(net->tcp_connections[i].inside_ip_address, |
469 |
packet + 26, 4) == 0 && |
470 |
memcmp(net->tcp_connections[i].outside_ip_address, |
471 |
packet + 30, 4) == 0) { |
472 |
con_id = i; |
473 |
break; |
474 |
} |
475 |
} |
476 |
|
477 |
/* |
478 |
* Unknown connection, and not SYN? Then drop the packet. |
479 |
* TODO: Send back RST? |
480 |
*/ |
481 |
if (con_id < 0 && !syn) { |
482 |
debug("[ net: TCP: dropping packet from unknown connection," |
483 |
" %i.%i.%i.%i:%i -> %i.%i.%i.%i:%i %s%s%s%s%s]\n", |
484 |
packet[26], packet[27], packet[28], packet[29], srcport, |
485 |
packet[30], packet[31], packet[32], packet[33], dstport, |
486 |
fin? "FIN ": "", syn? "SYN ": "", ack? "ACK ": "", |
487 |
psh? "PSH ": "", rst? "RST ": ""); |
488 |
return; |
489 |
} |
490 |
|
491 |
/* Known connection, and SYN? Then ignore the packet. */ |
492 |
if (con_id >= 0 && syn) { |
493 |
debug("[ net: TCP: ignoring redundant SYN packet from known" |
494 |
" connection, %i.%i.%i.%i:%i -> %i.%i.%i.%i:%i ]\n", |
495 |
packet[26], packet[27], packet[28], packet[29], srcport, |
496 |
packet[30], packet[31], packet[32], packet[33], dstport); |
497 |
return; |
498 |
} |
499 |
|
500 |
/* |
501 |
* A new outgoing connection? |
502 |
*/ |
503 |
if (con_id < 0 && syn) { |
504 |
debug("[ net: TCP: new outgoing connection, %i.%i.%i.%i:%i" |
505 |
" -> %i.%i.%i.%i:%i ]\n", |
506 |
packet[26], packet[27], packet[28], packet[29], srcport, |
507 |
packet[30], packet[31], packet[32], packet[33], dstport); |
508 |
|
509 |
/* Find a free connection id to use: */ |
510 |
if (free_con_id < 0) { |
511 |
#if 1 |
512 |
/* |
513 |
* TODO: Reuse the oldest one currently in use, or |
514 |
* just drop the new connection attempt? Drop for now. |
515 |
*/ |
516 |
fatal("[ TOO MANY TCP CONNECTIONS IN USE! " |
517 |
"Increase MAX_TCP_CONNECTIONS! ]\n"); |
518 |
return; |
519 |
#else |
520 |
int i; |
521 |
int64_t oldest = net-> |
522 |
tcp_connections[0].last_used_timestamp; |
523 |
free_con_id = 0; |
524 |
|
525 |
fatal("[ NO FREE TCP SLOTS, REUSING OLDEST ONE ]\n"); |
526 |
for (i=0; i<MAX_TCP_CONNECTIONS; i++) |
527 |
if (net->tcp_connections[i]. |
528 |
last_used_timestamp < oldest) { |
529 |
oldest = net->tcp_connections[i]. |
530 |
last_used_timestamp; |
531 |
free_con_id = i; |
532 |
} |
533 |
tcp_closeconnection(net, free_con_id); |
534 |
#endif |
535 |
} |
536 |
|
537 |
con_id = free_con_id; |
538 |
memset(&net->tcp_connections[con_id], 0, |
539 |
sizeof(struct tcp_connection)); |
540 |
|
541 |
memcpy(net->tcp_connections[con_id].ethernet_address, |
542 |
packet + 6, 6); |
543 |
memcpy(net->tcp_connections[con_id].inside_ip_address, |
544 |
packet + 26, 4); |
545 |
net->tcp_connections[con_id].inside_tcp_port = srcport; |
546 |
memcpy(net->tcp_connections[con_id].outside_ip_address, |
547 |
packet + 30, 4); |
548 |
net->tcp_connections[con_id].outside_tcp_port = dstport; |
549 |
|
550 |
net->tcp_connections[con_id].socket = |
551 |
socket(AF_INET, SOCK_STREAM, 0); |
552 |
if (net->tcp_connections[con_id].socket < 0) { |
553 |
fatal("[ net: TCP: socket() returned %i ]\n", |
554 |
net->tcp_connections[con_id].socket); |
555 |
return; |
556 |
} |
557 |
|
558 |
debug("[ new tcp outgoing socket=%i ]\n", |
559 |
net->tcp_connections[con_id].socket); |
560 |
|
561 |
net->tcp_connections[con_id].in_use = 1; |
562 |
|
563 |
/* Set the socket to non-blocking: */ |
564 |
res = fcntl(net->tcp_connections[con_id].socket, F_GETFL); |
565 |
fcntl(net->tcp_connections[con_id].socket, F_SETFL, |
566 |
res | O_NONBLOCK); |
567 |
|
568 |
remote_ip.sin_family = AF_INET; |
569 |
memcpy((unsigned char *)&remote_ip.sin_addr, |
570 |
net->tcp_connections[con_id].outside_ip_address, 4); |
571 |
remote_ip.sin_port = htons( |
572 |
net->tcp_connections[con_id].outside_tcp_port); |
573 |
|
574 |
res = connect(net->tcp_connections[con_id].socket, |
575 |
(struct sockaddr *)&remote_ip, sizeof(remote_ip)); |
576 |
|
577 |
/* connect can return -1, and errno = EINPROGRESS |
578 |
as we might not have connected right away. */ |
579 |
|
580 |
net->tcp_connections[con_id].state = |
581 |
TCP_OUTSIDE_TRYINGTOCONNECT; |
582 |
|
583 |
net->tcp_connections[con_id].outside_acknr = 0; |
584 |
net->tcp_connections[con_id].outside_seqnr = |
585 |
((random() & 0xffff) << 16) + (random() & 0xffff); |
586 |
} |
587 |
|
588 |
if (rst) { |
589 |
debug("[ 'rst': disconnecting TCP connection %i ]\n", con_id); |
590 |
net_ip_tcp_connectionreply(net, extra, con_id, 0, NULL, 0, 1); |
591 |
tcp_closeconnection(net, con_id); |
592 |
return; |
593 |
} |
594 |
|
595 |
if (ack && net->tcp_connections[con_id].state |
596 |
== TCP_OUTSIDE_DISCONNECTED2) { |
597 |
debug("[ 'ack': guestOS's final termination of TCP " |
598 |
"connection %i ]\n", con_id); |
599 |
|
600 |
/* Send an RST? (TODO, this is wrong...) */ |
601 |
net_ip_tcp_connectionreply(net, extra, con_id, 0, NULL, 0, 1); |
602 |
|
603 |
/* ... and forget about this connection: */ |
604 |
tcp_closeconnection(net, con_id); |
605 |
return; |
606 |
} |
607 |
|
608 |
if (fin && net->tcp_connections[con_id].state |
609 |
== TCP_OUTSIDE_DISCONNECTED) { |
610 |
debug("[ 'fin': response to outside's disconnection of " |
611 |
"TCP connection %i ]\n", con_id); |
612 |
|
613 |
/* Send an ACK: */ |
614 |
net->tcp_connections[con_id].state = TCP_OUTSIDE_CONNECTED; |
615 |
net_ip_tcp_connectionreply(net, extra, con_id, 0, NULL, 0, 0); |
616 |
net->tcp_connections[con_id].state = TCP_OUTSIDE_DISCONNECTED2; |
617 |
return; |
618 |
} |
619 |
|
620 |
if (fin) { |
621 |
debug("[ 'fin': guestOS disconnecting TCP connection %i ]\n", |
622 |
con_id); |
623 |
|
624 |
/* Send ACK: */ |
625 |
net_ip_tcp_connectionreply(net, extra, con_id, 0, NULL, 0, 0); |
626 |
net->tcp_connections[con_id].state = TCP_OUTSIDE_DISCONNECTED2; |
627 |
|
628 |
/* Return and send FIN: */ |
629 |
goto ret; |
630 |
} |
631 |
|
632 |
if (ack) { |
633 |
debug("ACK %i bytes, inside_acknr=%u outside_seqnr=%u\n", |
634 |
net->tcp_connections[con_id].incoming_buf_len, |
635 |
net->tcp_connections[con_id].inside_acknr, |
636 |
net->tcp_connections[con_id].outside_seqnr); |
637 |
net->tcp_connections[con_id].inside_acknr = acknr; |
638 |
if (net->tcp_connections[con_id].inside_acknr == |
639 |
net->tcp_connections[con_id].outside_seqnr && |
640 |
net->tcp_connections[con_id].incoming_buf_len != 0) { |
641 |
debug(" all acked\n"); |
642 |
net->tcp_connections[con_id].incoming_buf_len = 0; |
643 |
} |
644 |
} |
645 |
|
646 |
net->tcp_connections[con_id].inside_seqnr = seqnr; |
647 |
|
648 |
/* TODO: This is hardcoded for a specific NetBSD packet: */ |
649 |
if (packet[34 + 30] == 0x08 && packet[34 + 31] == 0x0a) |
650 |
net->tcp_connections[con_id].inside_timestamp = |
651 |
(packet[34 + 32 + 0] << 24) + |
652 |
(packet[34 + 32 + 1] << 16) + |
653 |
(packet[34 + 32 + 2] << 8) + |
654 |
(packet[34 + 32 + 3] << 0); |
655 |
|
656 |
|
657 |
net->timestamp ++; |
658 |
net->tcp_connections[con_id].last_used_timestamp = net->timestamp; |
659 |
|
660 |
|
661 |
if (net->tcp_connections[con_id].state != TCP_OUTSIDE_CONNECTED) { |
662 |
debug("[ not connected to outside ]\n"); |
663 |
return; |
664 |
} |
665 |
|
666 |
|
667 |
if (data_offset >= len) |
668 |
return; |
669 |
|
670 |
|
671 |
/* |
672 |
* We are here if this is a known connection, and data is to be |
673 |
* transmitted to the outside world. |
674 |
*/ |
675 |
|
676 |
send_ofs = data_offset; |
677 |
send_ofs += ((int32_t)net->tcp_connections[con_id].outside_acknr |
678 |
- (int32_t)seqnr); |
679 |
#if 1 |
680 |
debug("[ %i bytes of tcp data to be sent, beginning at seqnr %u, ", |
681 |
len - data_offset, seqnr); |
682 |
debug("outside is at acknr %u ==> %i actual bytes to be sent ]\n", |
683 |
net->tcp_connections[con_id].outside_acknr, len - send_ofs); |
684 |
#endif |
685 |
|
686 |
/* Drop outgoing packet if the guest OS' seqnr is not |
687 |
the same as we have acked. (We have missed something, perhaps.) */ |
688 |
if (seqnr != net->tcp_connections[con_id].outside_acknr) { |
689 |
debug("!! outgoing TCP packet dropped (seqnr = %u, " |
690 |
"outside_acknr = %u)\n", seqnr, |
691 |
net->tcp_connections[con_id].outside_acknr); |
692 |
goto ret; |
693 |
} |
694 |
|
695 |
if (len - send_ofs > 0) { |
696 |
/* Is the socket available for output? */ |
697 |
FD_ZERO(&rfds); /* write */ |
698 |
FD_SET(net->tcp_connections[con_id].socket, &rfds); |
699 |
tv.tv_sec = tv.tv_usec = 0; |
700 |
errno = 0; |
701 |
res = select(net->tcp_connections[con_id].socket+1, |
702 |
NULL, &rfds, NULL, &tv); |
703 |
if (res < 1) { |
704 |
net->tcp_connections[con_id].state = |
705 |
TCP_OUTSIDE_DISCONNECTED; |
706 |
debug("[ TCP: disconnect on select for writing ]\n"); |
707 |
goto ret; |
708 |
} |
709 |
|
710 |
res = write(net->tcp_connections[con_id].socket, |
711 |
packet + send_ofs, len - send_ofs); |
712 |
|
713 |
if (res > 0) { |
714 |
net->tcp_connections[con_id].outside_acknr += res; |
715 |
} else if (errno == EAGAIN) { |
716 |
/* Just ignore this attempt. */ |
717 |
return; |
718 |
} else { |
719 |
debug("[ error writing %i bytes to TCP connection %i:" |
720 |
" errno = %i ]\n", len - send_ofs, con_id, errno); |
721 |
net->tcp_connections[con_id].state = |
722 |
TCP_OUTSIDE_DISCONNECTED; |
723 |
debug("[ TCP: disconnect on write() ]\n"); |
724 |
goto ret; |
725 |
} |
726 |
} |
727 |
|
728 |
ret: |
729 |
/* Send an ACK (or FIN) to the guest OS: */ |
730 |
net_ip_tcp_connectionreply(net, extra, con_id, 0, NULL, 0, 0); |
731 |
} |
732 |
|
733 |
|
734 |
/* |
735 |
* net_ip_udp(): |
736 |
* |
737 |
* Handle a UDP packet. |
738 |
* |
739 |
* (See http://www.networksorcery.com/enp/protocol/udp.htm.) |
740 |
* |
741 |
* The IP header (at offset 14) could look something like |
742 |
* |
743 |
* ver=45 tos=00 len=003c id=0006 ofs=0000 ttl=40 p=11 sum=b798 |
744 |
* src=0a000001 dst=c1abcdef |
745 |
* |
746 |
* and the UDP data (beginning at offset 34): |
747 |
* |
748 |
* srcport=fffc dstport=0035 length=0028 chksum=76b6 |
749 |
* 43e20100000100000000000003667470066e6574627364036f726700001c0001 |
750 |
*/ |
751 |
static void net_ip_udp(struct net *net, void *extra, |
752 |
unsigned char *packet, int len) |
753 |
{ |
754 |
int con_id, free_con_id, i, srcport, dstport, udp_len; |
755 |
ssize_t res; |
756 |
struct sockaddr_in remote_ip; |
757 |
|
758 |
if ((packet[20] & 0x3f) != 0) { |
759 |
fatal("[ net_ip_udp(): WARNING! fragmented UDP " |
760 |
"packet, TODO ]\n"); |
761 |
return; |
762 |
} |
763 |
|
764 |
srcport = (packet[34] << 8) + packet[35]; |
765 |
dstport = (packet[36] << 8) + packet[37]; |
766 |
udp_len = (packet[38] << 8) + packet[39]; |
767 |
/* chksum at offset 40 and 41 */ |
768 |
|
769 |
debug("[ net: UDP: "); |
770 |
debug("srcport=%i dstport=%i len=%i ", srcport, dstport, udp_len); |
771 |
for (i=42; i<len; i++) { |
772 |
if (packet[i] >= ' ' && packet[i] < 127) |
773 |
debug("%c", packet[i]); |
774 |
else |
775 |
debug("[%02x]", packet[i]); |
776 |
} |
777 |
debug(" ]\n"); |
778 |
|
779 |
/* Is this "connection" new, or a currently ongoing one? */ |
780 |
con_id = free_con_id = -1; |
781 |
for (i=0; i<MAX_UDP_CONNECTIONS; i++) { |
782 |
if (!net->udp_connections[i].in_use) |
783 |
free_con_id = i; |
784 |
if (net->udp_connections[i].in_use && |
785 |
net->udp_connections[i].inside_udp_port == srcport && |
786 |
net->udp_connections[i].outside_udp_port == dstport && |
787 |
memcmp(net->udp_connections[i].inside_ip_address, |
788 |
packet + 26, 4) == 0 && |
789 |
memcmp(net->udp_connections[i].outside_ip_address, |
790 |
packet + 30, 4) == 0) { |
791 |
con_id = i; |
792 |
break; |
793 |
} |
794 |
} |
795 |
|
796 |
debug("&& UDP connection is "); |
797 |
if (con_id >= 0) |
798 |
debug("ONGOING"); |
799 |
else { |
800 |
debug("NEW"); |
801 |
if (free_con_id < 0) { |
802 |
int i; |
803 |
int64_t oldest = net-> |
804 |
udp_connections[0].last_used_timestamp; |
805 |
free_con_id = 0; |
806 |
|
807 |
debug(", NO FREE SLOTS, REUSING OLDEST ONE"); |
808 |
for (i=0; i<MAX_UDP_CONNECTIONS; i++) |
809 |
if (net->udp_connections[i]. |
810 |
last_used_timestamp < oldest) { |
811 |
oldest = net->udp_connections[i]. |
812 |
last_used_timestamp; |
813 |
free_con_id = i; |
814 |
} |
815 |
close(net->udp_connections[free_con_id].socket); |
816 |
} |
817 |
con_id = free_con_id; |
818 |
memset(&net->udp_connections[con_id], 0, |
819 |
sizeof(struct udp_connection)); |
820 |
|
821 |
memcpy(net->udp_connections[con_id].ethernet_address, |
822 |
packet + 6, 6); |
823 |
memcpy(net->udp_connections[con_id].inside_ip_address, |
824 |
packet + 26, 4); |
825 |
net->udp_connections[con_id].inside_udp_port = srcport; |
826 |
memcpy(net->udp_connections[con_id].outside_ip_address, |
827 |
packet + 30, 4); |
828 |
net->udp_connections[con_id].outside_udp_port = dstport; |
829 |
|
830 |
net->udp_connections[con_id].socket = socket(AF_INET, |
831 |
SOCK_DGRAM, 0); |
832 |
if (net->udp_connections[con_id].socket < 0) { |
833 |
fatal("[ net: UDP: socket() returned %i ]\n", |
834 |
net->udp_connections[con_id].socket); |
835 |
return; |
836 |
} |
837 |
|
838 |
debug(" {socket=%i}", net->udp_connections[con_id].socket); |
839 |
|
840 |
net->udp_connections[con_id].in_use = 1; |
841 |
|
842 |
/* Set the socket to non-blocking: */ |
843 |
res = fcntl(net->udp_connections[con_id].socket, F_GETFL); |
844 |
fcntl(net->udp_connections[con_id].socket, F_SETFL, |
845 |
res | O_NONBLOCK); |
846 |
} |
847 |
|
848 |
debug(", connection id %i\n", con_id); |
849 |
|
850 |
net->timestamp ++; |
851 |
net->udp_connections[con_id].last_used_timestamp = net->timestamp; |
852 |
|
853 |
remote_ip.sin_family = AF_INET; |
854 |
memcpy((unsigned char *)&remote_ip.sin_addr, |
855 |
net->udp_connections[con_id].outside_ip_address, 4); |
856 |
|
857 |
/* |
858 |
* Special case for the nameserver: If a UDP packet is sent to |
859 |
* the gateway, it will be forwarded to the nameserver, if it is |
860 |
* known. |
861 |
*/ |
862 |
if (net->nameserver_known && |
863 |
memcmp(net->udp_connections[con_id].outside_ip_address, |
864 |
&net->gateway_ipv4_addr[0], 4) == 0) { |
865 |
memcpy((unsigned char *)&remote_ip.sin_addr, |
866 |
&net->nameserver_ipv4, 4); |
867 |
net->udp_connections[con_id].fake_ns = 1; |
868 |
} |
869 |
|
870 |
remote_ip.sin_port = htons( |
871 |
net->udp_connections[con_id].outside_udp_port); |
872 |
|
873 |
res = sendto(net->udp_connections[con_id].socket, packet + 42, |
874 |
len - 42, 0, (const struct sockaddr *)&remote_ip, |
875 |
sizeof(remote_ip)); |
876 |
|
877 |
if (res != len-42) |
878 |
debug("[ net: UDP: unable to send %i bytes ]\n", len-42); |
879 |
else |
880 |
debug("[ net: UDP: OK!!! ]\n"); |
881 |
} |
882 |
|
883 |
|
884 |
/* |
885 |
* net_ip(): |
886 |
* |
887 |
* Handle an IP packet, coming from the emulated NIC. |
888 |
*/ |
889 |
void net_ip(struct net *net, void *extra, unsigned char *packet, int len) |
890 |
{ |
891 |
#if 1 |
892 |
int i; |
893 |
|
894 |
debug("[ net: IP: "); |
895 |
debug("ver=%02x ", packet[14]); |
896 |
debug("tos=%02x ", packet[15]); |
897 |
debug("len=%02x%02x ", packet[16], packet[17]); |
898 |
debug("id=%02x%02x ", packet[18], packet[19]); |
899 |
debug("ofs=%02x%02x ", packet[20], packet[21]); |
900 |
debug("ttl=%02x ", packet[22]); |
901 |
debug("p=%02x ", packet[23]); |
902 |
debug("sum=%02x%02x ", packet[24], packet[25]); |
903 |
debug("src=%02x%02x%02x%02x ", |
904 |
packet[26], packet[27], packet[28], packet[29]); |
905 |
debug("dst=%02x%02x%02x%02x ", |
906 |
packet[30], packet[31], packet[32], packet[33]); |
907 |
for (i=34; i<len; i++) |
908 |
debug("%02x", packet[i]); |
909 |
debug(" ]\n"); |
910 |
#endif |
911 |
|
912 |
/* Cut off overflowing tail data: */ |
913 |
if (len > 14 + packet[16]*256 + packet[17]) |
914 |
len = 14 + packet[16]*256 + packet[17]; |
915 |
|
916 |
if (packet[14] == 0x45) { |
917 |
/* IPv4: */ |
918 |
switch (packet[23]) { |
919 |
case 1: /* ICMP */ |
920 |
net_ip_icmp(net, extra, packet, len); |
921 |
break; |
922 |
case 6: /* TCP */ |
923 |
net_ip_tcp(net, extra, packet, len); |
924 |
break; |
925 |
case 17:/* UDP */ |
926 |
net_ip_udp(net, extra, packet, len); |
927 |
break; |
928 |
default: |
929 |
fatal("[ net: IP: UNIMPLEMENTED protocol %i ]\n", |
930 |
packet[23]); |
931 |
} |
932 |
} else |
933 |
fatal("[ net: IP: UNIMPLEMENTED ip, first byte = 0x%02x ]\n", |
934 |
packet[14]); |
935 |
} |
936 |
|
937 |
|
938 |
/* |
939 |
* net_ip_broadcast_dhcp(): |
940 |
* |
941 |
* Handle an IPv4 DHCP broadcast packet, coming from the emulated NIC. |
942 |
* |
943 |
* Read http://www.ietf.org/rfc/rfc2131.txt for details on DHCP. |
944 |
* (And http://users.telenet.be/mydotcom/library/network/dhcp.htm.) |
945 |
*/ |
946 |
static void net_ip_broadcast_dhcp(struct net *net, void *extra, |
947 |
unsigned char *packet, int len) |
948 |
{ |
949 |
/* |
950 |
* TODO |
951 |
*/ |
952 |
#if 1 |
953 |
struct ethernet_packet_link *lp; |
954 |
int i; |
955 |
|
956 |
fatal("[ net: IPv4 DHCP: "); |
957 |
#if 1 |
958 |
fatal("ver=%02x ", packet[14]); |
959 |
fatal("tos=%02x ", packet[15]); |
960 |
fatal("len=%02x%02x ", packet[16], packet[17]); |
961 |
fatal("id=%02x%02x ", packet[18], packet[19]); |
962 |
fatal("ofs=%02x%02x ", packet[20], packet[21]); |
963 |
fatal("ttl=%02x ", packet[22]); |
964 |
fatal("p=%02x ", packet[23]); |
965 |
fatal("sum=%02x%02x ", packet[24], packet[25]); |
966 |
#endif |
967 |
fatal("src=%02x%02x%02x%02x ", |
968 |
packet[26], packet[27], packet[28], packet[29]); |
969 |
fatal("dst=%02x%02x%02x%02x ", |
970 |
packet[30], packet[31], packet[32], packet[33]); |
971 |
#if 0 |
972 |
for (i=34; i<len; i++) |
973 |
fatal("%02x", packet[i]); |
974 |
#endif |
975 |
|
976 |
if (len < 34 + 8 + 236) { |
977 |
fatal("[ DHCP packet too short? Len=%i ]\n", len); |
978 |
return; |
979 |
} |
980 |
|
981 |
/* |
982 |
* UDP data (at offset 34): |
983 |
* |
984 |
* srcport=0044 dstport=0043 length=0134 chksum=a973 |
985 |
* data = 01010600d116d276000000000000000000000000000000 |
986 |
* 0000000000102030405060...0000...638253633501...000 |
987 |
*/ |
988 |
|
989 |
fatal("op=%02x ", packet[42]); |
990 |
fatal("htype=%02x ", packet[43]); |
991 |
fatal("hlen=%02x ", packet[44]); |
992 |
fatal("hops=%02x ", packet[45]); |
993 |
fatal("xid=%02x%02x%02x%02x ", packet[46], packet[47], |
994 |
packet[48], packet[49]); |
995 |
fatal("secs=%02x%02x ", packet[50], packet[51]); |
996 |
fatal("flags=%02x%02x ", packet[52], packet[53]); |
997 |
fatal("ciaddr=%02x%02x%02x%02x ", packet[54], packet[55], |
998 |
packet[56], packet[57]); |
999 |
fatal("yiaddr=%02x%02x%02x%02x ", packet[58], packet[59], |
1000 |
packet[60], packet[61]); |
1001 |
fatal("siaddr=%02x%02x%02x%02x ", packet[62], packet[63], |
1002 |
packet[64], packet[65]); |
1003 |
fatal("giaddr=%02x%02x%02x%02x ", packet[66], packet[67], |
1004 |
packet[68], packet[69]); |
1005 |
fatal("chaddr="); |
1006 |
for (i=70; i<70+16; i++) |
1007 |
fatal("%02x", packet[i]); |
1008 |
/* |
1009 |
| sname (64) | |
1010 |
| file (128) | |
1011 |
*/ |
1012 |
fatal(" ]\n"); |
1013 |
|
1014 |
lp = net_allocate_ethernet_packet_link(net, extra, len); |
1015 |
|
1016 |
/* Copy the old packet first: */ |
1017 |
memcpy(lp->data, packet, len); |
1018 |
|
1019 |
/* We are sending to the client, from the gateway: */ |
1020 |
memcpy(lp->data + 0, packet + 6, 6); |
1021 |
memcpy(lp->data + 6, net->gateway_ethernet_addr, 6); |
1022 |
|
1023 |
memcpy(lp->data + 26, &net->gateway_ipv4_addr[0], 4); |
1024 |
lp->data[30] = 0xff; |
1025 |
lp->data[31] = 0xff; |
1026 |
lp->data[32] = 0xff; |
1027 |
lp->data[33] = 0xff; |
1028 |
|
1029 |
/* Switch src and dst ports: */ |
1030 |
memcpy(lp->data + 34, packet + 36, 2); |
1031 |
memcpy(lp->data + 36, packet + 34, 2); |
1032 |
|
1033 |
/* Client's (yiaddr) IPv4 address: */ |
1034 |
lp->data[58] = 10; |
1035 |
lp->data[59] = 0; |
1036 |
lp->data[60] = 0; |
1037 |
lp->data[61] = 1; |
1038 |
|
1039 |
/* Server's IPv4 address: (giaddr) */ |
1040 |
memcpy(lp->data + 66, &net->gateway_ipv4_addr[0], 4); |
1041 |
|
1042 |
/* This is a Reply: */ |
1043 |
lp->data[42] = 0x02; |
1044 |
|
1045 |
snprintf((char *)lp->data + 70+16+64, 8, "gxemul"); |
1046 |
|
1047 |
/* Recalculate IP header checksum: */ |
1048 |
net_ip_checksum(lp->data + 14, 10, 20); |
1049 |
|
1050 |
/* ... and the UDP checksum: */ |
1051 |
net_ip_tcp_checksum(lp->data + 34, 6, len - 34 - 8, |
1052 |
lp->data + 26, lp->data + 30, 1); |
1053 |
|
1054 |
|
1055 |
/* Debug dump: */ |
1056 |
packet = lp->data; |
1057 |
fatal("[ net: IPv4 DHCP REPLY: "); |
1058 |
for (i=0; i<14; i++) |
1059 |
fatal("%02x", packet[i]); |
1060 |
fatal("ver=%02x ", packet[14]); |
1061 |
fatal("tos=%02x ", packet[15]); |
1062 |
fatal("len=%02x%02x ", packet[16], packet[17]); |
1063 |
fatal("id=%02x%02x ", packet[18], packet[19]); |
1064 |
fatal("ofs=%02x%02x ", packet[20], packet[21]); |
1065 |
fatal("ttl=%02x ", packet[22]); |
1066 |
fatal("p=%02x ", packet[23]); |
1067 |
fatal("sum=%02x%02x ", packet[24], packet[25]); |
1068 |
fatal("src=%02x%02x%02x%02x ", |
1069 |
packet[26], packet[27], packet[28], packet[29]); |
1070 |
fatal("dst=%02x%02x%02x%02x ", |
1071 |
packet[30], packet[31], packet[32], packet[33]); |
1072 |
fatal("op=%02x ", packet[42]); |
1073 |
fatal("htype=%02x ", packet[43]); |
1074 |
fatal("hlen=%02x ", packet[44]); |
1075 |
fatal("hops=%02x ", packet[45]); |
1076 |
fatal("xid=%02x%02x%02x%02x ", packet[46], packet[47], |
1077 |
packet[48], packet[49]); |
1078 |
fatal("secs=%02x%02x ", packet[50], packet[51]); |
1079 |
fatal("flags=%02x%02x ", packet[52], packet[53]); |
1080 |
fatal("ciaddr=%02x%02x%02x%02x ", packet[54], packet[55], |
1081 |
packet[56], packet[57]); |
1082 |
fatal("yiaddr=%02x%02x%02x%02x ", packet[58], packet[59], |
1083 |
packet[60], packet[61]); |
1084 |
fatal("siaddr=%02x%02x%02x%02x ", packet[62], packet[63], |
1085 |
packet[64], packet[65]); |
1086 |
fatal("giaddr=%02x%02x%02x%02x ", packet[66], packet[67], |
1087 |
packet[68], packet[69]); |
1088 |
fatal("chaddr="); |
1089 |
for (i=70; i<70+16; i++) |
1090 |
fatal("%02x", packet[i]); |
1091 |
fatal(" ]\n"); |
1092 |
|
1093 |
#endif |
1094 |
} |
1095 |
|
1096 |
|
1097 |
/* |
1098 |
* net_ip_broadcast(): |
1099 |
* |
1100 |
* Handle an IP broadcast packet, coming from the emulated NIC. |
1101 |
* (This is usually a DHCP request, or similar.) |
1102 |
*/ |
1103 |
void net_ip_broadcast(struct net *net, void *extra, |
1104 |
unsigned char *packet, int len) |
1105 |
{ |
1106 |
unsigned char *p = (void *) &net->netmask_ipv4; |
1107 |
uint32_t x, y; |
1108 |
int i, xl, warning = 0, match = 0; |
1109 |
|
1110 |
#if 0 |
1111 |
fatal("[ net: IP BROADCAST: "); |
1112 |
fatal("ver=%02x ", packet[14]); |
1113 |
fatal("tos=%02x ", packet[15]); |
1114 |
fatal("len=%02x%02x ", packet[16], packet[17]); |
1115 |
fatal("id=%02x%02x ", packet[18], packet[19]); |
1116 |
fatal("ofs=%02x%02x ", packet[20], packet[21]); |
1117 |
fatal("ttl=%02x ", packet[22]); |
1118 |
fatal("p=%02x ", packet[23]); |
1119 |
fatal("sum=%02x%02x ", packet[24], packet[25]); |
1120 |
fatal("src=%02x%02x%02x%02x ", |
1121 |
packet[26], packet[27], packet[28], packet[29]); |
1122 |
fatal("dst=%02x%02x%02x%02x ", |
1123 |
packet[30], packet[31], packet[32], packet[33]); |
1124 |
for (i=34; i<len; i++) |
1125 |
fatal("%02x", packet[i]); |
1126 |
fatal(" ]\n"); |
1127 |
#endif |
1128 |
|
1129 |
/* Check for 10.0.0.255 first, maybe some guest OSes think that |
1130 |
it's a /24 network, regardless of what it actually is. */ |
1131 |
y = (packet[30] << 24) + (packet[31] << 16) + |
1132 |
(packet[32] << 8) + packet[33]; |
1133 |
|
1134 |
x = (p[0] << 24) + (p[1] << 16) + (p[2] << 8) + p[3]; |
1135 |
/* Example: x = 10.0.0.0 */ |
1136 |
x |= 255; |
1137 |
|
1138 |
if (x == y) { |
1139 |
warning = 1; |
1140 |
match = 1; |
1141 |
} |
1142 |
|
1143 |
xl = 32 - net->netmask_ipv4_len; |
1144 |
x |= (1 << xl) - 1; |
1145 |
/* x = 10.255.255.255 */ |
1146 |
|
1147 |
if (x == y) |
1148 |
match = 1; |
1149 |
if (y == 0xffffffff) |
1150 |
match = 1; |
1151 |
|
1152 |
if (warning) |
1153 |
fatal("[ net_ip_broadcast(): warning: broadcast to " |
1154 |
"0x%08x, expecting broadcast to 0x%08x or " |
1155 |
"0xffffffff ]\n", y, x); |
1156 |
|
1157 |
/* Cut off overflowing tail data: */ |
1158 |
if (len > 14 + packet[16]*256 + packet[17]) |
1159 |
len = 14 + packet[16]*256 + packet[17]; |
1160 |
|
1161 |
/* Check for known packets: */ |
1162 |
if (packet[14] == 0x45 && /* IPv4 */ |
1163 |
packet[23] == 0x11 && /* UDP */ |
1164 |
packet[34] == 0 && packet[35] == 68 && /* DHCP client */ |
1165 |
packet[36] == 0 && packet[37] == 67) { /* DHCP server */ |
1166 |
net_ip_broadcast_dhcp(net, extra, packet, len); |
1167 |
return; |
1168 |
} |
1169 |
|
1170 |
/* Unknown packet: */ |
1171 |
fatal("[ net: UNIMPLEMENTED IP BROADCAST: "); |
1172 |
fatal("ver=%02x ", packet[14]); |
1173 |
fatal("tos=%02x ", packet[15]); |
1174 |
fatal("len=%02x%02x ", packet[16], packet[17]); |
1175 |
fatal("id=%02x%02x ", packet[18], packet[19]); |
1176 |
fatal("ofs=%02x%02x ", packet[20], packet[21]); |
1177 |
fatal("ttl=%02x ", packet[22]); |
1178 |
fatal("p=%02x ", packet[23]); |
1179 |
fatal("sum=%02x%02x ", packet[24], packet[25]); |
1180 |
fatal("src=%02x%02x%02x%02x ", |
1181 |
packet[26], packet[27], packet[28], packet[29]); |
1182 |
fatal("dst=%02x%02x%02x%02x ", |
1183 |
packet[30], packet[31], packet[32], packet[33]); |
1184 |
for (i=34; i<len; i++) |
1185 |
fatal("%02x", packet[i]); |
1186 |
fatal(" ]\n"); |
1187 |
} |
1188 |
|
1189 |
|
1190 |
/* |
1191 |
* net_udp_rx_avail(): |
1192 |
* |
1193 |
* Receive any available UDP packets (from the outside world). |
1194 |
*/ |
1195 |
void net_udp_rx_avail(struct net *net, void *extra) |
1196 |
{ |
1197 |
int received_packets_this_tick = 0; |
1198 |
int max_packets_this_tick = 200; |
1199 |
int con_id; |
1200 |
|
1201 |
for (con_id=0; con_id<MAX_UDP_CONNECTIONS; con_id++) { |
1202 |
ssize_t res; |
1203 |
unsigned char buf[66000]; |
1204 |
unsigned char udp_data[66008]; |
1205 |
struct sockaddr_in from; |
1206 |
socklen_t from_len = sizeof(from); |
1207 |
int ip_len, udp_len; |
1208 |
struct ethernet_packet_link *lp; |
1209 |
int max_per_packet; |
1210 |
int bytes_converted = 0; |
1211 |
int this_packets_data_length; |
1212 |
int fragment_ofs = 0; |
1213 |
|
1214 |
if (received_packets_this_tick > max_packets_this_tick) |
1215 |
break; |
1216 |
|
1217 |
if (!net->udp_connections[con_id].in_use) |
1218 |
continue; |
1219 |
|
1220 |
if (net->udp_connections[con_id].socket < 0) { |
1221 |
fatal("INTERNAL ERROR in net.c, udp socket < 0 " |
1222 |
"but in use?\n"); |
1223 |
continue; |
1224 |
} |
1225 |
|
1226 |
res = recvfrom(net->udp_connections[con_id].socket, buf, |
1227 |
sizeof(buf), 0, (struct sockaddr *)&from, &from_len); |
1228 |
|
1229 |
/* No more incoming UDP on this connection? */ |
1230 |
if (res < 0) |
1231 |
continue; |
1232 |
|
1233 |
net->timestamp ++; |
1234 |
net->udp_connections[con_id].last_used_timestamp = |
1235 |
net->timestamp; |
1236 |
|
1237 |
net->udp_connections[con_id].udp_id ++; |
1238 |
|
1239 |
/* |
1240 |
* Special case for the nameserver: If a UDP packet is |
1241 |
* received from the nameserver (if the nameserver's IP is |
1242 |
* known), fake it so that it comes from the gateway instead. |
1243 |
*/ |
1244 |
if (net->udp_connections[con_id].fake_ns) |
1245 |
memcpy(((unsigned char *)(&from))+4, |
1246 |
&net->gateway_ipv4_addr[0], 4); |
1247 |
|
1248 |
/* |
1249 |
* We now have a UDP packet of size 'res' which we need |
1250 |
* turn into one or more ethernet packets for the emulated |
1251 |
* operating system. Ethernet packets are at most 1518 |
1252 |
* bytes long. With some margin, that means we can have |
1253 |
* about 1500 bytes per packet. |
1254 |
* |
1255 |
* Ethernet = 14 bytes |
1256 |
* IP = 20 bytes |
1257 |
* (UDP = 8 bytes + data) |
1258 |
* |
1259 |
* So data can be at most max_per_packet - 34. For UDP |
1260 |
* fragments, each multiple should (?) be a multiple of |
1261 |
* 8 bytes, except the last which doesn't have any such |
1262 |
* restriction. |
1263 |
*/ |
1264 |
max_per_packet = 1500; |
1265 |
|
1266 |
/* UDP: */ |
1267 |
udp_len = res + 8; |
1268 |
/* from[2..3] = outside_udp_port */ |
1269 |
udp_data[0] = ((unsigned char *)&from)[2]; |
1270 |
udp_data[1] = ((unsigned char *)&from)[3]; |
1271 |
udp_data[2] = (net->udp_connections[con_id]. |
1272 |
inside_udp_port >> 8) & 0xff; |
1273 |
udp_data[3] = net->udp_connections[con_id]. |
1274 |
inside_udp_port & 0xff; |
1275 |
udp_data[4] = udp_len >> 8; |
1276 |
udp_data[5] = udp_len & 0xff; |
1277 |
udp_data[6] = 0; |
1278 |
udp_data[7] = 0; |
1279 |
memcpy(udp_data + 8, buf, res); |
1280 |
/* |
1281 |
* TODO: UDP checksum, if necessary. At least NetBSD |
1282 |
* and OpenBSD accept UDP packets with 0x0000 in the |
1283 |
* checksum field anyway. |
1284 |
*/ |
1285 |
|
1286 |
while (bytes_converted < udp_len) { |
1287 |
this_packets_data_length = udp_len - bytes_converted; |
1288 |
|
1289 |
/* Do we need to fragment? */ |
1290 |
if (this_packets_data_length > max_per_packet-34) { |
1291 |
this_packets_data_length = |
1292 |
max_per_packet - 34; |
1293 |
while (this_packets_data_length & 7) |
1294 |
this_packets_data_length --; |
1295 |
} |
1296 |
|
1297 |
ip_len = 20 + this_packets_data_length; |
1298 |
|
1299 |
lp = net_allocate_ethernet_packet_link(net, extra, |
1300 |
14 + 20 + this_packets_data_length); |
1301 |
|
1302 |
/* Ethernet header: */ |
1303 |
memcpy(lp->data + 0, net->udp_connections[con_id]. |
1304 |
ethernet_address, 6); |
1305 |
memcpy(lp->data + 6, net->gateway_ethernet_addr, 6); |
1306 |
lp->data[12] = 0x08; /* IP = 0x0800 */ |
1307 |
lp->data[13] = 0x00; |
1308 |
|
1309 |
/* IP header: */ |
1310 |
lp->data[14] = 0x45; /* ver */ |
1311 |
lp->data[15] = 0x00; /* tos */ |
1312 |
lp->data[16] = ip_len >> 8; |
1313 |
lp->data[17] = ip_len & 0xff; |
1314 |
lp->data[18] = net->udp_connections[con_id].udp_id >> 8; |
1315 |
lp->data[19] = net->udp_connections[con_id].udp_id |
1316 |
& 0xff; |
1317 |
lp->data[20] = (fragment_ofs >> 8); |
1318 |
if (bytes_converted + this_packets_data_length |
1319 |
< udp_len) |
1320 |
lp->data[20] |= 0x20; /* More fragments */ |
1321 |
lp->data[21] = fragment_ofs & 0xff; |
1322 |
lp->data[22] = 0x40; /* ttl */ |
1323 |
lp->data[23] = 17; /* p = UDP */ |
1324 |
lp->data[26] = ((unsigned char *)&from)[4]; |
1325 |
lp->data[27] = ((unsigned char *)&from)[5]; |
1326 |
lp->data[28] = ((unsigned char *)&from)[6]; |
1327 |
lp->data[29] = ((unsigned char *)&from)[7]; |
1328 |
memcpy(lp->data + 30, net->udp_connections[con_id]. |
1329 |
inside_ip_address, 4); |
1330 |
net_ip_checksum(lp->data + 14, 10, 20); |
1331 |
|
1332 |
memcpy(lp->data+34, udp_data + bytes_converted, |
1333 |
this_packets_data_length); |
1334 |
|
1335 |
bytes_converted += this_packets_data_length; |
1336 |
fragment_ofs = bytes_converted / 8; |
1337 |
|
1338 |
received_packets_this_tick ++; |
1339 |
} |
1340 |
|
1341 |
/* This makes sure we check this connection AGAIN |
1342 |
for more incoming UDP packets, before moving to the |
1343 |
next connection: */ |
1344 |
con_id --; |
1345 |
} |
1346 |
} |
1347 |
|
1348 |
|
1349 |
/* |
1350 |
* net_tcp_rx_avail(): |
1351 |
* |
1352 |
* Receive any available TCP packets (from the outside world). |
1353 |
*/ |
1354 |
void net_tcp_rx_avail(struct net *net, void *extra) |
1355 |
{ |
1356 |
int received_packets_this_tick = 0; |
1357 |
int max_packets_this_tick = 200; |
1358 |
int con_id; |
1359 |
|
1360 |
for (con_id=0; con_id<MAX_TCP_CONNECTIONS; con_id++) { |
1361 |
unsigned char buf[66000]; |
1362 |
ssize_t res, res2; |
1363 |
fd_set rfds; |
1364 |
struct timeval tv; |
1365 |
|
1366 |
if (received_packets_this_tick > max_packets_this_tick) |
1367 |
break; |
1368 |
|
1369 |
if (!net->tcp_connections[con_id].in_use) |
1370 |
continue; |
1371 |
|
1372 |
if (net->tcp_connections[con_id].socket < 0) { |
1373 |
fatal("INTERNAL ERROR in net.c, tcp socket < 0" |
1374 |
" but in use?\n"); |
1375 |
continue; |
1376 |
} |
1377 |
|
1378 |
if (net->tcp_connections[con_id].incoming_buf == NULL) { |
1379 |
net->tcp_connections[con_id].incoming_buf = |
1380 |
malloc(TCP_INCOMING_BUF_LEN); |
1381 |
if (net->tcp_connections[con_id].incoming_buf == NULL) { |
1382 |
printf("out of memory allocating " |
1383 |
"incoming_buf for con_id %i\n", con_id); |
1384 |
exit(1); |
1385 |
} |
1386 |
} |
1387 |
|
1388 |
if (net->tcp_connections[con_id].state >= |
1389 |
TCP_OUTSIDE_DISCONNECTED) |
1390 |
continue; |
1391 |
|
1392 |
/* Is the socket available for output? */ |
1393 |
FD_ZERO(&rfds); /* write */ |
1394 |
FD_SET(net->tcp_connections[con_id].socket, &rfds); |
1395 |
tv.tv_sec = tv.tv_usec = 0; |
1396 |
errno = 0; |
1397 |
res = select(net->tcp_connections[con_id].socket+1, |
1398 |
NULL, &rfds, NULL, &tv); |
1399 |
|
1400 |
if (errno == ECONNREFUSED) { |
1401 |
fatal("[ ECONNREFUSED: TODO ]\n"); |
1402 |
net->tcp_connections[con_id].state = |
1403 |
TCP_OUTSIDE_DISCONNECTED; |
1404 |
fatal("CHANGING TO TCP_OUTSIDE_DISCONNECTED " |
1405 |
"(refused connection)\n"); |
1406 |
continue; |
1407 |
} |
1408 |
|
1409 |
if (errno == ETIMEDOUT) { |
1410 |
fatal("[ ETIMEDOUT: TODO ]\n"); |
1411 |
/* TODO */ |
1412 |
net->tcp_connections[con_id].state = |
1413 |
TCP_OUTSIDE_DISCONNECTED; |
1414 |
fatal("CHANGING TO TCP_OUTSIDE_DISCONNECTED " |
1415 |
"(timeout)\n"); |
1416 |
continue; |
1417 |
} |
1418 |
|
1419 |
if (net->tcp_connections[con_id].state == |
1420 |
TCP_OUTSIDE_TRYINGTOCONNECT && res > 0) { |
1421 |
net->tcp_connections[con_id].state = |
1422 |
TCP_OUTSIDE_CONNECTED; |
1423 |
debug("CHANGING TO TCP_OUTSIDE_CONNECTED\n"); |
1424 |
net_ip_tcp_connectionreply(net, extra, con_id, 1, |
1425 |
NULL, 0, 0); |
1426 |
} |
1427 |
|
1428 |
if (net->tcp_connections[con_id].state == |
1429 |
TCP_OUTSIDE_CONNECTED && res < 1) { |
1430 |
continue; |
1431 |
} |
1432 |
|
1433 |
/* |
1434 |
* Does this connection have unacknowledged data? Then, if |
1435 |
* enough number of rounds have passed, try to resend it using |
1436 |
* the old value of seqnr. |
1437 |
*/ |
1438 |
if (net->tcp_connections[con_id].incoming_buf_len != 0) { |
1439 |
net->tcp_connections[con_id].incoming_buf_rounds ++; |
1440 |
if (net->tcp_connections[con_id].incoming_buf_rounds > |
1441 |
10000) { |
1442 |
debug(" at seqnr %u but backing back to %u," |
1443 |
" resending %i bytes\n", |
1444 |
net->tcp_connections[con_id].outside_seqnr, |
1445 |
net->tcp_connections[con_id]. |
1446 |
incoming_buf_seqnr, |
1447 |
net->tcp_connections[con_id]. |
1448 |
incoming_buf_len); |
1449 |
|
1450 |
net->tcp_connections[con_id]. |
1451 |
incoming_buf_rounds = 0; |
1452 |
net->tcp_connections[con_id].outside_seqnr = |
1453 |
net->tcp_connections[con_id]. |
1454 |
incoming_buf_seqnr; |
1455 |
|
1456 |
net_ip_tcp_connectionreply(net, extra, con_id, |
1457 |
0, net->tcp_connections[con_id]. |
1458 |
incoming_buf, |
1459 |
net->tcp_connections[con_id]. |
1460 |
incoming_buf_len, 0); |
1461 |
} |
1462 |
continue; |
1463 |
} |
1464 |
|
1465 |
/* Don't receive unless the guest OS is ready! */ |
1466 |
if (((int32_t)net->tcp_connections[con_id].outside_seqnr - |
1467 |
(int32_t)net->tcp_connections[con_id].inside_acknr) > 0) { |
1468 |
/* fatal("YOYO 1! outside_seqnr - inside_acknr = %i\n", |
1469 |
net->tcp_connections[con_id].outside_seqnr - |
1470 |
net->tcp_connections[con_id].inside_acknr); */ |
1471 |
continue; |
1472 |
} |
1473 |
|
1474 |
/* Is there incoming data available on the socket? */ |
1475 |
FD_ZERO(&rfds); /* read */ |
1476 |
FD_SET(net->tcp_connections[con_id].socket, &rfds); |
1477 |
tv.tv_sec = tv.tv_usec = 0; |
1478 |
res2 = select(net->tcp_connections[con_id].socket+1, &rfds, |
1479 |
NULL, NULL, &tv); |
1480 |
|
1481 |
/* No more incoming TCP data on this connection? */ |
1482 |
if (res2 < 1) |
1483 |
continue; |
1484 |
|
1485 |
res = read(net->tcp_connections[con_id].socket, buf, 1400); |
1486 |
if (res > 0) { |
1487 |
/* debug("\n -{- %lli -}-\n", (long long)res); */ |
1488 |
net->tcp_connections[con_id].incoming_buf_len = res; |
1489 |
net->tcp_connections[con_id].incoming_buf_rounds = 0; |
1490 |
net->tcp_connections[con_id].incoming_buf_seqnr = |
1491 |
net->tcp_connections[con_id].outside_seqnr; |
1492 |
debug(" putting %i bytes (seqnr %u) in the incoming " |
1493 |
"buf\n", res, net->tcp_connections[con_id]. |
1494 |
incoming_buf_seqnr); |
1495 |
memcpy(net->tcp_connections[con_id].incoming_buf, |
1496 |
buf, res); |
1497 |
|
1498 |
net_ip_tcp_connectionreply(net, extra, con_id, 0, |
1499 |
buf, res, 0); |
1500 |
} else if (res == 0) { |
1501 |
net->tcp_connections[con_id].state = |
1502 |
TCP_OUTSIDE_DISCONNECTED; |
1503 |
debug("CHANGING TO TCP_OUTSIDE_DISCONNECTED, read" |
1504 |
" res=0\n"); |
1505 |
net_ip_tcp_connectionreply(net, extra, con_id, 0, |
1506 |
NULL, 0, 0); |
1507 |
} else { |
1508 |
net->tcp_connections[con_id].state = |
1509 |
TCP_OUTSIDE_DISCONNECTED; |
1510 |
fatal("CHANGING TO TCP_OUTSIDE_DISCONNECTED, " |
1511 |
"read res<=0, errno = %i\n", errno); |
1512 |
net_ip_tcp_connectionreply(net, extra, con_id, 0, |
1513 |
NULL, 0, 0); |
1514 |
} |
1515 |
|
1516 |
net->timestamp ++; |
1517 |
net->tcp_connections[con_id].last_used_timestamp = |
1518 |
net->timestamp; |
1519 |
} |
1520 |
} |
1521 |
|
1522 |
|