/[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 12 - (show annotations)
Mon Oct 8 16:18:38 2007 UTC (16 years, 6 months ago) by dpavlin
File MIME type: text/plain
File size: 67342 byte(s)
++ trunk/HISTORY	(local)
$Id: HISTORY,v 1.905 2005/08/16 09:16:24 debug Exp $
20050628	Continuing the work on the ARM translation engine. end_of_page
		works. Experimenting with load/store translation caches
		(virtual -> physical -> host).
20050629	More ARM stuff (memory access translation cache, mostly). This
		might break a lot of stuff elsewhere, probably some MIPS-
		related translation things.
20050630	Many load/stores are now automatically generated and included
		into cpu_arm_instr.c; 1024 functions in total (!).
		Fixes based on feedback from Alec Voropay: only print 8 hex
		digits instead of 16 in some cases when emulating 32-bit
		machines; similar 8 vs 16 digit fix for breakpoint addresses;
		4Kc has 16 TLB entries, not 48; the MIPS config select1
		register is now printed with "reg ,0".
		Also changing many other occurances of 16 vs 8 digit output.
		Adding cache associativity fields to mips_cpu_types.h; updating
		some other cache fields; making the output of
		mips_cpu_dumpinfo() look nicer.
		Generalizing the bintrans stuff for device accesses to also
		work with the new translation system. (This might also break
		some MIPS things.)
		Adding multi-load/store instructions to the ARM disassembler
		and the translator, and some optimizations of various kinds.
20050701	Adding a simple dev_disk (it can read/write sectors from
		disk images).
20050712	Adding dev_ether (a simple ethernet send/receive device).
		Debugger command "ninstrs" for toggling show_nr_of_instructions
		during runtime.
		Removing the framebuffer logo.
20050713	Continuing on dev_ether.
		Adding a dummy cpu_alpha (again).
20050714	More work on cpu_alpha.
20050715	More work on cpu_alpha. Many instructions work, enough to run
		a simple framebuffer fill test (similar to the ARM test).
20050716	More Alpha stuff.
20050717	Minor updates (Alpha stuff).
20050718	Minor updates (Alpha stuff).
20050719	Generalizing some Alpha instructions.
20050720	More Alpha-related updates.
20050721	Continuing on cpu_alpha. Importing rpb.h from NetBSD/alpha.
20050722	Alpha-related updates: userland stuff (Hello World using
		write() compiled statically for FreeBSD/Alpha runs fine), and
		more instructions are now implemented.
20050723	Fixing ldq_u and stq_u.
		Adding more instructions (conditional moves, masks, extracts,
		shifts).
20050724	More FreeBSD/Alpha userland stuff, and adding some more
		instructions (inserts).
20050725	Continuing on the Alpha stuff. (Adding dummy ldt/stt.)
		Adding a -A command line option to turn off alignment checks
		in some cases (for translated code).
		Trying to remove the old bintrans code which updated the pc
		and nr_of_executed_instructions for every instruction.
20050726	Making another attempt att removing the pc/nr of instructions
		code. This time it worked, huge performance increase for
		artificial test code, but performance loss for real-world
		code :-( so I'm scrapping that code for now.
		Tiny performance increase on Alpha (by using ret instead of
		jmp, to play nice with the Alpha's branch prediction) for the
		old MIPS bintrans backend.
20050727	Various minor fixes and cleanups.
20050728	Switching from a 2-level virtual to host/physical translation
		system for ARM emulation, to a 1-level translation.
		Trying to switch from 2-level to 1-level for the MIPS bintrans
		system as well (Alpha only, so far), but there is at least one
		problem: caches and/or how they work with device mappings.
20050730	Doing the 2-level to 1-level conversion for the i386 backend.
		The cache/device bug is still there for R2K/3K :(
		Various other minor updates (Malta etc).
		The mc146818 clock now updates the UIP bit in a way which works
		better with Linux for at least sgimips and Malta emulation.
		Beginning the work on refactoring the dyntrans system.
20050731	Continuing the dyntrans refactoring.
		Fixing a small but serious host alignment bug in memory_rw.
		Adding support for big-endian load/stores to the i386 bintrans
		backend.
		Another minor i386 bintrans backend update: stores from the
		zero register are now one (or two) loads shorter.
		The slt and sltu instructions were incorrectly implemented for
		the i386 backend; only using them for 32-bit mode for now.
20050801	Continuing the dyntrans refactoring.
		Cleanup of the ns16550 serial controller (removing unnecessary
		code).
		Bugfix (memory corruption bug) in dev_gt, and a patch/hack from
		Alec Voropay for Linux/Malta.
20050802	More cleanup/refactoring of the dyntrans subsystem: adding
		phys_page pointers to the lookup tables, for quick jumps
		between translated pages.
		Better fix for the ns16550 device (but still no real FIFO
		functionality).
		Converting cpu_ppc to the new dyntrans system. This means that
		I will have to start from scratch with implementing each
		instruction, and figure out how to implement dual 64/32-bit
		modes etc.
		Removing the URISC CPU family, because it was useless.
20050803	When selecting a machine type, the main type can now be omitted
		if the subtype name is unique. (I.e. -E can be omitted.)
		Fixing a dyntrans/device update bug. (Writes to offset 0 of
		a device could sometimes go unnoticed.)
		Adding an experimental "instruction combination" hack for
		ARM for memset-like byte fill loops.
20050804	Minor progress on cpu_alpha and related things.
		Finally fixing the MIPS dmult/dmultu bugs.
		Fixing some minor TODOs.
20050805	Generalizing the 8259 PIC. It now also works with Cobalt
		and evbmips emulation, in addition to the x86 hack.
		Finally converting the ns16550 device to use devinit.
		Continuing the work on the dyntrans system. Thinking about
		how to add breakpoints.
20050806	More dyntrans updates. Breakpoints seem to work now.
20050807	Minor updates: cpu_alpha and related things; removing
		dev_malta (as it isn't used any more).
		Dyntrans: working on general "show trace tree" support.
		The trace tree stuff now works with both the old MIPS code and
		with newer dyntrans modes. :)
		Continuing on Alpha-related stuff (trying to get *BSD to boot
		a bit further, adding more instructions, etc).
20050808	Adding a dummy IA64 cpu family, and continuing the refactoring
		of the dyntrans system.
		Removing the regression test stuff, because it was more or
		less useless.
		Adding loadlinked/storeconditional type instructions to the
		Alpha emulation. (Needed for Linux/alpha. Not very well tested
		yet.)
20050809	The function call trace tree now prints a per-function nr of
		arguments. (Semi-meaningless, since that data isn't read yet
		from the ELFs; some hardcoded symbols such as memcpy() and
		strlen() work fine, though.)
		More dyntrans refactoring; taking out more of the things that
		are common to all cpu families.
20050810	Working on adding support for "dual mode" for PPC dyntrans
		(i.e. both 64-bit and 32-bit modes).
		(Re)adding some simple PPC instructions.
20050811	Adding a dummy M68K cpu family. The dyntrans system isn't ready
		for variable-length ISAs yet, so it's completely bogus so far.
		Re-adding more PPC instructions.
		Adding a hack to src/file.c which allows OpenBSD/mac68k a.out
		kernels to be loaded.
		Beginning to add PPC loads/stores. So far they only work in
		32-bit mode.
20050812	The configure file option "add_remote" now accepts symbolic
		host names, in addition to numeric IPv4 addresses.
		Re-adding more PPC instructions.
20050814	Continuing to port back more PPC instructions.
		Found and fixed the cache/device write-update bug for 32-bit
		MIPS bintrans. :-)
		Triggered a really weird and annoying bug in Compaq's C
		compiler; ccc sometimes outputs code which loads from an
		address _before_ checking whether the pointer was NULL or not.
		(I'm not sure how to handle this problem.)
20050815	Removing all of the old x86 instruction execution code; adding
		a new (dummy) dyntrans module for x86.
		Taking the first steps to extend the dyntrans system to support
		variable-length instructions.
		Slowly preparing for the next release.
20050816	Adding a dummy SPARC cpu module.
		Minor updates (documentation etc) for the release.

==============  RELEASE 0.3.5  ==============


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

  ViewVC Help
Powered by ViewVC 1.1.26