/[gxemul]/trunk/src/net.c
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Contents of /trunk/src/net.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 20 - (show annotations)
Mon Oct 8 16:19:23 2007 UTC (16 years, 6 months ago) by dpavlin
File MIME type: text/plain
File size: 67532 byte(s)
++ trunk/HISTORY	(local)
$Id: HISTORY,v 1.1055 2005/11/25 22:48:36 debug Exp $
20051031	Adding disassembly support for more ARM instructions (clz,
		smul* etc), and adding a hack to support "new tiny" pages
		for StrongARM.
20051101	Minor documentation updates (NetBSD 2.0.2 -> 2.1, and OpenBSD
		3.7 -> 3.8, and lots of testing).
		Changing from 1-sector PIO mode 0 transfers to 128-sector PIO
		mode 3 (in dev_wdc).
		Various minor ARM dyntrans updates (pc-relative loads from
		within the same page as the instruction are now treated as
		constant "mov").
20051102	Re-enabling instruction combinations (they were accidentally
		disabled).
		Dyntrans TLB entries are now overwritten using a round-robin
		scheme instead of randomly. This increases performance.
		Fixing a typo in file.c (thanks to Chuan-Hua Chang for
		noticing it).
		Experimenting with adding ATAPI support to dev_wdc (to make
		emulated *BSD detect cdroms as cdroms, not harddisks).
20051104	Various minor updates.
20051105	Continuing on the ATAPI emulation. Seems to work well enough
		for a NetBSD/cats installation, but not OpenBSD/cats.
		Various other updates.
20051106	Modifying the -Y command line option to allow scaleup with
		certain graphic controllers (only dev_vga so far), not just
		scaledown.
		Some minor dyntrans cleanups.
20051107	Beginning a cleanup up the PCI subsystem (removing the
		read_register hack, etc).
20051108	Continuing the cleanup; splitting up some pci devices into a
		normal autodev device and some separate pci glue code.
20051109	Continuing on the PCI bus stuff; all old pci_*.c have been
		incorporated into normal devices and/or rewritten as glue code
		only, adding a dummy Intel 82371AB PIIX4 for Malta (not really
		tested yet).
		Minor pckbc fix so that Linux doesn't complain.
		Working on the DEC 21143 NIC (ethernet mac rom stuff mostly).
		Various other minor fixes.
20051110	Some more ARM dyntrans fine-tuning (e.g. some instruction
		combinations (cmps followed by conditional branch within the
		same page) and special cases for DPIs with regform when the
		shifter isn't used).
20051111	ARM dyntrans updates: O(n)->O(1) for just-mark-as-non-
		writable in the generic pc_to_pointers function, and some other
		minor hacks.
		Merging Cobalt and evbmips (Malta) ISA interrupt handling,
		and some minor fixes to allow Linux to accept harddisk irqs.
20051112	Minor device updates (pckbc, dec21143, lpt, ...), most
		importantly fixing the ALI M1543/M5229 so that harddisk irqs
		work with Linux/CATS.
20051113	Some more generalizations of the PCI subsystem.
		Finally took the time to add a hack for SCSI CDROM TOCs; this
		enables OpenBSD to use partition 'a' (as needed by the OpenBSD
		installer), and Windows NT's installer to get a bit further.
		Also fixing dev_wdc to allow Linux to detect ATAPI CDROMs.
		Continuing on the DEC 21143.
20051114	Minor ARM dyntrans tweaks; ARM cmps+branch optimization when
		comparing with 0, and generalizing the xchg instr. comb.
		Adding disassembly of ARM mrrc/mcrr and q{,d}{add,sub}.
20051115	Continuing on various PPC things (BATs, other address trans-
		lation things, various loads/stores, BeBox emulation, etc.).
		Beginning to work on PPC interrupt/exception support.
20051116	Factoring out some code which initializes legacy ISA devices
		from those machines that use them (bus_isa).
		Continuing on PPC interrupt/exception support.
20051117	Minor Malta fixes: RTC year offset = 80, disabling a speed hack
		which caused NetBSD to detect a too fast cpu, and adding a new
		hack to make Linux detect a faster cpu.
		Continuing on the Artesyn PM/PPC emulation mode.
		Adding an Algor emulation skeleton (P4032 and P5064);
		implementing some of the basics.
		Continuing on PPC emulation in general; usage of unimplemented
		SPRs is now easier to track, continuing on memory/exception
		related issues, etc.
20051118	More work on PPC emulation (tgpr0..3, exception handling,
		memory stuff, syscalls, etc.).
20051119	Changing the ARM dyntrans code to mostly use cpu->pc, and not
		necessarily use arm reg 15. Seems to work.
		Various PPC updates; continuing on the PReP emulation mode.
20051120	Adding a workaround/hack to dev_mc146818 to allow NetBSD/prep
		to detect the clock.
20051121	More cleanup of the PCI bus (memory and I/O bases, etc).
		Continuing on various PPC things (decrementer and timebase,
		WDCs on obio (on PReP) use irq 13, not 14/15).
20051122	Continuing on the CPC700 controller (interrupts etc) for PMPPC,
		and on PPC stuff in general.
		Finally! After some bug fixes to the virtual to physical addr
		translation, NetBSD/{prep,pmppc} 2.1 reach userland and are
		stable enough to be interacted with.
		More PCI updates; reverse-endian device access for PowerPC etc.
20051123	Generalizing the IEEE floating point subsystem (moving it out
		from src/cpus/cpu_mips_coproc.c into a new src/float_emul.c).
		Input via slave xterms was sometimes not really working; fixing
		this for ns16550, and a warning message is now displayed if
		multiple non-xterm consoles are active.
		Adding some PPC floating point support, etc.
		Various interrupt related updates (dev_wdc, _ns16550, _8259,
		and the isa32 common code in machine.c).
		NetBSD/prep can now be installed! :-) (Well, with some manual
		commands necessary before running sysinst.) Updating the
		documentation and various other things to reflect this.
20051124	Various minor documentation updates.
		Continuing the work on the DEC 21143 NIC.
20051125	LOTS of work on the 21143. Both OpenBSD and NetBSD work fine
		with it now, except that OpenBSD sometimes gives a time-out
		warning.
		Minor documentation updates.

==============  RELEASE 0.3.7  ==============


1 /*
2 * Copyright (C) 2004-2005 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.c,v 1.84 2005/11/25 03:38:00 debug Exp $
29 *
30 * Emulated (ethernet / internet) network support.
31 *
32 *
33 * NOTE: This is just an ugly hack, and just barely enough to get some
34 * Internet networking up and running for the guest OS.
35 *
36 * TODO:
37 * o) TCP: fin/ack stuff, and connection time-outs and
38 * connection refused (reset on connect?), resend
39 * data to the guest OS if no ack has arrived for
40 * some time (? buffers?)
41 * http://www.tcpipguide.com/free/t_TCPConnectionTermination-2.htm
42 * o) remove the netbsd-specific options in the tcp header (?)
43 * o) Outgoing UDP packet fragment support.
44 * o) IPv6 (outgoing, incoming, and the nameserver/gateway)
45 * o) Incoming connections
46 *
47 * TODO 2: The following comments are old! Fix this.
48 *
49 *
50 * The emulated NIC has a MAC address of (for example) 10:20:30:00:00:10.
51 * From the emulated environment, the only other machine existing on the
52 * network is a "gateway" or "firewall", which has an address of
53 * 60:50:40:30:20:10. This module (net.c) contains the emulation of that
54 * gateway. It works like a NAT firewall, but emulated in userland software.
55 *
56 * The gateway uses IPv4 address 10.0.0.254, the guest OS (inside the
57 * emulator) could use any 10.x.x.x address, except 10.0.0.254. A suitable
58 * choice is, for example 10.0.0.1.
59 *
60 *
61 * NOTE: The 'extra' argument used in many functions in this file is a pointer
62 * to something unique for each controller, so that if multiple controllers
63 * are emulated concurrently, they will not get packets that aren't meant
64 * for some other controller.
65 *
66 *
67 * |------------------ a network --------------------------------|
68 * ^ ^ ^
69 * | | |
70 * a NIC connected another NIC the gateway
71 * to the network |
72 * v
73 * outside
74 * world
75 *
76 * The gateway isn't connected as a NIC, but is an "implicit" machine on the
77 * network.
78 *
79 * (See http://www.sinclair.org.au/keith/networking/vendor.html for a list
80 * of ethernet MAC assignments.)
81 */
82
83 #include <stdio.h>
84 #include <stdlib.h>
85 #include <string.h>
86 #include <unistd.h>
87 #include <errno.h>
88 #include <sys/types.h>
89 #include <sys/time.h>
90 #include <sys/socket.h>
91 #include <netinet/in.h>
92 #include <arpa/inet.h>
93 #include <netdb.h>
94 #include <fcntl.h>
95 #include <signal.h>
96
97 #include "machine.h"
98 #include "misc.h"
99 #include "net.h"
100
101
102 /* #define debug fatal */
103
104
105 #define ADDR_IPV4 1
106 #define ADDR_IPV6 2
107 #define ADDR_ETHERNET 3
108
109
110 /*
111 * net_debugaddr():
112 *
113 * Print an address using debug().
114 */
115 static void net_debugaddr(void *ipv4_addr, int type)
116 {
117 int i;
118 unsigned char *p = ipv4_addr;
119
120 switch (type) {
121 case ADDR_IPV4:
122 for (i=0; i<4; i++)
123 debug("%s%i", i? "." : "", p[i]);
124 break;
125 case ADDR_IPV6:
126 for (i=0; i<16; i+=2)
127 debug("%s%4x", i? ":" : "", p[i] * 256 + p[i+1]);
128 break;
129 case ADDR_ETHERNET:
130 for (i=0; i<6; i++)
131 debug("%s%02x", i? ":" : "", p[i]);
132 break;
133 default:
134 fatal("( net_debugaddr(): UNIMPLEMTED type %i )\n", type);
135 }
136 }
137
138
139 /*
140 * net_generate_unique_mac():
141 *
142 * Generate a "unique" serial number for a machine. The machine's serial
143 * number is combined with the machine's current number of NICs to form a
144 * more-or-less valid MAC address.
145 *
146 * The return value (6 bytes) are written to macbuf.
147 */
148 void net_generate_unique_mac(struct machine *machine, unsigned char *macbuf)
149 {
150 int x, y;
151
152 if (macbuf == NULL || machine == NULL) {
153 fatal("**\n** net_generate_unique_mac(): NULL ptr\n**\n");
154 return;
155 }
156
157 x = machine->serial_nr;
158 y = machine->nr_of_nics;
159
160 /*
161 * TODO: What is a good starting value? Something like this?
162 *
163 * +-----------+-------------------+-------------+-------------+
164 * | 16 bits | 16 bits machine | 12 bits | 4 bits of |
165 * | fixed | serial nr | NIC nr(*) | zeroes |
166 * +-----------+-------------------+-------------+-------------+
167 */
168 macbuf[0] = 0x10;
169 macbuf[1] = 0x20;
170 macbuf[2] = 0x30;
171 macbuf[3] = 0;
172 macbuf[4] = 0;
173 macbuf[5] = machine->serial_nr << 4;
174
175 if (macbuf[0] & 1 || macbuf[5] & 1) {
176 fatal("Internal error in net_generate_unique_mac().\n");
177 exit(1);
178 }
179
180 /* TODO: Remember the mac addresses somewhere? */
181 machine->nr_of_nics ++;
182 }
183
184
185 /*
186 * net_ip_checksum():
187 *
188 * Fill in an IP header checksum. (This works for ICMP too.)
189 * chksumoffset should be 10 for IP headers, and len = 20.
190 * For ICMP packets, chksumoffset = 2 and len = length of the ICMP packet.
191 */
192 void net_ip_checksum(unsigned char *ip_header, int chksumoffset, int len)
193 {
194 int i;
195 uint32_t sum = 0;
196
197 for (i=0; i<len; i+=2)
198 if (i != chksumoffset) {
199 uint16_t w = (ip_header[i] << 8) + ip_header[i+1];
200 sum += w;
201 while (sum > 65535) {
202 int to_add = sum >> 16;
203 sum = (sum & 0xffff) + to_add;
204 }
205 }
206
207 sum ^= 0xffff;
208 ip_header[chksumoffset + 0] = sum >> 8;
209 ip_header[chksumoffset + 1] = sum & 0xff;
210 }
211
212
213 /*
214 * net_ip_tcp_checksum():
215 *
216 * Fill in a TCP header checksum. This differs slightly from the IP
217 * checksum. The checksum is calculated on a pseudo header, the actual
218 * TCP header, and the data. This is what the pseudo header looks like:
219 *
220 * uint32_t srcaddr;
221 * uint32_t dstaddr;
222 * uint16_t protocol; (= 6 for tcp)
223 * uint16_t tcp_len;
224 *
225 * tcp_len is length of header PLUS data. The psedo header is created
226 * internally here, and does not need to be supplied by the caller.
227 */
228 static void net_ip_tcp_checksum(unsigned char *tcp_header, int chksumoffset,
229 int tcp_len, unsigned char *srcaddr, unsigned char *dstaddr,
230 int udpflag)
231 {
232 int i, pad = 0;
233 unsigned char pseudoh[12];
234 uint32_t sum = 0;
235
236 memcpy(pseudoh + 0, srcaddr, 4);
237 memcpy(pseudoh + 4, dstaddr, 4);
238 pseudoh[8] = 0x00;
239 pseudoh[9] = udpflag? 17 : 6;
240 pseudoh[10] = tcp_len >> 8;
241 pseudoh[11] = tcp_len & 255;
242
243 for (i=0; i<12; i+=2) {
244 uint16_t w = (pseudoh[i] << 8) + pseudoh[i+1];
245 sum += w;
246 while (sum > 65535) {
247 int to_add = sum >> 16;
248 sum = (sum & 0xffff) + to_add;
249 }
250 }
251
252 if (tcp_len & 1) {
253 tcp_len ++;
254 pad = 1;
255 }
256
257 for (i=0; i<tcp_len; i+=2)
258 if (i != chksumoffset) {
259 uint16_t w;
260 if (!pad || i < tcp_len-2)
261 w = (tcp_header[i] << 8) + tcp_header[i+1];
262 else
263 w = (tcp_header[i] << 8) + 0x00;
264 sum += w;
265 while (sum > 65535) {
266 int to_add = sum >> 16;
267 sum = (sum & 0xffff) + to_add;
268 }
269 }
270
271 sum ^= 0xffff;
272 tcp_header[chksumoffset + 0] = sum >> 8;
273 tcp_header[chksumoffset + 1] = sum & 0xff;
274 }
275
276
277 /*
278 * net_allocate_packet_link():
279 *
280 * This routine allocates an ethernet_packet_link struct, and adds it at
281 * the end of the packet chain. A data buffer is allocated (and zeroed),
282 * and the data, extra, and len fields of the link are set.
283 *
284 * Return value is a pointer to the link on success. It doesn't return on
285 * failure.
286 */
287 static struct ethernet_packet_link *net_allocate_packet_link(
288 struct net *net, void *extra, int len)
289 {
290 struct ethernet_packet_link *lp;
291
292 lp = malloc(sizeof(struct ethernet_packet_link));
293 if (lp == NULL) {
294 fprintf(stderr, "net_allocate_packet_link(): out of memory\n");
295 exit(1);
296 }
297
298 /* memset(lp, 0, sizeof(struct ethernet_packet_link)); */
299
300 lp->len = len;
301 lp->extra = extra;
302 lp->data = malloc(len);
303 if (lp->data == NULL) {
304 fprintf(stderr, "net_allocate_packet_link(): out of memory\n");
305 exit(1);
306 }
307 lp->next = NULL;
308
309 /* TODO: maybe this is not necessary: */
310 memset(lp->data, 0, len);
311
312 /* Add last in the link chain: */
313 lp->prev = net->last_ethernet_packet;
314 if (lp->prev != NULL)
315 lp->prev->next = lp;
316 else
317 net->first_ethernet_packet = lp;
318 net->last_ethernet_packet = lp;
319
320 return lp;
321 }
322
323
324 /*
325 * net_ip_icmp():
326 *
327 * Handle an ICMP packet.
328 *
329 * The IP header (at offset 14) could look something like
330 *
331 * ver=45 tos=00 len=0054 id=001a ofs=0000 ttl=ff p=01 sum=a87e
332 * src=0a000005 dst=03050607
333 *
334 * and the ICMP specific data (beginning at offset 34):
335 *
336 * type=08 code=00 chksum=b8bf
337 * 000c0008d5cee94089190c0008090a0b
338 * 0c0d0e0f101112131415161718191a1b
339 * 1c1d1e1f202122232425262728292a2b
340 * 2c2d2e2f3031323334353637
341 */
342 static void net_ip_icmp(struct net *net, void *extra,
343 unsigned char *packet, int len)
344 {
345 int type;
346 struct ethernet_packet_link *lp;
347
348 type = packet[34];
349
350 switch (type) {
351 case 8: /* ECHO request */
352 debug("[ ICMP echo ]\n");
353 lp = net_allocate_packet_link(net, extra, len);
354
355 /* Copy the old packet first: */
356 memcpy(lp->data, packet, len);
357
358 /* Switch to and from ethernet addresses: */
359 memcpy(lp->data + 0, packet + 6, 6);
360 memcpy(lp->data + 6, packet + 0, 6);
361
362 /* Switch to and from IP addresses: */
363 memcpy(lp->data + 26, packet + 30, 4);
364 memcpy(lp->data + 30, packet + 26, 4);
365
366 /* Change from echo REQUEST to echo REPLY: */
367 lp->data[34] = 0x00;
368
369 /* Decrease the TTL to a low value: */
370 lp->data[22] = 2;
371
372 /* Recalculate ICMP checksum: */
373 net_ip_checksum(lp->data + 34, 2, len - 34);
374
375 /* Recalculate IP header checksum: */
376 net_ip_checksum(lp->data + 14, 10, 20);
377
378 break;
379 default:
380 fatal("[ net: ICMP type %i not yet implemented ]\n", type);
381 }
382 }
383
384
385 /*
386 * tcp_closeconnection():
387 *
388 * Helper function which closes down a TCP connection completely.
389 */
390 static void tcp_closeconnection(struct net *net, int con_id)
391 {
392 close(net->tcp_connections[con_id].socket);
393 net->tcp_connections[con_id].state = TCP_OUTSIDE_DISCONNECTED;
394 net->tcp_connections[con_id].in_use = 0;
395 net->tcp_connections[con_id].incoming_buf_len = 0;
396 }
397
398
399 /*
400 * net_ip_tcp_connectionreply():
401 *
402 * When changing from state _TRYINGTOCONNECT to _CONNECTED, then this
403 * function should be called with connecting set to 1.
404 *
405 * To send a generic ack reply, set connecting to 0.
406 *
407 * To send data (PSH), set data to non-NULL and datalen to the length.
408 *
409 * This creates an ethernet packet for the guest OS with an ACK to the
410 * initial SYN packet.
411 */
412 static void net_ip_tcp_connectionreply(struct net *net, void *extra,
413 int con_id, int connecting, unsigned char *data, int datalen, int rst)
414 {
415 struct ethernet_packet_link *lp;
416 int tcp_length, ip_len, option_len = 20;
417
418 if (connecting)
419 net->tcp_connections[con_id].outside_acknr =
420 net->tcp_connections[con_id].inside_seqnr + 1;
421
422 net->tcp_connections[con_id].tcp_id ++;
423 tcp_length = 20 + option_len + datalen;
424 ip_len = 20 + tcp_length;
425 lp = net_allocate_packet_link(net, extra, 14 + ip_len);
426
427 /* Ethernet header: */
428 memcpy(lp->data + 0, net->tcp_connections[con_id].ethernet_address, 6);
429 memcpy(lp->data + 6, net->gateway_ethernet_addr, 6);
430 lp->data[12] = 0x08; /* IP = 0x0800 */
431 lp->data[13] = 0x00;
432
433 /* IP header: */
434 lp->data[14] = 0x45; /* ver */
435 lp->data[15] = 0x10; /* tos */
436 lp->data[16] = ip_len >> 8;
437 lp->data[17] = ip_len & 0xff;
438 lp->data[18] = net->tcp_connections[con_id].tcp_id >> 8;
439 lp->data[19] = net->tcp_connections[con_id].tcp_id & 0xff;
440 lp->data[20] = 0x40; /* don't fragment */
441 lp->data[21] = 0x00;
442 lp->data[22] = 0x40; /* ttl */
443 lp->data[23] = 6; /* p = TCP */
444 memcpy(lp->data + 26, net->tcp_connections[con_id].
445 outside_ip_address, 4);
446 memcpy(lp->data + 30, net->tcp_connections[con_id].
447 inside_ip_address, 4);
448 net_ip_checksum(lp->data + 14, 10, 20);
449
450 /* TCP header and options at offset 34: */
451 lp->data[34] = net->tcp_connections[con_id].outside_tcp_port >> 8;
452 lp->data[35] = net->tcp_connections[con_id].outside_tcp_port & 0xff;
453 lp->data[36] = net->tcp_connections[con_id].inside_tcp_port >> 8;
454 lp->data[37] = net->tcp_connections[con_id].inside_tcp_port & 0xff;
455 lp->data[38] = (net->tcp_connections[con_id].
456 outside_seqnr >> 24) & 0xff;
457 lp->data[39] = (net->tcp_connections[con_id].
458 outside_seqnr >> 16) & 0xff;
459 lp->data[40] = (net->tcp_connections[con_id].
460 outside_seqnr >> 8) & 0xff;
461 lp->data[41] = net->tcp_connections[con_id].
462 outside_seqnr & 0xff;
463 lp->data[42] = (net->tcp_connections[con_id].
464 outside_acknr >> 24) & 0xff;
465 lp->data[43] = (net->tcp_connections[con_id].
466 outside_acknr >> 16) & 0xff;
467 lp->data[44] = (net->tcp_connections[con_id].
468 outside_acknr >> 8) & 0xff;
469 lp->data[45] = net->tcp_connections[con_id].outside_acknr & 0xff;
470
471 /* Control */
472 lp->data[46] = (option_len + 20) / 4 * 0x10;
473 lp->data[47] = 0x10; /* ACK */
474 if (connecting)
475 lp->data[47] |= 0x02; /* SYN */
476 if (net->tcp_connections[con_id].state == TCP_OUTSIDE_CONNECTED)
477 lp->data[47] |= 0x08; /* PSH */
478 if (rst)
479 lp->data[47] |= 0x04; /* RST */
480 if (net->tcp_connections[con_id].state >= TCP_OUTSIDE_DISCONNECTED)
481 lp->data[47] |= 0x01; /* FIN */
482
483 /* Window */
484 lp->data[48] = 0x10;
485 lp->data[49] = 0x00;
486
487 /* no urgent ptr */
488
489 /* options */
490 /* TODO: HAHA, this is ugly */
491 lp->data[54] = 0x02;
492 lp->data[55] = 0x04;
493 lp->data[56] = 0x05;
494 lp->data[57] = 0xb4;
495 lp->data[58] = 0x01;
496 lp->data[59] = 0x03;
497 lp->data[60] = 0x03;
498 lp->data[61] = 0x00;
499 lp->data[62] = 0x01;
500 lp->data[63] = 0x01;
501 lp->data[64] = 0x08;
502 lp->data[65] = 0x0a;
503 lp->data[66] = (net->timestamp >> 24) & 0xff;
504 lp->data[67] = (net->timestamp >> 16) & 0xff;
505 lp->data[68] = (net->timestamp >> 8) & 0xff;
506 lp->data[69] = net->timestamp & 0xff;
507 lp->data[70] = (net->tcp_connections[con_id].
508 inside_timestamp >> 24) & 0xff;
509 lp->data[71] = (net->tcp_connections[con_id].
510 inside_timestamp >> 16) & 0xff;
511 lp->data[72] = (net->tcp_connections[con_id].
512 inside_timestamp >> 8) & 0xff;
513 lp->data[73] = net->tcp_connections[con_id].
514 inside_timestamp & 0xff;
515
516 /* data: */
517 if (data != NULL) {
518 memcpy(lp->data + 74, data, datalen);
519 net->tcp_connections[con_id].outside_seqnr += datalen;
520 }
521
522 /* Checksum: */
523 net_ip_tcp_checksum(lp->data + 34, 16, tcp_length,
524 lp->data + 26, lp->data + 30, 0);
525
526 #if 0
527 {
528 int i;
529 fatal("[ net_ip_tcp_connectionreply(%i): ", connecting);
530 for (i=0; i<ip_len+14; i++)
531 fatal("%02x", lp->data[i]);
532 fatal(" ]\n");
533 }
534 #endif
535
536 if (connecting)
537 net->tcp_connections[con_id].outside_seqnr ++;
538 }
539
540
541 /*
542 * net_ip_tcp():
543 *
544 * Handle a TCP packet comming from the emulated OS.
545 *
546 * The IP header (at offset 14) could look something like
547 *
548 * ver=45 tos=00 len=003c id=0006 ofs=0000 ttl=40 p=11 sum=b798
549 * src=0a000001 dst=c1abcdef
550 *
551 * TCP header, at offset 34:
552 *
553 * srcport=fffe dstport=0015 seqnr=af419a1d acknr=00000000
554 * control=a002 window=4000 checksum=fe58 urgent=0000
555 * and then "options and padding" and then data.
556 * (020405b4010303000101080a0000000000000000)
557 *
558 * See the following URLs for good descriptions of TCP:
559 *
560 * http://www.networksorcery.com/enp/protocol/tcp.htm
561 * http://www.tcpipguide.com/free/t_TCPIPTransmissionControlProtocolTCP.htm
562 */
563 static void net_ip_tcp(struct net *net, void *extra,
564 unsigned char *packet, int len)
565 {
566 int con_id, free_con_id, i, res;
567 int srcport, dstport, data_offset, window, checksum, urgptr;
568 int syn, ack, psh, rst, urg, fin;
569 uint32_t seqnr, acknr;
570 struct sockaddr_in remote_ip;
571 fd_set rfds;
572 struct timeval tv;
573 int send_ofs;
574
575 #if 0
576 fatal("[ net: TCP: ");
577 for (i=0; i<26; i++)
578 fatal("%02x", packet[i]);
579 fatal(" ");
580 #endif
581
582 srcport = (packet[34] << 8) + packet[35];
583 dstport = (packet[36] << 8) + packet[37];
584
585 seqnr = (packet[38] << 24) + (packet[39] << 16)
586 + (packet[40] << 8) + packet[41];
587 acknr = (packet[42] << 24) + (packet[43] << 16)
588 + (packet[44] << 8) + packet[45];
589
590 #if 0
591 fatal("%i.%i.%i.%i:%i -> %i.%i.%i.%i:%i, seqnr=%lli acknr=%lli ",
592 packet[26], packet[27], packet[28], packet[29], srcport,
593 packet[30], packet[31], packet[32], packet[33], dstport,
594 (long long)seqnr, (long long)acknr);
595 #endif
596
597 data_offset = (packet[46] >> 4) * 4 + 34;
598 /* data_offset is now data offset within packet :-) */
599
600 urg = packet[47] & 32;
601 ack = packet[47] & 16;
602 psh = packet[47] & 8;
603 rst = packet[47] & 4;
604 syn = packet[47] & 2;
605 fin = packet[47] & 1;
606 window = (packet[48] << 8) + packet[49];
607 checksum = (packet[50] << 8) + packet[51];
608 urgptr = (packet[52] << 8) + packet[53];
609
610 #if 0
611 fatal(urg? "URG " : "");
612 fatal(ack? "ACK " : "");
613 fatal(psh? "PSH " : "");
614 fatal(rst? "RST " : "");
615 fatal(syn? "SYN " : "");
616 fatal(fin? "FIN " : "");
617
618 fatal("window=0x%04x checksum=0x%04x urgptr=0x%04x ",
619 window, checksum, urgptr);
620
621 fatal("options=");
622 for (i=34+20; i<data_offset; i++)
623 fatal("%02x", packet[i]);
624
625 fatal(" data=");
626 for (i=data_offset; i<len; i++)
627 fatal("%02x", packet[i]);
628
629 fatal(" ]\n");
630 #endif
631
632 net_ip_tcp_checksum(packet + 34, 16, len - 34,
633 packet + 26, packet + 30, 0);
634 if (packet[50] * 256 + packet[51] != checksum) {
635 debug("TCP: dropping packet because of checksum mismatch "
636 "(0x%04x != 0x%04x)\n", packet[50] * 256 + packet[51],
637 checksum);
638
639 return;
640 }
641
642 /* Does this packet belong to a current connection? */
643 con_id = free_con_id = -1;
644 for (i=0; i<MAX_TCP_CONNECTIONS; i++) {
645 if (!net->tcp_connections[i].in_use)
646 free_con_id = i;
647 if (net->tcp_connections[i].in_use &&
648 net->tcp_connections[i].inside_tcp_port == srcport &&
649 net->tcp_connections[i].outside_tcp_port == dstport &&
650 memcmp(net->tcp_connections[i].inside_ip_address,
651 packet + 26, 4) == 0 &&
652 memcmp(net->tcp_connections[i].outside_ip_address,
653 packet + 30, 4) == 0) {
654 con_id = i;
655 break;
656 }
657 }
658
659 /*
660 * Unknown connection, and not SYN? Then drop the packet.
661 * TODO: Send back RST?
662 */
663 if (con_id < 0 && !syn) {
664 debug("[ net: TCP: dropping packet from unknown connection,"
665 " %i.%i.%i.%i:%i -> %i.%i.%i.%i:%i %s%s%s%s%s]\n",
666 packet[26], packet[27], packet[28], packet[29], srcport,
667 packet[30], packet[31], packet[32], packet[33], dstport,
668 fin? "FIN ": "", syn? "SYN ": "", ack? "ACK ": "",
669 psh? "PSH ": "", rst? "RST ": "");
670 return;
671 }
672
673 /* Known connection, and SYN? Then ignore the packet. */
674 if (con_id >= 0 && syn) {
675 debug("[ net: TCP: ignoring redundant SYN packet from known"
676 " connection, %i.%i.%i.%i:%i -> %i.%i.%i.%i:%i ]\n",
677 packet[26], packet[27], packet[28], packet[29], srcport,
678 packet[30], packet[31], packet[32], packet[33], dstport);
679 return;
680 }
681
682 /*
683 * A new outgoing connection?
684 */
685 if (con_id < 0 && syn) {
686 debug("[ net: TCP: new outgoing connection, %i.%i.%i.%i:%i"
687 " -> %i.%i.%i.%i:%i ]\n",
688 packet[26], packet[27], packet[28], packet[29], srcport,
689 packet[30], packet[31], packet[32], packet[33], dstport);
690
691 /* Find a free connection id to use: */
692 if (free_con_id < 0) {
693 #if 1
694 /*
695 * TODO: Reuse the oldest one currently in use, or
696 * just drop the new connection attempt? Drop for now.
697 */
698 fatal("[ TOO MANY TCP CONNECTIONS IN USE! "
699 "Increase MAX_TCP_CONNECTIONS! ]\n");
700 return;
701 #else
702 int i;
703 int64_t oldest = net->
704 tcp_connections[0].last_used_timestamp;
705 free_con_id = 0;
706
707 fatal("[ NO FREE TCP SLOTS, REUSING OLDEST ONE ]\n");
708 for (i=0; i<MAX_TCP_CONNECTIONS; i++)
709 if (net->tcp_connections[i].
710 last_used_timestamp < oldest) {
711 oldest = net->tcp_connections[i].
712 last_used_timestamp;
713 free_con_id = i;
714 }
715 tcp_closeconnection(net, free_con_id);
716 #endif
717 }
718
719 con_id = free_con_id;
720 memset(&net->tcp_connections[con_id], 0,
721 sizeof(struct tcp_connection));
722
723 memcpy(net->tcp_connections[con_id].ethernet_address,
724 packet + 6, 6);
725 memcpy(net->tcp_connections[con_id].inside_ip_address,
726 packet + 26, 4);
727 net->tcp_connections[con_id].inside_tcp_port = srcport;
728 memcpy(net->tcp_connections[con_id].outside_ip_address,
729 packet + 30, 4);
730 net->tcp_connections[con_id].outside_tcp_port = dstport;
731
732 net->tcp_connections[con_id].socket =
733 socket(AF_INET, SOCK_STREAM, 0);
734 if (net->tcp_connections[con_id].socket < 0) {
735 fatal("[ net: TCP: socket() returned %i ]\n",
736 net->tcp_connections[con_id].socket);
737 return;
738 }
739
740 debug("[ new tcp outgoing socket=%i ]\n",
741 net->tcp_connections[con_id].socket);
742
743 net->tcp_connections[con_id].in_use = 1;
744
745 /* Set the socket to non-blocking: */
746 res = fcntl(net->tcp_connections[con_id].socket, F_GETFL);
747 fcntl(net->tcp_connections[con_id].socket, F_SETFL,
748 res | O_NONBLOCK);
749
750 remote_ip.sin_family = AF_INET;
751 memcpy((unsigned char *)&remote_ip.sin_addr,
752 net->tcp_connections[con_id].outside_ip_address, 4);
753 remote_ip.sin_port = htons(
754 net->tcp_connections[con_id].outside_tcp_port);
755
756 res = connect(net->tcp_connections[con_id].socket,
757 (struct sockaddr *)&remote_ip, sizeof(remote_ip));
758
759 /* connect can return -1, and errno = EINPROGRESS
760 as we might not have connected right away. */
761
762 net->tcp_connections[con_id].state =
763 TCP_OUTSIDE_TRYINGTOCONNECT;
764
765 net->tcp_connections[con_id].outside_acknr = 0;
766 net->tcp_connections[con_id].outside_seqnr =
767 ((random() & 0xffff) << 16) + (random() & 0xffff);
768 }
769
770 if (rst) {
771 debug("[ 'rst': disconnecting TCP connection %i ]\n", con_id);
772 net_ip_tcp_connectionreply(net, extra, con_id, 0, NULL, 0, 1);
773 tcp_closeconnection(net, con_id);
774 return;
775 }
776
777 if (ack && net->tcp_connections[con_id].state
778 == TCP_OUTSIDE_DISCONNECTED2) {
779 debug("[ 'ack': guestOS's final termination of TCP "
780 "connection %i ]\n", con_id);
781
782 /* Send an RST? (TODO, this is wrong...) */
783 net_ip_tcp_connectionreply(net, extra, con_id, 0, NULL, 0, 1);
784
785 /* ... and forget about this connection: */
786 tcp_closeconnection(net, con_id);
787 return;
788 }
789
790 if (fin && net->tcp_connections[con_id].state
791 == TCP_OUTSIDE_DISCONNECTED) {
792 debug("[ 'fin': response to outside's disconnection of "
793 "TCP connection %i ]\n", con_id);
794
795 /* Send an ACK: */
796 net->tcp_connections[con_id].state = TCP_OUTSIDE_CONNECTED;
797 net_ip_tcp_connectionreply(net, extra, con_id, 0, NULL, 0, 0);
798 net->tcp_connections[con_id].state = TCP_OUTSIDE_DISCONNECTED2;
799 return;
800 }
801
802 if (fin) {
803 debug("[ 'fin': guestOS disconnecting TCP connection %i ]\n",
804 con_id);
805
806 /* Send ACK: */
807 net_ip_tcp_connectionreply(net, extra, con_id, 0, NULL, 0, 0);
808 net->tcp_connections[con_id].state = TCP_OUTSIDE_DISCONNECTED2;
809
810 /* Return and send FIN: */
811 goto ret;
812 }
813
814 if (ack) {
815 debug("ACK %i bytes, inside_acknr=%u outside_seqnr=%u\n",
816 net->tcp_connections[con_id].incoming_buf_len,
817 net->tcp_connections[con_id].inside_acknr,
818 net->tcp_connections[con_id].outside_seqnr);
819 net->tcp_connections[con_id].inside_acknr = acknr;
820 if (net->tcp_connections[con_id].inside_acknr ==
821 net->tcp_connections[con_id].outside_seqnr &&
822 net->tcp_connections[con_id].incoming_buf_len != 0) {
823 debug(" all acked\n");
824 net->tcp_connections[con_id].incoming_buf_len = 0;
825 }
826 }
827
828 net->tcp_connections[con_id].inside_seqnr = seqnr;
829
830 /* TODO: This is hardcoded for a specific NetBSD packet: */
831 if (packet[34 + 30] == 0x08 && packet[34 + 31] == 0x0a)
832 net->tcp_connections[con_id].inside_timestamp =
833 (packet[34 + 32 + 0] << 24) +
834 (packet[34 + 32 + 1] << 16) +
835 (packet[34 + 32 + 2] << 8) +
836 (packet[34 + 32 + 3] << 0);
837
838
839 net->timestamp ++;
840 net->tcp_connections[con_id].last_used_timestamp = net->timestamp;
841
842
843 if (net->tcp_connections[con_id].state != TCP_OUTSIDE_CONNECTED) {
844 debug("[ not connected to outside ]\n");
845 return;
846 }
847
848
849 if (data_offset >= len)
850 return;
851
852
853 /*
854 * We are here if this is a known connection, and data is to be
855 * transmitted to the outside world.
856 */
857
858 send_ofs = data_offset;
859 send_ofs += ((int32_t)net->tcp_connections[con_id].outside_acknr
860 - (int32_t)seqnr);
861 #if 1
862 debug("[ %i bytes of tcp data to be sent, beginning at seqnr %u, ",
863 len - data_offset, seqnr);
864 debug("outside is at acknr %u ==> %i actual bytes to be sent ]\n",
865 net->tcp_connections[con_id].outside_acknr, len - send_ofs);
866 #endif
867
868 /* Drop outgoing packet if the guest OS' seqnr is not
869 the same as we have acked. (We have missed something, perhaps.) */
870 if (seqnr != net->tcp_connections[con_id].outside_acknr) {
871 debug("!! outgoing TCP packet dropped (seqnr = %u, "
872 "outside_acknr = %u)\n", seqnr,
873 net->tcp_connections[con_id].outside_acknr);
874 goto ret;
875 }
876
877 if (len - send_ofs > 0) {
878 /* Is the socket available for output? */
879 FD_ZERO(&rfds); /* write */
880 FD_SET(net->tcp_connections[con_id].socket, &rfds);
881 tv.tv_sec = tv.tv_usec = 0;
882 errno = 0;
883 res = select(net->tcp_connections[con_id].socket+1,
884 NULL, &rfds, NULL, &tv);
885 if (res < 1) {
886 net->tcp_connections[con_id].state =
887 TCP_OUTSIDE_DISCONNECTED;
888 debug("[ TCP: disconnect on select for writing ]\n");
889 goto ret;
890 }
891
892 res = write(net->tcp_connections[con_id].socket,
893 packet + send_ofs, len - send_ofs);
894
895 if (res > 0) {
896 net->tcp_connections[con_id].outside_acknr += res;
897 } else if (errno == EAGAIN) {
898 /* Just ignore this attempt. */
899 return;
900 } else {
901 debug("[ error writing %i bytes to TCP connection %i:"
902 " errno = %i ]\n", len - send_ofs, con_id, errno);
903 net->tcp_connections[con_id].state =
904 TCP_OUTSIDE_DISCONNECTED;
905 debug("[ TCP: disconnect on write() ]\n");
906 goto ret;
907 }
908 }
909
910 ret:
911 /* Send an ACK (or FIN) to the guest OS: */
912 net_ip_tcp_connectionreply(net, extra, con_id, 0, NULL, 0, 0);
913 }
914
915
916 /*
917 * net_ip_udp():
918 *
919 * Handle a UDP packet.
920 *
921 * (See http://www.networksorcery.com/enp/protocol/udp.htm.)
922 *
923 * The IP header (at offset 14) could look something like
924 *
925 * ver=45 tos=00 len=003c id=0006 ofs=0000 ttl=40 p=11 sum=b798
926 * src=0a000001 dst=c1abcdef
927 *
928 * and the UDP data (beginning at offset 34):
929 *
930 * srcport=fffc dstport=0035 length=0028 chksum=76b6
931 * 43e20100000100000000000003667470066e6574627364036f726700001c0001
932 */
933 static void net_ip_udp(struct net *net, void *extra,
934 unsigned char *packet, int len)
935 {
936 int con_id, free_con_id, i, srcport, dstport, udp_len;
937 ssize_t res;
938 struct sockaddr_in remote_ip;
939
940 if ((packet[20] & 0x3f) != 0) {
941 fatal("[ net_ip_udp(): WARNING! fragmented UDP "
942 "packet, TODO ]\n");
943 return;
944 }
945
946 srcport = (packet[34] << 8) + packet[35];
947 dstport = (packet[36] << 8) + packet[37];
948 udp_len = (packet[38] << 8) + packet[39];
949 /* chksum at offset 40 and 41 */
950
951 debug("[ net: UDP: ");
952 debug("srcport=%i dstport=%i len=%i ", srcport, dstport, udp_len);
953 for (i=42; i<len; i++) {
954 if (packet[i] >= ' ' && packet[i] < 127)
955 debug("%c", packet[i]);
956 else
957 debug("[%02x]", packet[i]);
958 }
959 debug(" ]\n");
960
961 /* Is this "connection" new, or a currently ongoing one? */
962 con_id = free_con_id = -1;
963 for (i=0; i<MAX_UDP_CONNECTIONS; i++) {
964 if (!net->udp_connections[i].in_use)
965 free_con_id = i;
966 if (net->udp_connections[i].in_use &&
967 net->udp_connections[i].inside_udp_port == srcport &&
968 net->udp_connections[i].outside_udp_port == dstport &&
969 memcmp(net->udp_connections[i].inside_ip_address,
970 packet + 26, 4) == 0 &&
971 memcmp(net->udp_connections[i].outside_ip_address,
972 packet + 30, 4) == 0) {
973 con_id = i;
974 break;
975 }
976 }
977
978 debug("&& UDP connection is ");
979 if (con_id >= 0)
980 debug("ONGOING");
981 else {
982 debug("NEW");
983 if (free_con_id < 0) {
984 int i;
985 int64_t oldest = net->
986 udp_connections[0].last_used_timestamp;
987 free_con_id = 0;
988
989 debug(", NO FREE SLOTS, REUSING OLDEST ONE");
990 for (i=0; i<MAX_UDP_CONNECTIONS; i++)
991 if (net->udp_connections[i].
992 last_used_timestamp < oldest) {
993 oldest = net->udp_connections[i].
994 last_used_timestamp;
995 free_con_id = i;
996 }
997 close(net->udp_connections[free_con_id].socket);
998 }
999 con_id = free_con_id;
1000 memset(&net->udp_connections[con_id], 0,
1001 sizeof(struct udp_connection));
1002
1003 memcpy(net->udp_connections[con_id].ethernet_address,
1004 packet + 6, 6);
1005 memcpy(net->udp_connections[con_id].inside_ip_address,
1006 packet + 26, 4);
1007 net->udp_connections[con_id].inside_udp_port = srcport;
1008 memcpy(net->udp_connections[con_id].outside_ip_address,
1009 packet + 30, 4);
1010 net->udp_connections[con_id].outside_udp_port = dstport;
1011
1012 net->udp_connections[con_id].socket = socket(AF_INET,
1013 SOCK_DGRAM, 0);
1014 if (net->udp_connections[con_id].socket < 0) {
1015 fatal("[ net: UDP: socket() returned %i ]\n",
1016 net->udp_connections[con_id].socket);
1017 return;
1018 }
1019
1020 debug(" {socket=%i}", net->udp_connections[con_id].socket);
1021
1022 net->udp_connections[con_id].in_use = 1;
1023
1024 /* Set the socket to non-blocking: */
1025 res = fcntl(net->udp_connections[con_id].socket, F_GETFL);
1026 fcntl(net->udp_connections[con_id].socket, F_SETFL,
1027 res | O_NONBLOCK);
1028 }
1029
1030 debug(", connection id %i\n", con_id);
1031
1032 net->timestamp ++;
1033 net->udp_connections[con_id].last_used_timestamp = net->timestamp;
1034
1035 remote_ip.sin_family = AF_INET;
1036 memcpy((unsigned char *)&remote_ip.sin_addr,
1037 net->udp_connections[con_id].outside_ip_address, 4);
1038
1039 /*
1040 * Special case for the nameserver: If a UDP packet is sent to
1041 * the gateway, it will be forwarded to the nameserver, if it is
1042 * known.
1043 */
1044 if (net->nameserver_known &&
1045 memcmp(net->udp_connections[con_id].outside_ip_address,
1046 &net->gateway_ipv4_addr[0], 4) == 0) {
1047 memcpy((unsigned char *)&remote_ip.sin_addr,
1048 &net->nameserver_ipv4, 4);
1049 net->udp_connections[con_id].fake_ns = 1;
1050 }
1051
1052 remote_ip.sin_port = htons(
1053 net->udp_connections[con_id].outside_udp_port);
1054
1055 res = sendto(net->udp_connections[con_id].socket, packet + 42,
1056 len - 42, 0, (const struct sockaddr *)&remote_ip,
1057 sizeof(remote_ip));
1058
1059 if (res != len-42)
1060 debug("[ net: UDP: unable to send %i bytes ]\n", len-42);
1061 else
1062 debug("[ net: UDP: OK!!! ]\n");
1063 }
1064
1065
1066 /*
1067 * net_ip():
1068 *
1069 * Handle an IP packet, coming from the emulated NIC.
1070 */
1071 static void net_ip(struct net *net, void *extra,
1072 unsigned char *packet, int len)
1073 {
1074 #if 1
1075 int i;
1076
1077 debug("[ net: IP: ");
1078 debug("ver=%02x ", packet[14]);
1079 debug("tos=%02x ", packet[15]);
1080 debug("len=%02x%02x ", packet[16], packet[17]);
1081 debug("id=%02x%02x ", packet[18], packet[19]);
1082 debug("ofs=%02x%02x ", packet[20], packet[21]);
1083 debug("ttl=%02x ", packet[22]);
1084 debug("p=%02x ", packet[23]);
1085 debug("sum=%02x%02x ", packet[24], packet[25]);
1086 debug("src=%02x%02x%02x%02x ",
1087 packet[26], packet[27], packet[28], packet[29]);
1088 debug("dst=%02x%02x%02x%02x ",
1089 packet[30], packet[31], packet[32], packet[33]);
1090 for (i=34; i<len; i++)
1091 debug("%02x", packet[i]);
1092 debug(" ]\n");
1093 #endif
1094
1095 /* Cut off overflowing tail data: */
1096 if (len > 14 + packet[16]*256 + packet[17])
1097 len = 14 + packet[16]*256 + packet[17];
1098
1099 if (packet[14] == 0x45) {
1100 /* IPv4: */
1101 switch (packet[23]) {
1102 case 1: /* ICMP */
1103 net_ip_icmp(net, extra, packet, len);
1104 break;
1105 case 6: /* TCP */
1106 net_ip_tcp(net, extra, packet, len);
1107 break;
1108 case 17:/* UDP */
1109 net_ip_udp(net, extra, packet, len);
1110 break;
1111 default:
1112 fatal("[ net: IP: UNIMPLEMENTED protocol %i ]\n",
1113 packet[23]);
1114 }
1115 } else
1116 fatal("[ net: IP: UNIMPLEMENTED ip, first byte = 0x%02x ]\n",
1117 packet[14]);
1118 }
1119
1120
1121 /*
1122 * net_ip_broadcast_dhcp():
1123 *
1124 * Handle an IPv4 DHCP broadcast packet, coming from the emulated NIC.
1125 *
1126 * Read http://www.ietf.org/rfc/rfc2131.txt for details on DHCP.
1127 * (And http://users.telenet.be/mydotcom/library/network/dhcp.htm.)
1128 */
1129 static void net_ip_broadcast_dhcp(struct net *net, void *extra,
1130 unsigned char *packet, int len)
1131 {
1132 /*
1133 * TODO
1134 */
1135 #if 0
1136 struct ethernet_packet_link *lp;
1137 int i;
1138
1139 fatal("[ net: IPv4 DHCP: ");
1140 #if 0
1141 fatal("ver=%02x ", packet[14]);
1142 fatal("tos=%02x ", packet[15]);
1143 fatal("len=%02x%02x ", packet[16], packet[17]);
1144 fatal("id=%02x%02x ", packet[18], packet[19]);
1145 fatal("ofs=%02x%02x ", packet[20], packet[21]);
1146 fatal("ttl=%02x ", packet[22]);
1147 fatal("p=%02x ", packet[23]);
1148 fatal("sum=%02x%02x ", packet[24], packet[25]);
1149 #endif
1150 fatal("src=%02x%02x%02x%02x ",
1151 packet[26], packet[27], packet[28], packet[29]);
1152 fatal("dst=%02x%02x%02x%02x ",
1153 packet[30], packet[31], packet[32], packet[33]);
1154 #if 0
1155 for (i=34; i<len; i++)
1156 fatal("%02x", packet[i]);
1157 #endif
1158
1159 if (len < 34 + 8 + 236) {
1160 fatal("[ DHCP packet too short? Len=%i ]\n", len);
1161 return;
1162 }
1163
1164 /*
1165 * UDP data (at offset 34):
1166 *
1167 * srcport=0044 dstport=0043 length=0134 chksum=a973
1168 * data = 01010600d116d276000000000000000000000000000000
1169 * 0000000000102030405060...0000...638253633501...000
1170 */
1171
1172 fatal("op=%02x ", packet[42]);
1173 fatal("htype=%02x ", packet[43]);
1174 fatal("hlen=%02x ", packet[44]);
1175 fatal("hops=%02x ", packet[45]);
1176 fatal("xid=%02x%02x%02x%02x ", packet[46], packet[47],
1177 packet[48], packet[49]);
1178 fatal("secs=%02x%02x ", packet[50], packet[51]);
1179 fatal("flags=%02x%02x ", packet[52], packet[53]);
1180 fatal("ciaddr=%02x%02x%02x%02x ", packet[54], packet[55],
1181 packet[56], packet[57]);
1182 fatal("yiaddr=%02x%02x%02x%02x ", packet[58], packet[59],
1183 packet[60], packet[61]);
1184 fatal("siaddr=%02x%02x%02x%02x ", packet[62], packet[63],
1185 packet[64], packet[65]);
1186 fatal("giaddr=%02x%02x%02x%02x ", packet[66], packet[67],
1187 packet[68], packet[69]);
1188 fatal("chaddr=");
1189 for (i=70; i<70+16; i++)
1190 fatal("%02x", packet[i]);
1191 /*
1192 | sname (64) |
1193 | file (128) |
1194 */
1195 fatal(" ]\n");
1196
1197 lp = net_allocate_packet_link(net, extra, len);
1198
1199 /* Copy the old packet first: */
1200 memcpy(lp->data, packet, len);
1201
1202 /* We are sending to the client, from the gateway: */
1203 memcpy(lp->data + 0, packet + 6, 6);
1204 memcpy(lp->data + 6, net->gateway_ethernet_addr, 6);
1205
1206 memcpy(lp->data + 26, &net->gateway_ipv4_addr[0], 4);
1207 lp->data[30] = 0xff;
1208 lp->data[31] = 0xff;
1209 lp->data[32] = 0xff;
1210 lp->data[33] = 0xff;
1211
1212 /* Switch src and dst ports: */
1213 memcpy(lp->data + 34, packet + 36, 2);
1214 memcpy(lp->data + 36, packet + 34, 2);
1215
1216 /* Client's (yiaddr) IPv4 address: */
1217 lp->data[58] = 10;
1218 lp->data[59] = 0;
1219 lp->data[60] = 0;
1220 lp->data[61] = 1;
1221
1222 /* Server's IPv4 address: (giaddr) */
1223 memcpy(lp->data + 66, &net->gateway_ipv4_addr[0], 4);
1224
1225 /* This is a Reply: */
1226 lp->data[42] = 0x02;
1227
1228 snprintf(lp->data + 70+16+64, 8, "gxemul");
1229
1230 /* Recalculate IP header checksum: */
1231 net_ip_checksum(lp->data + 14, 10, 20);
1232
1233 /* ... and the UDP checksum: */
1234 net_ip_tcp_checksum(lp->data + 34, 6, len - 34 - 8,
1235 lp->data + 26, lp->data + 30, 1);
1236
1237
1238 /* Debug dump: */
1239 packet = lp->data;
1240 fatal("[ net: IPv4 DHCP REPLY: ");
1241 for (i=0; i<14; i++)
1242 fatal("%02x", packet[i]);
1243 fatal("ver=%02x ", packet[14]);
1244 fatal("tos=%02x ", packet[15]);
1245 fatal("len=%02x%02x ", packet[16], packet[17]);
1246 fatal("id=%02x%02x ", packet[18], packet[19]);
1247 fatal("ofs=%02x%02x ", packet[20], packet[21]);
1248 fatal("ttl=%02x ", packet[22]);
1249 fatal("p=%02x ", packet[23]);
1250 fatal("sum=%02x%02x ", packet[24], packet[25]);
1251 fatal("src=%02x%02x%02x%02x ",
1252 packet[26], packet[27], packet[28], packet[29]);
1253 fatal("dst=%02x%02x%02x%02x ",
1254 packet[30], packet[31], packet[32], packet[33]);
1255 fatal("op=%02x ", packet[42]);
1256 fatal("htype=%02x ", packet[43]);
1257 fatal("hlen=%02x ", packet[44]);
1258 fatal("hops=%02x ", packet[45]);
1259 fatal("xid=%02x%02x%02x%02x ", packet[46], packet[47],
1260 packet[48], packet[49]);
1261 fatal("secs=%02x%02x ", packet[50], packet[51]);
1262 fatal("flags=%02x%02x ", packet[52], packet[53]);
1263 fatal("ciaddr=%02x%02x%02x%02x ", packet[54], packet[55],
1264 packet[56], packet[57]);
1265 fatal("yiaddr=%02x%02x%02x%02x ", packet[58], packet[59],
1266 packet[60], packet[61]);
1267 fatal("siaddr=%02x%02x%02x%02x ", packet[62], packet[63],
1268 packet[64], packet[65]);
1269 fatal("giaddr=%02x%02x%02x%02x ", packet[66], packet[67],
1270 packet[68], packet[69]);
1271 fatal("chaddr=");
1272 for (i=70; i<70+16; i++)
1273 fatal("%02x", packet[i]);
1274 fatal(" ]\n");
1275
1276 #endif
1277 }
1278
1279
1280 /*
1281 * net_ip_broadcast():
1282 *
1283 * Handle an IP broadcast packet, coming from the emulated NIC.
1284 * (This is usually a DHCP request, or similar.)
1285 */
1286 static void net_ip_broadcast(struct net *net, void *extra,
1287 unsigned char *packet, int len)
1288 {
1289 unsigned char *p = (void *) &net->netmask_ipv4;
1290 uint32_t x, y;
1291 int i, xl, warning = 0, match = 0;
1292
1293 #if 0
1294 fatal("[ net: IP BROADCAST: ");
1295 fatal("ver=%02x ", packet[14]);
1296 fatal("tos=%02x ", packet[15]);
1297 fatal("len=%02x%02x ", packet[16], packet[17]);
1298 fatal("id=%02x%02x ", packet[18], packet[19]);
1299 fatal("ofs=%02x%02x ", packet[20], packet[21]);
1300 fatal("ttl=%02x ", packet[22]);
1301 fatal("p=%02x ", packet[23]);
1302 fatal("sum=%02x%02x ", packet[24], packet[25]);
1303 fatal("src=%02x%02x%02x%02x ",
1304 packet[26], packet[27], packet[28], packet[29]);
1305 fatal("dst=%02x%02x%02x%02x ",
1306 packet[30], packet[31], packet[32], packet[33]);
1307 for (i=34; i<len; i++)
1308 fatal("%02x", packet[i]);
1309 fatal(" ]\n");
1310 #endif
1311
1312 /* Check for 10.0.0.255 first, maybe some guest OSes think that
1313 it's a /24 network, regardless of what it actually is. */
1314 y = (packet[30] << 24) + (packet[31] << 16) +
1315 (packet[32] << 8) + packet[33];
1316
1317 x = (p[0] << 24) + (p[1] << 16) + (p[2] << 8) + p[3];
1318 /* Example: x = 10.0.0.0 */
1319 x |= 255;
1320
1321 if (x == y) {
1322 warning = 1;
1323 match = 1;
1324 }
1325
1326 xl = 32 - net->netmask_ipv4_len;
1327 x |= (1 << xl) - 1;
1328 /* x = 10.255.255.255 */
1329
1330 if (x == y)
1331 match = 1;
1332 if (y == 0xffffffff)
1333 match = 1;
1334
1335 if (warning)
1336 fatal("[ net_ip_broadcast(): warning: broadcast to "
1337 "0x%08x, expecting broadcast to 0x%08x or "
1338 "0xffffffff ]\n", y, x);
1339
1340 /* Cut off overflowing tail data: */
1341 if (len > 14 + packet[16]*256 + packet[17])
1342 len = 14 + packet[16]*256 + packet[17];
1343
1344 /* Check for known packets: */
1345 if (packet[14] == 0x45 && /* IPv4 */
1346 packet[23] == 0x11 && /* UDP */
1347 packet[34] == 0 && packet[35] == 68 && /* DHCP client */
1348 packet[36] == 0 && packet[37] == 67) { /* DHCP server */
1349 net_ip_broadcast_dhcp(net, extra, packet, len);
1350 return;
1351 }
1352
1353 /* Unknown packet: */
1354 fatal("[ net: UNIMPLEMENTED IP BROADCAST: ");
1355 fatal("ver=%02x ", packet[14]);
1356 fatal("tos=%02x ", packet[15]);
1357 fatal("len=%02x%02x ", packet[16], packet[17]);
1358 fatal("id=%02x%02x ", packet[18], packet[19]);
1359 fatal("ofs=%02x%02x ", packet[20], packet[21]);
1360 fatal("ttl=%02x ", packet[22]);
1361 fatal("p=%02x ", packet[23]);
1362 fatal("sum=%02x%02x ", packet[24], packet[25]);
1363 fatal("src=%02x%02x%02x%02x ",
1364 packet[26], packet[27], packet[28], packet[29]);
1365 fatal("dst=%02x%02x%02x%02x ",
1366 packet[30], packet[31], packet[32], packet[33]);
1367 for (i=34; i<len; i++)
1368 fatal("%02x", packet[i]);
1369 fatal(" ]\n");
1370 }
1371
1372
1373 /*
1374 * net_arp():
1375 *
1376 * Handle an ARP (or RARP) packet, coming from the emulated NIC.
1377 *
1378 * An ARP packet might look like this:
1379 *
1380 * ARP header:
1381 * ARP hardware addr family: 0001
1382 * ARP protocol addr family: 0800
1383 * ARP addr lengths: 06 04
1384 * ARP request: 0001
1385 * ARP from: 112233445566 01020304
1386 * ARP to: 000000000000 01020301
1387 *
1388 * An ARP request with a 'to' IP value of the gateway should cause an
1389 * ARP response packet to be created.
1390 *
1391 * An ARP request with the same from and to IP addresses should be ignored.
1392 * (This would be a host testing to see if there is an IP collision.)
1393 */
1394 static void net_arp(struct net *net, void *extra,
1395 unsigned char *packet, int len, int reverse)
1396 {
1397 int q;
1398 int i;
1399
1400 /* TODO: This debug dump assumes ethernet->IPv4 translation: */
1401 if (reverse)
1402 debug("[ net: RARP: ");
1403 else
1404 debug("[ net: ARP: ");
1405 for (i=0; i<2; i++)
1406 debug("%02x", packet[i]);
1407 debug(" ");
1408 for (i=2; i<4; i++)
1409 debug("%02x", packet[i]);
1410 debug(" ");
1411 debug("%02x", packet[4]);
1412 debug(" ");
1413 debug("%02x", packet[5]);
1414 debug(" req=");
1415 debug("%02x", packet[6]); /* Request type */
1416 debug("%02x", packet[7]);
1417 debug(" from=");
1418 for (i=8; i<18; i++)
1419 debug("%02x", packet[i]);
1420 debug(" to=");
1421 for (i=18; i<28; i++)
1422 debug("%02x", packet[i]);
1423 debug(" ]\n");
1424
1425 if (packet[0] == 0x00 && packet[1] == 0x01 &&
1426 packet[2] == 0x08 && packet[3] == 0x00 &&
1427 packet[4] == 0x06 && packet[5] == 0x04) {
1428 int r = (packet[6] << 8) + packet[7];
1429 struct ethernet_packet_link *lp;
1430
1431 switch (r) {
1432 case 1: /* Request */
1433 /* Only create a reply if this was meant for the
1434 gateway: */
1435 if (memcmp(packet+24, net->gateway_ipv4_addr, 4) != 0)
1436 break;
1437
1438 lp = net_allocate_packet_link(net, extra, 60 + 14);
1439
1440 /* Copy the old packet first: */
1441 memset(lp->data, 0, 60 + 14);
1442 memcpy(lp->data + 14, packet, len);
1443
1444 /* Add ethernet ARP header: */
1445 memcpy(lp->data + 0, lp->data + 8 + 14, 6);
1446 memcpy(lp->data + 6, net->gateway_ethernet_addr, 6);
1447 lp->data[12] = 0x08; lp->data[13] = 0x06;
1448
1449 /* Address of the emulated machine: */
1450 memcpy(lp->data + 18 + 14, lp->data + 8 + 14, 10);
1451
1452 /* Address of the gateway: */
1453 memcpy(lp->data + 8 + 14, net->gateway_ethernet_addr,
1454 6);
1455 memcpy(lp->data + 14 + 14, net->gateway_ipv4_addr, 4);
1456
1457 /* This is a Reply: */
1458 lp->data[6 + 14] = 0x00; lp->data[7 + 14] = 0x02;
1459
1460 break;
1461 case 3: /* Reverse Request */
1462 lp = net_allocate_packet_link(net, extra, 60 + 14);
1463
1464 /* Copy the old packet first: */
1465 memset(lp->data, 0, 60 + 14);
1466 memcpy(lp->data + 14, packet, len);
1467
1468 /* Add ethernet RARP header: */
1469 memcpy(lp->data + 0, packet + 8, 6);
1470 memcpy(lp->data + 6, net->gateway_ethernet_addr, 6);
1471 lp->data[12] = 0x80; lp->data[13] = 0x35;
1472
1473 /* This is a RARP reply: */
1474 lp->data[6 + 14] = 0x00; lp->data[7 + 14] = 0x04;
1475
1476 /* Address of the gateway: */
1477 memcpy(lp->data + 8 + 14, net->gateway_ethernet_addr,
1478 6);
1479 memcpy(lp->data + 14 + 14, net->gateway_ipv4_addr, 4);
1480
1481 /* MAC address of emulated machine: */
1482 memcpy(lp->data + 18 + 14, packet + 8, 6);
1483
1484 /*
1485 * IP address of the emulated machine: Automagically
1486 * generated from the MAC address. :-)
1487 *
1488 * packet+8 points to the client's mac address,
1489 * for example 10:20:30:00:00:z0, where z is 0..15.
1490 * 10:20:30:00:00:10 results in 10.0.0.1.
1491 */
1492 /* q = (packet[8 + 3]) >> 4; */
1493 /* q = q*15 + ((packet[8 + 4]) >> 4); */
1494 q = (packet[8 + 5]) >> 4;
1495 lp->data[24 + 14] = 10;
1496 lp->data[25 + 14] = 0;
1497 lp->data[26 + 14] = 0;
1498 lp->data[27 + 14] = q;
1499 break;
1500 case 2: /* Reply */
1501 case 4: /* Reverse Reply */
1502 default:
1503 fatal("[ net: ARP: UNIMPLEMENTED request type "
1504 "0x%04x ]\n", r);
1505 }
1506 } else {
1507 fatal("[ net: ARP: UNIMPLEMENTED arp packet type: ");
1508 for (i=0; i<len; i++)
1509 fatal("%02x", packet[i]);
1510 fatal(" ]\n");
1511 }
1512 }
1513
1514
1515 /*
1516 * net_ethernet_rx_avail():
1517 *
1518 * Return 1 if there is a packet available for this 'extra' pointer, otherwise
1519 * return 0.
1520 *
1521 * Appart from actually checking for incoming packets from the outside world,
1522 * this function basically works like net_ethernet_rx() but it only receives
1523 * a return value telling us whether there is a packet or not, we don't
1524 * actually get the packet.
1525 */
1526 int net_ethernet_rx_avail(struct net *net, void *extra)
1527 {
1528 int received_packets_this_tick = 0;
1529 int max_packets_this_tick = 200;
1530 int con_id;
1531
1532 if (net == NULL)
1533 return 0;
1534
1535 /*
1536 * If the network is distributed across multiple emulator processes,
1537 * then receive incoming packets from those processes.
1538 */
1539 if (net->local_port != 0) {
1540 struct sockaddr_in si;
1541 socklen_t si_len = sizeof(si);
1542 int res, i;
1543 unsigned char buf[60000];
1544
1545 if ((res = recvfrom(net->local_port_socket, buf, sizeof(buf), 0,
1546 (struct sockaddr *)&si, &si_len)) != -1) {
1547 /* fatal("DISTRIBUTED packet, %i bytes from %s:%d\n",
1548 res, inet_ntoa(si.sin_addr), ntohs(si.sin_port)); */
1549 for (i=0; i<net->n_nics; i++) {
1550 struct ethernet_packet_link *lp;
1551 lp = net_allocate_packet_link(net,
1552 net->nic_extra[i], res);
1553 memcpy(lp->data, buf, res);
1554 }
1555 }
1556 }
1557
1558 /*
1559 * UDP:
1560 */
1561 for (con_id=0; con_id<MAX_UDP_CONNECTIONS; con_id++) {
1562 ssize_t res;
1563 unsigned char buf[66000];
1564 unsigned char udp_data[66008];
1565 struct sockaddr_in from;
1566 socklen_t from_len = sizeof(from);
1567 int ip_len, udp_len;
1568 struct ethernet_packet_link *lp;
1569 int max_per_packet;
1570 int bytes_converted = 0;
1571 int this_packets_data_length;
1572 int fragment_ofs = 0;
1573
1574 if (received_packets_this_tick > max_packets_this_tick)
1575 break;
1576
1577 if (!net->udp_connections[con_id].in_use)
1578 continue;
1579
1580 if (net->udp_connections[con_id].socket < 0) {
1581 fatal("INTERNAL ERROR in net.c, udp socket < 0 "
1582 "but in use?\n");
1583 continue;
1584 }
1585
1586 res = recvfrom(net->udp_connections[con_id].socket, buf,
1587 sizeof(buf), 0, (struct sockaddr *)&from, &from_len);
1588
1589 /* No more incoming UDP on this connection? */
1590 if (res < 0)
1591 continue;
1592
1593 net->timestamp ++;
1594 net->udp_connections[con_id].last_used_timestamp =
1595 net->timestamp;
1596
1597 net->udp_connections[con_id].udp_id ++;
1598
1599 /*
1600 * Special case for the nameserver: If a UDP packet is
1601 * received from the nameserver (if the nameserver's IP is
1602 * known), fake it so that it comes from the gateway instead.
1603 */
1604 if (net->udp_connections[con_id].fake_ns)
1605 memcpy(((unsigned char *)(&from))+4,
1606 &net->gateway_ipv4_addr[0], 4);
1607
1608 /*
1609 * We now have a UDP packet of size 'res' which we need
1610 * turn into one or more ethernet packets for the emulated
1611 * operating system. Ethernet packets are at most 1518
1612 * bytes long. With some margin, that means we can have
1613 * about 1500 bytes per packet.
1614 *
1615 * Ethernet = 14 bytes
1616 * IP = 20 bytes
1617 * (UDP = 8 bytes + data)
1618 *
1619 * So data can be at most max_per_packet - 34. For UDP
1620 * fragments, each multiple should (?) be a multiple of
1621 * 8 bytes, except the last which doesn't have any such
1622 * restriction.
1623 */
1624 max_per_packet = 1500;
1625
1626 /* UDP: */
1627 udp_len = res + 8;
1628 /* from[2..3] = outside_udp_port */
1629 udp_data[0] = ((unsigned char *)&from)[2];
1630 udp_data[1] = ((unsigned char *)&from)[3];
1631 udp_data[2] = (net->udp_connections[con_id].
1632 inside_udp_port >> 8) & 0xff;
1633 udp_data[3] = net->udp_connections[con_id].
1634 inside_udp_port & 0xff;
1635 udp_data[4] = udp_len >> 8;
1636 udp_data[5] = udp_len & 0xff;
1637 udp_data[6] = 0;
1638 udp_data[7] = 0;
1639 memcpy(udp_data + 8, buf, res);
1640 /*
1641 * TODO: UDP checksum, if necessary. At least NetBSD
1642 * and OpenBSD accept UDP packets with 0x0000 in the
1643 * checksum field anyway.
1644 */
1645
1646 while (bytes_converted < udp_len) {
1647 this_packets_data_length = udp_len - bytes_converted;
1648
1649 /* Do we need to fragment? */
1650 if (this_packets_data_length > max_per_packet-34) {
1651 this_packets_data_length =
1652 max_per_packet - 34;
1653 while (this_packets_data_length & 7)
1654 this_packets_data_length --;
1655 }
1656
1657 ip_len = 20 + this_packets_data_length;
1658
1659 lp = net_allocate_packet_link(net, extra,
1660 14 + 20 + this_packets_data_length);
1661
1662 /* Ethernet header: */
1663 memcpy(lp->data + 0, net->udp_connections[con_id].
1664 ethernet_address, 6);
1665 memcpy(lp->data + 6, net->gateway_ethernet_addr, 6);
1666 lp->data[12] = 0x08; /* IP = 0x0800 */
1667 lp->data[13] = 0x00;
1668
1669 /* IP header: */
1670 lp->data[14] = 0x45; /* ver */
1671 lp->data[15] = 0x00; /* tos */
1672 lp->data[16] = ip_len >> 8;
1673 lp->data[17] = ip_len & 0xff;
1674 lp->data[18] = net->udp_connections[con_id].udp_id >> 8;
1675 lp->data[19] = net->udp_connections[con_id].udp_id
1676 & 0xff;
1677 lp->data[20] = (fragment_ofs >> 8);
1678 if (bytes_converted + this_packets_data_length
1679 < udp_len)
1680 lp->data[20] |= 0x20; /* More fragments */
1681 lp->data[21] = fragment_ofs & 0xff;
1682 lp->data[22] = 0x40; /* ttl */
1683 lp->data[23] = 17; /* p = UDP */
1684 lp->data[26] = ((unsigned char *)&from)[4];
1685 lp->data[27] = ((unsigned char *)&from)[5];
1686 lp->data[28] = ((unsigned char *)&from)[6];
1687 lp->data[29] = ((unsigned char *)&from)[7];
1688 memcpy(lp->data + 30, net->udp_connections[con_id].
1689 inside_ip_address, 4);
1690 net_ip_checksum(lp->data + 14, 10, 20);
1691
1692 memcpy(lp->data+34, udp_data + bytes_converted,
1693 this_packets_data_length);
1694
1695 bytes_converted += this_packets_data_length;
1696 fragment_ofs = bytes_converted / 8;
1697
1698 received_packets_this_tick ++;
1699 }
1700
1701 /* This makes sure we check this connection AGAIN
1702 for more incoming UDP packets, before moving to the
1703 next connection: */
1704 con_id --;
1705 }
1706
1707 /*
1708 * TCP:
1709 */
1710 for (con_id=0; con_id<MAX_TCP_CONNECTIONS; con_id++) {
1711 unsigned char buf[66000];
1712 ssize_t res, res2;
1713 fd_set rfds;
1714 struct timeval tv;
1715
1716 if (received_packets_this_tick > max_packets_this_tick)
1717 break;
1718
1719 if (!net->tcp_connections[con_id].in_use)
1720 continue;
1721
1722 if (net->tcp_connections[con_id].socket < 0) {
1723 fatal("INTERNAL ERROR in net.c, tcp socket < 0"
1724 " but in use?\n");
1725 continue;
1726 }
1727
1728 if (net->tcp_connections[con_id].incoming_buf == NULL) {
1729 net->tcp_connections[con_id].incoming_buf =
1730 malloc(TCP_INCOMING_BUF_LEN);
1731 if (net->tcp_connections[con_id].incoming_buf == NULL) {
1732 printf("out of memory allocating "
1733 "incoming_buf for con_id %i\n", con_id);
1734 exit(1);
1735 }
1736 }
1737
1738 if (net->tcp_connections[con_id].state >=
1739 TCP_OUTSIDE_DISCONNECTED)
1740 continue;
1741
1742 /* Is the socket available for output? */
1743 FD_ZERO(&rfds); /* write */
1744 FD_SET(net->tcp_connections[con_id].socket, &rfds);
1745 tv.tv_sec = tv.tv_usec = 0;
1746 errno = 0;
1747 res = select(net->tcp_connections[con_id].socket+1,
1748 NULL, &rfds, NULL, &tv);
1749
1750 if (errno == ECONNREFUSED) {
1751 fatal("[ ECONNREFUSED: TODO ]\n");
1752 net->tcp_connections[con_id].state =
1753 TCP_OUTSIDE_DISCONNECTED;
1754 fatal("CHANGING TO TCP_OUTSIDE_DISCONNECTED "
1755 "(refused connection)\n");
1756 continue;
1757 }
1758
1759 if (errno == ETIMEDOUT) {
1760 fatal("[ ETIMEDOUT: TODO ]\n");
1761 /* TODO */
1762 net->tcp_connections[con_id].state =
1763 TCP_OUTSIDE_DISCONNECTED;
1764 fatal("CHANGING TO TCP_OUTSIDE_DISCONNECTED "
1765 "(timeout)\n");
1766 continue;
1767 }
1768
1769 if (net->tcp_connections[con_id].state ==
1770 TCP_OUTSIDE_TRYINGTOCONNECT && res > 0) {
1771 net->tcp_connections[con_id].state =
1772 TCP_OUTSIDE_CONNECTED;
1773 debug("CHANGING TO TCP_OUTSIDE_CONNECTED\n");
1774 net_ip_tcp_connectionreply(net, extra, con_id, 1,
1775 NULL, 0, 0);
1776 }
1777
1778 if (net->tcp_connections[con_id].state ==
1779 TCP_OUTSIDE_CONNECTED && res < 1) {
1780 continue;
1781 }
1782
1783 /*
1784 * Does this connection have unacknowledged data? Then, if
1785 * enough number of rounds have passed, try to resend it using
1786 * the old value of seqnr.
1787 */
1788 if (net->tcp_connections[con_id].incoming_buf_len != 0) {
1789 net->tcp_connections[con_id].incoming_buf_rounds ++;
1790 if (net->tcp_connections[con_id].incoming_buf_rounds >
1791 10000) {
1792 debug(" at seqnr %u but backing back to %u,"
1793 " resending %i bytes\n",
1794 net->tcp_connections[con_id].outside_seqnr,
1795 net->tcp_connections[con_id].
1796 incoming_buf_seqnr,
1797 net->tcp_connections[con_id].
1798 incoming_buf_len);
1799
1800 net->tcp_connections[con_id].
1801 incoming_buf_rounds = 0;
1802 net->tcp_connections[con_id].outside_seqnr =
1803 net->tcp_connections[con_id].
1804 incoming_buf_seqnr;
1805
1806 net_ip_tcp_connectionreply(net, extra, con_id,
1807 0, net->tcp_connections[con_id].
1808 incoming_buf,
1809 net->tcp_connections[con_id].
1810 incoming_buf_len, 0);
1811 }
1812 continue;
1813 }
1814
1815 /* Don't receive unless the guest OS is ready! */
1816 if (((int32_t)net->tcp_connections[con_id].outside_seqnr -
1817 (int32_t)net->tcp_connections[con_id].inside_acknr) > 0) {
1818 /* fatal("YOYO 1! outside_seqnr - inside_acknr = %i\n",
1819 net->tcp_connections[con_id].outside_seqnr -
1820 net->tcp_connections[con_id].inside_acknr); */
1821 continue;
1822 }
1823
1824 /* Is there incoming data available on the socket? */
1825 FD_ZERO(&rfds); /* read */
1826 FD_SET(net->tcp_connections[con_id].socket, &rfds);
1827 tv.tv_sec = tv.tv_usec = 0;
1828 res2 = select(net->tcp_connections[con_id].socket+1, &rfds,
1829 NULL, NULL, &tv);
1830
1831 /* No more incoming TCP data on this connection? */
1832 if (res2 < 1)
1833 continue;
1834
1835 res = read(net->tcp_connections[con_id].socket, buf, 1400);
1836 if (res > 0) {
1837 /* debug("\n -{- %lli -}-\n", (long long)res); */
1838 net->tcp_connections[con_id].incoming_buf_len = res;
1839 net->tcp_connections[con_id].incoming_buf_rounds = 0;
1840 net->tcp_connections[con_id].incoming_buf_seqnr =
1841 net->tcp_connections[con_id].outside_seqnr;
1842 debug(" putting %i bytes (seqnr %u) in the incoming "
1843 "buf\n", res, net->tcp_connections[con_id].
1844 incoming_buf_seqnr);
1845 memcpy(net->tcp_connections[con_id].incoming_buf,
1846 buf, res);
1847
1848 net_ip_tcp_connectionreply(net, extra, con_id, 0,
1849 buf, res, 0);
1850 } else if (res == 0) {
1851 net->tcp_connections[con_id].state =
1852 TCP_OUTSIDE_DISCONNECTED;
1853 debug("CHANGING TO TCP_OUTSIDE_DISCONNECTED, read"
1854 " res=0\n");
1855 net_ip_tcp_connectionreply(net, extra, con_id, 0,
1856 NULL, 0, 0);
1857 } else {
1858 net->tcp_connections[con_id].state =
1859 TCP_OUTSIDE_DISCONNECTED;
1860 fatal("CHANGING TO TCP_OUTSIDE_DISCONNECTED, "
1861 "read res<=0, errno = %i\n", errno);
1862 net_ip_tcp_connectionreply(net, extra, con_id, 0,
1863 NULL, 0, 0);
1864 }
1865
1866 net->timestamp ++;
1867 net->tcp_connections[con_id].last_used_timestamp =
1868 net->timestamp;
1869 }
1870
1871 return net_ethernet_rx(net, extra, NULL, NULL);
1872 }
1873
1874
1875 /*
1876 * net_ethernet_rx():
1877 *
1878 * Receive an ethernet packet. (This means handing over an already prepared
1879 * packet from this module (net.c) to a specific ethernet controller device.)
1880 *
1881 * Return value is 1 if there was a packet available. *packetp and *lenp
1882 * will be set to the packet's data pointer and length, respectively, and
1883 * the packet will be removed from the linked list). If there was no packet
1884 * available, 0 is returned.
1885 *
1886 * If packetp is NULL, then the search is aborted as soon as a packet with
1887 * the correct 'extra' field is found, and a 1 is returned, but as packetp
1888 * is NULL we can't return the actual packet. (This is the internal form
1889 * if net_ethernet_rx_avail().)
1890 */
1891 int net_ethernet_rx(struct net *net, void *extra,
1892 unsigned char **packetp, int *lenp)
1893 {
1894 struct ethernet_packet_link *lp, *prev;
1895
1896 if (net == NULL)
1897 return 0;
1898
1899 /* Find the first packet which has the right 'extra' field. */
1900
1901 lp = net->first_ethernet_packet;
1902 prev = NULL;
1903 while (lp != NULL) {
1904 if (lp->extra == extra) {
1905 /* We found a packet for this controller! */
1906 if (packetp == NULL || lenp == NULL)
1907 return 1;
1908
1909 /* Let's return it: */
1910 (*packetp) = lp->data;
1911 (*lenp) = lp->len;
1912
1913 /* Remove this link from the linked list: */
1914 if (prev == NULL)
1915 net->first_ethernet_packet = lp->next;
1916 else
1917 prev->next = lp->next;
1918
1919 if (lp->next == NULL)
1920 net->last_ethernet_packet = prev;
1921 else
1922 lp->next->prev = prev;
1923
1924 free(lp);
1925
1926 /* ... and return successfully: */
1927 return 1;
1928 }
1929
1930 prev = lp;
1931 lp = lp->next;
1932 }
1933
1934 /* No packet found. :-( */
1935 return 0;
1936 }
1937
1938
1939 /*
1940 * send_udp():
1941 *
1942 * Send a simple UDP packet to some other (real) host. Used for distributed
1943 * network simulations.
1944 */
1945 void send_udp(struct in_addr *addrp, int portnr, unsigned char *packet,
1946 size_t len)
1947 {
1948 int s;
1949 struct sockaddr_in si;
1950
1951 s = socket(AF_INET, SOCK_DGRAM, 0);
1952 if (s < 0) {
1953 perror("send_udp(): socket");
1954 return;
1955 }
1956
1957 /* fatal("send_udp(): sending to port %i\n", portnr); */
1958
1959 si.sin_family = AF_INET;
1960 si.sin_addr = *addrp;
1961 si.sin_port = htons(portnr);
1962
1963 if (sendto(s, packet, len, 0, (struct sockaddr *)&si,
1964 sizeof(si)) != len) {
1965 perror("send_udp(): sendto");
1966 }
1967
1968 close(s);
1969 }
1970
1971
1972 /*
1973 * net_ethernet_tx():
1974 *
1975 * Transmit an ethernet packet, as seen from the emulated ethernet controller.
1976 * If the packet can be handled here, it will not necessarily be transmitted
1977 * to the outside world.
1978 */
1979 void net_ethernet_tx(struct net *net, void *extra,
1980 unsigned char *packet, int len)
1981 {
1982 int i, n;
1983
1984 if (net == NULL)
1985 return;
1986
1987 /* Drop too small packets: */
1988 if (len < 20)
1989 return;
1990
1991 /*
1992 * Copy this packet to all other NICs on this network:
1993 */
1994 if (extra != NULL && net->n_nics > 0) {
1995 for (i=0; i<net->n_nics; i++)
1996 if (extra != net->nic_extra[i]) {
1997 struct ethernet_packet_link *lp;
1998 lp = net_allocate_packet_link(net,
1999 net->nic_extra[i], len);
2000
2001 /* Copy the entire packet: */
2002 memcpy(lp->data, packet, len);
2003 }
2004 }
2005
2006 /*
2007 * If this network is distributed across multiple emulator processes,
2008 * then transmit the packet to those other processes.
2009 */
2010 if (net->remote_nets != NULL) {
2011 struct remote_net *rnp = net->remote_nets;
2012 while (rnp != NULL) {
2013 send_udp(&rnp->ipv4_addr, rnp->portnr, packet, len);
2014 rnp = rnp->next;
2015 }
2016 }
2017
2018 /* Drop packets that are not destined for the gateway: */
2019 if (memcmp(packet, net->gateway_ethernet_addr, 6) != 0
2020 && packet[0] != 0xff && packet[0] != 0x00)
2021 return;
2022
2023 /*
2024 * The code below simulates the behaviour of a "NAT"-style
2025 * gateway.
2026 */
2027 #if 0
2028 fatal("[ net: ethernet: ");
2029 for (i=0; i<6; i++) fatal("%02x", packet[i]); fatal(" ");
2030 for (i=6; i<12; i++) fatal("%02x", packet[i]); fatal(" ");
2031 for (i=12; i<14; i++) fatal("%02x", packet[i]); fatal(" ");
2032 for (i=14; i<len; i++) fatal("%02x", packet[i]); fatal(" ]\n");
2033 #endif
2034
2035 /* Sprite: */
2036 if (packet[12] == 0x05 && packet[13] == 0x00) {
2037 /* TODO. */
2038 fatal("[ net: TX: UNIMPLEMENTED Sprite packet ]\n");
2039 return;
2040 }
2041
2042 /* IP: */
2043 if (packet[12] == 0x08 && packet[13] == 0x00) {
2044 /* Routed via the gateway? */
2045 if (memcmp(packet+0, net->gateway_ethernet_addr, 6) == 0) {
2046 net_ip(net, extra, packet, len);
2047 return;
2048 }
2049
2050 /* Broadcast? (DHCP does this.) */
2051 n = 0;
2052 for (i=0; i<6; i++)
2053 if (packet[i] == 0xff)
2054 n++;
2055 if (n == 6) {
2056 net_ip_broadcast(net, extra, packet, len);
2057 return;
2058 }
2059
2060 if (net->n_nics < 2) {
2061 fatal("[ net: TX: IP packet not for gateway, "
2062 "and not broadcast: ");
2063 for (i=0; i<14; i++)
2064 fatal("%02x", packet[i]);
2065 fatal(" ]\n");
2066 }
2067 return;
2068 }
2069
2070 /* ARP: */
2071 if (packet[12] == 0x08 && packet[13] == 0x06) {
2072 if (len != 42 && len != 60)
2073 fatal("[ net_ethernet_tx: WARNING! unusual "
2074 "ARP len (%i) ]\n", len);
2075 net_arp(net, extra, packet + 14, len - 14, 0);
2076 return;
2077 }
2078
2079 /* RARP: */
2080 if (packet[12] == 0x80 && packet[13] == 0x35) {
2081 net_arp(net, extra, packet + 14, len - 14, 1);
2082 return;
2083 }
2084
2085 /* IPv6: */
2086 if (packet[12] == 0x86 && packet[13] == 0xdd) {
2087 /* TODO. */
2088 fatal("[ net: TX: UNIMPLEMENTED IPv6 packet ]\n");
2089 return;
2090 }
2091
2092 fatal("[ net: TX: UNIMPLEMENTED ethernet packet type 0x%02x%02x! ]\n",
2093 packet[12], packet[13]);
2094 }
2095
2096
2097 /*
2098 * parse_resolvconf():
2099 *
2100 * This function parses "/etc/resolv.conf" to figure out the nameserver
2101 * and domain used by the host.
2102 */
2103 static void parse_resolvconf(struct net *net)
2104 {
2105 FILE *f;
2106 char buf[8000];
2107 size_t len;
2108 int res;
2109 unsigned int i, start;
2110
2111 /*
2112 * This is a very ugly hack, which tries to figure out which
2113 * nameserver the host uses by looking for the string 'nameserver'
2114 * in /etc/resolv.conf.
2115 *
2116 * This can later on be used for DHCP autoconfiguration. (TODO)
2117 *
2118 * TODO: This is hardcoded to use /etc/resolv.conf. Not all
2119 * operating systems use that filename.
2120 *
2121 * TODO: This is hardcoded for AF_INET (that is, IPv4).
2122 *
2123 * TODO: This assumes that the first nameserver listed is the
2124 * one to use.
2125 */
2126 f = fopen("/etc/resolv.conf", "r");
2127 if (f == NULL)
2128 return;
2129
2130 /* TODO: get rid of the hardcoded values */
2131 memset(buf, 0, sizeof(buf));
2132 len = fread(buf, 1, sizeof(buf) - 100, f);
2133 fclose(f);
2134 buf[sizeof(buf) - 1] = '\0';
2135
2136 for (i=0; i<len; i++)
2137 if (strncmp(buf+i, "nameserver", 10) == 0) {
2138 char *p;
2139
2140 /*
2141 * "nameserver" (1 or more whitespace)
2142 * "x.y.z.w" (non-digit)
2143 */
2144
2145 /* debug("found nameserver at offset %i\n", i); */
2146 i += 10;
2147 while (i<len && (buf[i]==' ' || buf[i]=='\t'))
2148 i++;
2149 if (i >= len)
2150 break;
2151 start = i;
2152
2153 p = buf+start;
2154 while ((*p >= '0' && *p <= '9') || *p == '.')
2155 p++;
2156 *p = '\0';
2157
2158 #ifdef HAVE_INET_PTON
2159 res = inet_pton(AF_INET, buf + start,
2160 &net->nameserver_ipv4);
2161 #else
2162 res = inet_aton(buf + start, &net->nameserver_ipv4);
2163 #endif
2164 if (res < 1)
2165 break;
2166
2167 net->nameserver_known = 1;
2168 break;
2169 }
2170
2171 for (i=0; i<len; i++)
2172 if (strncmp(buf+i, "domain", 6) == 0) {
2173 /* "domain" (1 or more whitespace) domain_name */
2174 i += 6;
2175 while (i<len && (buf[i]==' ' || buf[i]=='\t'))
2176 i++;
2177 if (i >= len)
2178 break;
2179
2180 start = i;
2181 while (i<len && buf[i]!='\n' && buf[i]!='\r')
2182 i++;
2183 if (i < len)
2184 buf[i] = '\0';
2185 /* fatal("DOMAIN='%s'\n", buf + start); */
2186 net->domain_name = strdup(buf + start);
2187 break;
2188 }
2189 }
2190
2191
2192 /*
2193 * net_add_nic():
2194 *
2195 * Add a NIC to a network. (All NICs on a network will see each other's
2196 * packets.)
2197 */
2198 void net_add_nic(struct net *net, void *extra, unsigned char *macaddr)
2199 {
2200 if (net == NULL)
2201 return;
2202
2203 if (extra == NULL) {
2204 fprintf(stderr, "net_add_nic(): extra = NULL\n");
2205 exit(1);
2206 }
2207
2208 net->n_nics ++;
2209 net->nic_extra = realloc(net->nic_extra, sizeof(void *)
2210 * net->n_nics);
2211 if (net->nic_extra == NULL) {
2212 fprintf(stderr, "net_add_nic(): out of memory\n");
2213 exit(1);
2214 }
2215
2216 net->nic_extra[net->n_nics - 1] = extra;
2217 }
2218
2219
2220 /*
2221 * net_gateway_init():
2222 *
2223 * This function creates a "gateway" machine (for example at IPv4 address
2224 * 10.0.0.254, if the net is 10.0.0.0/8), which acts as a gateway/router/
2225 * nameserver etc.
2226 */
2227 static void net_gateway_init(struct net *net)
2228 {
2229 unsigned char *p = (void *) &net->netmask_ipv4;
2230 uint32_t x;
2231 int xl;
2232
2233 x = (p[0] << 24) + (p[1] << 16) + (p[2] << 8) + p[3];
2234 xl = 32 - net->netmask_ipv4_len;
2235 if (xl > 8)
2236 xl = 8;
2237 x |= ((1 << xl) - 1) & ~1;
2238
2239 net->gateway_ipv4_addr[0] = x >> 24;
2240 net->gateway_ipv4_addr[1] = x >> 16;
2241 net->gateway_ipv4_addr[2] = x >> 8;
2242 net->gateway_ipv4_addr[3] = x;
2243
2244 net->gateway_ethernet_addr[0] = 0x60;
2245 net->gateway_ethernet_addr[1] = 0x50;
2246 net->gateway_ethernet_addr[2] = 0x40;
2247 net->gateway_ethernet_addr[3] = 0x30;
2248 net->gateway_ethernet_addr[4] = 0x20;
2249 net->gateway_ethernet_addr[5] = 0x10;
2250 }
2251
2252
2253 /*
2254 * net_dumpinfo():
2255 *
2256 * Called from the debugger's "machine" command, to print some info about
2257 * a network.
2258 */
2259 void net_dumpinfo(struct net *net)
2260 {
2261 int iadd = 4;
2262 struct remote_net *rnp;
2263
2264 debug("net: simulating ");
2265
2266 net_debugaddr(&net->netmask_ipv4, ADDR_IPV4);
2267 debug("/%i", net->netmask_ipv4_len);
2268
2269 debug(" (max outgoing: TCP=%i, UDP=%i)\n",
2270 MAX_TCP_CONNECTIONS, MAX_UDP_CONNECTIONS);
2271
2272 debug_indentation(iadd);
2273
2274 debug("simulated gateway: ");
2275 net_debugaddr(&net->gateway_ipv4_addr, ADDR_IPV4);
2276 debug(" (");
2277 net_debugaddr(&net->gateway_ethernet_addr, ADDR_ETHERNET);
2278 debug(")\n");
2279
2280 debug_indentation(iadd);
2281 if (!net->nameserver_known) {
2282 debug("(could not determine nameserver)");
2283 } else {
2284 debug("using nameserver ");
2285 net_debugaddr(&net->nameserver_ipv4, ADDR_IPV4);
2286 }
2287 if (net->domain_name != NULL && net->domain_name[0])
2288 debug(", domain \"%s\"", net->domain_name);
2289 debug("\n");
2290 debug_indentation(-iadd);
2291
2292 rnp = net->remote_nets;
2293 if (net->local_port != 0)
2294 debug("distributed network: local port = %i\n",
2295 net->local_port);
2296 debug_indentation(iadd);
2297 while (rnp != NULL) {
2298 debug("remote \"%s\": ", rnp->name);
2299 net_debugaddr(&rnp->ipv4_addr, ADDR_IPV4);
2300 debug(" port %i\n", rnp->portnr);
2301 rnp = rnp->next;
2302 }
2303 debug_indentation(-iadd);
2304
2305 debug_indentation(-iadd);
2306 }
2307
2308
2309 /*
2310 * net_init():
2311 *
2312 * This function creates a network, and returns a pointer to it.
2313 * ipv4addr should be something like "10.0.0.0", netipv4len = 8.
2314 * If n_remote is more than zero, remote should be a pointer to an array
2315 * of strings of the following format: "host:portnr".
2316 *
2317 * On failure, exit() is called.
2318 */
2319 struct net *net_init(struct emul *emul, int init_flags,
2320 char *ipv4addr, int netipv4len, char **remote, int n_remote,
2321 int local_port)
2322 {
2323 struct net *net;
2324 int res;
2325
2326 net = malloc(sizeof(struct net));
2327 if (net == NULL) {
2328 fprintf(stderr, "net_init(): out of memory\n");
2329 exit(1);
2330 }
2331
2332 memset(net, 0, sizeof(struct net));
2333
2334 /* Set the back pointer: */
2335 net->emul = emul;
2336
2337 /* Sane defaults: */
2338 net->timestamp = 0;
2339 net->first_ethernet_packet = net->last_ethernet_packet = NULL;
2340
2341 #ifdef HAVE_INET_PTON
2342 res = inet_pton(AF_INET, ipv4addr, &net->netmask_ipv4);
2343 #else
2344 res = inet_aton(ipv4addr, &net->netmask_ipv4);
2345 #endif
2346 if (res < 1) {
2347 fprintf(stderr, "net_init(): could not parse IPv4 address"
2348 " '%s'\n", ipv4addr);
2349 exit(1);
2350 }
2351
2352 if (netipv4len < 1 || netipv4len > 30) {
2353 fprintf(stderr, "net_init(): extremely weird ipv4 "
2354 "network length (%i)\n", netipv4len);
2355 exit(1);
2356 }
2357 net->netmask_ipv4_len = netipv4len;
2358
2359 net->nameserver_known = 0;
2360 net->domain_name = "";
2361 parse_resolvconf(net);
2362
2363 /* Distributed network? Then add remote hosts: */
2364 if (local_port != 0) {
2365 struct sockaddr_in si_self;
2366
2367 net->local_port = local_port;
2368 net->local_port_socket = socket(AF_INET, SOCK_DGRAM, 0);
2369 if (net->local_port_socket < 0) {
2370 perror("socket");
2371 exit(1);
2372 }
2373
2374 memset((char *)&si_self, sizeof(si_self), 0);
2375 si_self.sin_family = AF_INET;
2376 si_self.sin_port = htons(local_port);
2377 si_self.sin_addr.s_addr = htonl(INADDR_ANY);
2378 if (bind(net->local_port_socket, (struct sockaddr *)&si_self,
2379 sizeof(si_self)) < 0) {
2380 perror("bind");
2381 exit(1);
2382 }
2383
2384 /* Set the socket to non-blocking: */
2385 res = fcntl(net->local_port_socket, F_GETFL);
2386 fcntl(net->local_port_socket, F_SETFL, res | O_NONBLOCK);
2387 }
2388 if (n_remote != 0) {
2389 struct remote_net *rnp;
2390 while ((n_remote--) != 0) {
2391 struct hostent *hp;
2392
2393 /* debug("adding '%s'\n", remote[n_remote]); */
2394 rnp = malloc(sizeof(struct remote_net));
2395 memset(rnp, 0, sizeof(struct remote_net));
2396
2397 rnp->next = net->remote_nets;
2398 net->remote_nets = rnp;
2399
2400 rnp->name = strdup(remote[n_remote]);
2401 if (strchr(rnp->name, ':') != NULL)
2402 strchr(rnp->name, ':')[0] = '\0';
2403
2404 hp = gethostbyname(rnp->name);
2405 if (hp == NULL) {
2406 fprintf(stderr, "could not resolve '%s'\n",
2407 rnp->name);
2408 exit(1);
2409 }
2410 memcpy(&rnp->ipv4_addr, hp->h_addr, hp->h_length);
2411 free(rnp->name);
2412
2413 /* And again: */
2414 rnp->name = strdup(remote[n_remote]);
2415 if (strchr(rnp->name, ':') == NULL) {
2416 fprintf(stderr, "Remote network '%s' is not "
2417 "'host:portnr'?\n", rnp->name);
2418 exit(1);
2419 }
2420 rnp->portnr = atoi(strchr(rnp->name, ':') + 1);
2421 }
2422 }
2423
2424 if (init_flags & NET_INIT_FLAG_GATEWAY)
2425 net_gateway_init(net);
2426
2427 net_dumpinfo(net);
2428
2429 /* This is neccessary when using the real network: */
2430 signal(SIGPIPE, SIG_IGN);
2431
2432 return net;
2433 }
2434

  ViewVC Help
Powered by ViewVC 1.1.26