/[gxemul]/trunk/src/net/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/net.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 42 - (show annotations)
Mon Oct 8 16:22:32 2007 UTC (16 years, 6 months ago) by dpavlin
File MIME type: text/plain
File size: 21814 byte(s)
++ trunk/HISTORY	(local)
$Id: HISTORY,v 1.1613 2007/06/15 20:11:26 debug Exp $
20070501	Continuing a little on m88k disassembly (control registers,
		more instructions).
		Adding a dummy mvme88k machine mode.
20070502	Re-adding MIPS load/store alignment exceptions.
20070503	Implementing more of the M88K disassembly code.
20070504	Adding disassembly of some more M88K load/store instructions.
		Implementing some relatively simple M88K instructions (br.n,
		xor[.u] imm, and[.u] imm).
20070505	Implementing M88K three-register and, or, xor, and jmp[.n],
		bsr[.n] including function call trace stuff.
		Applying a patch from Bruce M. Simpson which implements the
		SYSCON_BOARD_CPU_CLOCK_FREQ_ID object of the syscon call in
		the yamon PROM emulation.
20070506	Implementing M88K bb0[.n] and bb1[.n], and skeletons for
		ldcr and stcr (although no control regs are implemented yet).
20070509	Found and fixed the bug which caused Linux for QEMU_MIPS to
		stop working in 0.4.5.1: It was a faulty change to the MIPS
		'sc' and 'scd' instructions I made while going through gcc -W
		warnings on 20070428.
20070510	Updating the Linux/QEMU_MIPS section in guestoses.html to
		use mips-test-0.2.tar.gz instead of 0.1.
		A big thank you to Miod Vallat for sending me M88K manuals.
		Implementing more M88K instructions (addu, subu, div[u], mulu,
		ext[u], clr, set, cmp).
20070511	Fixing bugs in the M88K "and" and "and.u" instructions (found
		by comparing against the manual).
		Implementing more M88K instructions (mask[.u], mak, bcnd (auto-
		generated)) and some more control register details.
		Cleanup: Removing the experimental AVR emulation mode and
		corresponding devices; AVR emulation wasn't really meaningful.
		Implementing autogeneration of most M88K loads/stores. The
		rectangle drawing demo (with -O0) for M88K runs :-)
		Beginning on M88K exception handling.
		More M88K instructions: tb0, tb1, rte, sub, jsr[.n].
		Adding some skeleton MVME PROM ("BUG") emulation.
20070512	Fixing a bug in the M88K cmp instruction.
		Adding the M88K lda (scaled register) instruction.
		Fixing bugs in 64-bit (32-bit pairs) M88K loads/stores.
		Removing the unused tick_hz stuff from the machine struct.
		Implementing the M88K xmem instruction. OpenBSD/mvme88k gets
		far enough to display the Copyright banner :-)
		Implementing subu.co (guess), addu.co, addu.ci, ff0, and ff1.
		Adding a dev_mvme187, for MVME187-specific devices/registers.
		OpenBSD/mvme88k prints more boot messages. :)
20070515	Continuing on MVME187 emulation (adding more devices, beginning
		on the CMMUs, etc).
		Adding the M88K and.c, xor.c, and or.c instructions, and making
		sure that mul, div, etc cause exceptions if executed when SFD1
		is disabled.
20070517	Continuing on M88K and MVME187 emulation in general; moving
		the CMMU registers to the CPU struct, separating dev_pcc2 from
		dev_mvme187, and beginning on memory_m88k.c (BATC and PATC).
		Fixing a bug in 64-bit (32-bit pairs) M88K fast stores.
		Implementing the clock part of dev_mk48txx.
		Implementing the M88K fstcr and xcr instructions.
		Implementing m88k_cpu_tlbdump().
		Beginning on the implementation of a separate address space
		for M88K .usr loads/stores.
20070520	Removing the non-working (skeleton) Sandpoint, SonyNEWS, SHARK
		Dnard, and Zaurus machine modes.
		Experimenting with dyntrans to_be_translated read-ahead. It
		seems to give a very small performance increase for MIPS
		emulation, but a large performance degradation for SuperH. Hm.
20070522	Disabling correct SuperH ITLB emulation; it does not seem to be
		necessary in order to let SH4 guest OSes run, and it slows down
		userspace code.
		Implementing "samepage" branches for SuperH emulation, and some
		other minor speed hacks.
20070525	Continuing on M88K memory-related stuff: exceptions, memory
		transaction register contents, etc.
		Implementing the M88K subu.ci instruction.
		Removing the non-working (skeleton) Iyonix machine mode.
		OpenBSD/mvme88k reaches userland :-), starts executing
		/sbin/init's instructions, and issues a few syscalls, before
		crashing.
20070526	Fixing bugs in dev_mk48txx, so that OpenBSD/mvme88k detects
		the correct time-of-day.
		Implementing a generic IRQ controller for the test machines
		(dev_irqc), similar to a proposed patch from Petr Stepan.
		Experimenting some more with translation read-ahead.
		Adding an "expect" script for automated OpenBSD/landisk
		install regression/performance tests.
20070527	Adding a dummy mmEye (SH3) machine mode skeleton.
		FINALLY found the strange M88K bug I have been hunting: I had
		not emulated the SNIP value for exceptions occurring in
		branch delay slots correctly.
		Implementing correct exceptions for 64-bit M88K loads/stores.
		Address to symbol lookups are now disabled when M88K is
		running in usermode (because usermode addresses don't have
		anything to do with supervisor addresses).
20070531	Removing the mmEye machine mode skeleton.
20070604	Some minor code cleanup.
20070605	Moving src/useremul.c into a subdir (src/useremul/), and
		cleaning up some more legacy constructs.
		Adding -Wstrict-aliasing and -fstrict-aliasing detection to
		the configure script.
20070606	Adding a check for broken GCC on Solaris to the configure
		script. (GCC 3.4.3 on Solaris cannot handle static variables
		which are initialized to 0 or NULL. :-/)
		Removing the old (non-working) ARC emulation modes: NEC RD94,
		R94, R96, and R98, and the last traces of Olivetti M700 and
		Deskstation Tyne.
		Removing the non-working skeleton WDSC device (dev_wdsc).
20070607	Thinking about how to use the host's cc + ld at runtime to
		generate native code. (See experiments/native_cc_ld_test.i
		for an example.)
20070608	Adding a program counter sampling timer, which could be useful
		for native code generation experiments.
		The KN02_CSR_NRMMOD bit in the DECstation 5000/200 (KN02) CSR
		should always be set, to allow a 5000/200 PROM to boot.
20070609	Moving out breakpoint details from the machine struct into
		a helper struct, and removing the limit on max nr of
		breakpoints.
20070610	Moving out tick functions into a helper struct as well (which
		also gets rid of the max limit).
20070612	FINALLY figured out why Debian/DECstation stopped working when
		translation read-ahead was enabled: in src/memory_rw.c, the
		call to invalidate_code_translation was made also if the
		memory access was an instruction load (if the page was mapped
		as writable); it shouldn't be called in that case.
20070613	Implementing some more MIPS32/64 revision 2 instructions: di,
		ei, ext, dext, dextm, dextu, and ins.
20070614	Implementing an instruction combination for the NetBSD/arm
		idle loop (making the host not use any cpu if NetBSD/arm
		inside the emulator is not using any cpu).
		Increasing the nr of ARM VPH entries from 128 to 384.
20070615	Removing the ENABLE_arch stuff from the configure script, so
		that all included architectures are included in both release
		and development builds.
		Moving memory related helper functions from misc.c to memory.c.
		Adding preliminary instructions for netbooting NetBSD/pmppc to
		guestoses.html; it doesn't work yet, there are weird timeouts.
		Beginning a total rewrite of the userland emulation modes
		(removing all emulation modes, beginning from scratch with
		NetBSD/MIPS and FreeBSD/Alpha only).
20070616	After fixing a bug in the DEC21143 NIC (the TDSTAT_OWN bit was
		only cleared for the last segment when transmitting, not all
		segments), NetBSD/pmppc boots with root-on-nfs without the
		timeouts. Updating guestoses.html.
		Removing the skeleton PSP (Playstation Portable) mode.
		Moving X11-related stuff in the machine struct into a helper
		struct.
		Cleanup of out-of-memory checks, to use a new CHECK_ALLOCATION
		macro (which prints a meaningful error message).
		Adding a COMMENT to each machine and device (for automagic
		.index comment generation).
		Doing regression testing for the next release.

==============  RELEASE 0.4.6  ==============


1 /*
2 * Copyright (C) 2004-2007 Anders Gavare. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 *
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. The name of the author may not be used to endorse or promote products
13 * derived from this software without specific prior written permission.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 *
28 * $Id: net.c,v 1.10 2007/06/15 17:02:39 debug Exp $
29 *
30 * Emulated network.
31 *
32 * (Read the README file in this directory for more details.)
33 *
34 *
35 * NOTE: The 'extra' argument used in many functions in this file is a pointer
36 * to something unique for each NIC (i.e. the NIC itself :-), so that if
37 * multiple NICs are emulated concurrently, they will not get packets that
38 * are meant for some other controller.
39 */
40
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <unistd.h>
45 #include <errno.h>
46 #include <sys/types.h>
47 #include <sys/socket.h>
48 #include <netinet/in.h>
49 #include <arpa/inet.h>
50 #include <netdb.h>
51 #include <fcntl.h>
52 #include <signal.h>
53
54 #include "machine.h"
55 #include "misc.h"
56 #include "net.h"
57
58
59 /* #define debug fatal */
60
61
62 /*
63 * net_allocate_ethernet_packet_link():
64 *
65 * This routine allocates an ethernet_packet_link struct, and adds it at
66 * the end of the packet chain. A data buffer is allocated, and the data,
67 * extra, and len fields of the link are set.
68 *
69 * Note: The data buffer is not zeroed.
70 *
71 * Return value is a pointer to the link on success. It doesn't return on
72 * failure.
73 */
74 struct ethernet_packet_link *net_allocate_ethernet_packet_link(
75 struct net *net, void *extra, size_t len)
76 {
77 struct ethernet_packet_link *lp;
78
79 CHECK_ALLOCATION(lp = malloc(sizeof(struct ethernet_packet_link)));
80
81 lp->len = len;
82 lp->extra = extra;
83 CHECK_ALLOCATION(lp->data = malloc(len));
84
85 lp->next = NULL;
86
87 /* Add last in the link chain: */
88 lp->prev = net->last_ethernet_packet;
89 if (lp->prev != NULL)
90 lp->prev->next = lp;
91 else
92 net->first_ethernet_packet = lp;
93 net->last_ethernet_packet = lp;
94
95 return lp;
96 }
97
98
99 /*
100 * net_arp():
101 *
102 * Handle an ARP (or RARP) packet, coming from the emulated NIC.
103 *
104 * An ARP packet might look like this:
105 *
106 * ARP header:
107 * ARP hardware addr family: 0001
108 * ARP protocol addr family: 0800
109 * ARP addr lengths: 06 04
110 * ARP request: 0001
111 * ARP from: 112233445566 01020304
112 * ARP to: 000000000000 01020301
113 *
114 * An ARP request with a 'to' IP value of the gateway should cause an
115 * ARP response packet to be created.
116 *
117 * An ARP request with the same from and to IP addresses should be ignored.
118 * (This would be a host testing to see if there is an IP collision.)
119 */
120 static void net_arp(struct net *net, void *extra,
121 unsigned char *packet, int len, int reverse)
122 {
123 int q;
124 int i;
125
126 /* TODO: This debug dump assumes ethernet->IPv4 translation: */
127 if (reverse)
128 debug("[ net: RARP: ");
129 else
130 debug("[ net: ARP: ");
131 for (i=0; i<2; i++)
132 debug("%02x", packet[i]);
133 debug(" ");
134 for (i=2; i<4; i++)
135 debug("%02x", packet[i]);
136 debug(" ");
137 debug("%02x", packet[4]);
138 debug(" ");
139 debug("%02x", packet[5]);
140 debug(" req=");
141 debug("%02x", packet[6]); /* Request type */
142 debug("%02x", packet[7]);
143 debug(" from=");
144 for (i=8; i<18; i++)
145 debug("%02x", packet[i]);
146 debug(" to=");
147 for (i=18; i<28; i++)
148 debug("%02x", packet[i]);
149 debug(" ]\n");
150
151 if (packet[0] == 0x00 && packet[1] == 0x01 &&
152 packet[2] == 0x08 && packet[3] == 0x00 &&
153 packet[4] == 0x06 && packet[5] == 0x04) {
154 int r = (packet[6] << 8) + packet[7];
155 struct ethernet_packet_link *lp;
156
157 switch (r) {
158 case 1: /* Request */
159 /* Only create a reply if this was meant for the
160 gateway: */
161 if (memcmp(packet+24, net->gateway_ipv4_addr, 4) != 0)
162 break;
163
164 lp = net_allocate_ethernet_packet_link(
165 net, extra, 60 + 14);
166
167 /* Copy the old packet first: */
168 memset(lp->data, 0, 60 + 14);
169 memcpy(lp->data + 14, packet, len);
170
171 /* Add ethernet ARP header: */
172 memcpy(lp->data + 0, lp->data + 8 + 14, 6);
173 memcpy(lp->data + 6, net->gateway_ethernet_addr, 6);
174 lp->data[12] = 0x08; lp->data[13] = 0x06;
175
176 /* Address of the emulated machine: */
177 memcpy(lp->data + 18 + 14, lp->data + 8 + 14, 10);
178
179 /* Address of the gateway: */
180 memcpy(lp->data + 8 + 14, net->gateway_ethernet_addr,
181 6);
182 memcpy(lp->data + 14 + 14, net->gateway_ipv4_addr, 4);
183
184 /* This is a Reply: */
185 lp->data[6 + 14] = 0x00; lp->data[7 + 14] = 0x02;
186
187 break;
188 case 3: /* Reverse Request */
189 lp = net_allocate_ethernet_packet_link(
190 net, extra, 60 + 14);
191
192 /* Copy the old packet first: */
193 memset(lp->data, 0, 60 + 14);
194 memcpy(lp->data + 14, packet, len);
195
196 /* Add ethernet RARP header: */
197 memcpy(lp->data + 0, packet + 8, 6);
198 memcpy(lp->data + 6, net->gateway_ethernet_addr, 6);
199 lp->data[12] = 0x80; lp->data[13] = 0x35;
200
201 /* This is a RARP reply: */
202 lp->data[6 + 14] = 0x00; lp->data[7 + 14] = 0x04;
203
204 /* Address of the gateway: */
205 memcpy(lp->data + 8 + 14, net->gateway_ethernet_addr,
206 6);
207 memcpy(lp->data + 14 + 14, net->gateway_ipv4_addr, 4);
208
209 /* MAC address of emulated machine: */
210 memcpy(lp->data + 18 + 14, packet + 8, 6);
211
212 /*
213 * IP address of the emulated machine: Automagically
214 * generated from the MAC address. :-)
215 *
216 * packet+8 points to the client's mac address,
217 * for example 10:20:30:00:00:z0, where z is 0..15.
218 * 10:20:30:00:00:10 results in 10.0.0.1.
219 */
220 /* q = (packet[8 + 3]) >> 4; */
221 /* q = q*15 + ((packet[8 + 4]) >> 4); */
222 q = (packet[8 + 5]) >> 4;
223 lp->data[24 + 14] = 10;
224 lp->data[25 + 14] = 0;
225 lp->data[26 + 14] = 0;
226 lp->data[27 + 14] = q;
227 break;
228 case 2: /* Reply */
229 case 4: /* Reverse Reply */
230 default:
231 fatal("[ net: ARP: UNIMPLEMENTED request type "
232 "0x%04x ]\n", r);
233 }
234 } else {
235 fatal("[ net: ARP: UNIMPLEMENTED arp packet type: ");
236 for (i=0; i<len; i++)
237 fatal("%02x", packet[i]);
238 fatal(" ]\n");
239 }
240 }
241
242
243 /*
244 * net_ethernet_rx_avail():
245 *
246 * Return 1 if there is a packet available for this 'extra' pointer, otherwise
247 * return 0.
248 *
249 * Appart from actually checking for incoming packets from the outside world,
250 * this function basically works like net_ethernet_rx() but it only receives
251 * a return value telling us whether there is a packet or not, we don't
252 * actually get the packet.
253 */
254 int net_ethernet_rx_avail(struct net *net, void *extra)
255 {
256 if (net == NULL)
257 return 0;
258
259 /*
260 * If the network is distributed across multiple emulator processes,
261 * then receive incoming packets from those processes.
262 */
263 if (net->local_port != 0) {
264 struct sockaddr_in si;
265 socklen_t si_len = sizeof(si);
266 int res, i, nreceived = 0;
267 unsigned char buf[60000];
268
269 do {
270 res = recvfrom(net->local_port_socket, buf,
271 sizeof(buf), 0, (struct sockaddr *)&si, &si_len);
272
273 if (res != -1) {
274 nreceived ++;
275
276 /* fatal("[ incoming DISTRIBUTED packet, %i "
277 "bytes from %s:%d\n", res,
278 inet_ntoa(si.sin_addr),
279 ntohs(si.sin_port)); */
280
281 /* Add the packet to all "our" NICs on this
282 network: */
283 for (i=0; i<net->n_nics; i++) {
284 struct ethernet_packet_link *lp;
285 lp = net_allocate_ethernet_packet_link(
286 net, net->nic_extra[i], res);
287 memcpy(lp->data, buf, res);
288 }
289 }
290 } while (res != -1 && nreceived < 100);
291 }
292
293 /* IP protocol specific: */
294 net_udp_rx_avail(net, extra);
295 net_tcp_rx_avail(net, extra);
296
297 return net_ethernet_rx(net, extra, NULL, NULL);
298 }
299
300
301 /*
302 * net_ethernet_rx():
303 *
304 * Receive an ethernet packet. (This means handing over an already prepared
305 * packet from this module to a specific ethernet controller device.)
306 *
307 * Return value is 1 if there was a packet available. *packetp and *lenp
308 * will be set to the packet's data pointer and length, respectively, and
309 * the packet will be removed from the linked list). If there was no packet
310 * available, 0 is returned.
311 *
312 * If packetp is NULL, then the search is aborted as soon as a packet with
313 * the correct 'extra' field is found, and a 1 is returned, but as packetp
314 * is NULL we can't return the actual packet. (This is the internal form
315 * if net_ethernet_rx_avail().)
316 */
317 int net_ethernet_rx(struct net *net, void *extra,
318 unsigned char **packetp, int *lenp)
319 {
320 struct ethernet_packet_link *lp, *prev;
321
322 if (net == NULL)
323 return 0;
324
325 /* Find the first packet which has the right 'extra' field. */
326
327 lp = net->first_ethernet_packet;
328 prev = NULL;
329 while (lp != NULL) {
330 if (lp->extra == extra) {
331 /* We found a packet for this controller! */
332 if (packetp == NULL || lenp == NULL)
333 return 1;
334
335 /* Let's return it: */
336 (*packetp) = lp->data;
337 (*lenp) = lp->len;
338
339 /* Remove this link from the linked list: */
340 if (prev == NULL)
341 net->first_ethernet_packet = lp->next;
342 else
343 prev->next = lp->next;
344
345 if (lp->next == NULL)
346 net->last_ethernet_packet = prev;
347 else
348 lp->next->prev = prev;
349
350 free(lp);
351
352 /* ... and return successfully: */
353 return 1;
354 }
355
356 prev = lp;
357 lp = lp->next;
358 }
359
360 /* No packet found. :-( */
361 return 0;
362 }
363
364
365 /*
366 * net_ethernet_tx():
367 *
368 * Transmit an ethernet packet, as seen from the emulated ethernet controller.
369 * If the packet can be handled here, it will not necessarily be transmitted
370 * to the outside world.
371 */
372 void net_ethernet_tx(struct net *net, void *extra,
373 unsigned char *packet, int len)
374 {
375 int i, eth_type, for_the_gateway;
376
377 if (net == NULL)
378 return;
379
380 for_the_gateway = !memcmp(packet, net->gateway_ethernet_addr, 6);
381
382 /* Drop too small packets: */
383 if (len < 20) {
384 fatal("[ net_ethernet_tx: Warning: dropping tiny packet "
385 "(%i bytes) ]\n", len);
386 return;
387 }
388
389 /*
390 * Copy this packet to all other NICs on this network (except if
391 * it is aimed specifically at the gateway's ethernet address):
392 */
393 if (!for_the_gateway && extra != NULL && net->n_nics > 0) {
394 for (i=0; i<net->n_nics; i++)
395 if (extra != net->nic_extra[i]) {
396 struct ethernet_packet_link *lp;
397 lp = net_allocate_ethernet_packet_link(net,
398 net->nic_extra[i], len);
399
400 /* Copy the entire packet: */
401 memcpy(lp->data, packet, len);
402 }
403 }
404
405 /*
406 * If this network is distributed across multiple emulator processes,
407 * then transmit the packet to those other processes.
408 */
409 if (!for_the_gateway && net->remote_nets != NULL) {
410 struct remote_net *rnp = net->remote_nets;
411 while (rnp != NULL) {
412 send_udp(&rnp->ipv4_addr, rnp->portnr, packet, len);
413 rnp = rnp->next;
414 }
415 }
416
417
418 /*
419 * The code below simulates the behaviour of a "NAT"-style gateway.
420 *
421 * Packets that are not destined for the gateway are dropped first:
422 * (DHCP packets are let through, though.)
423 */
424
425 if (!for_the_gateway && packet[0] != 0xff && packet[0] != 0x00)
426 return;
427
428 #if 0
429 fatal("[ net: ethernet: ");
430 for (i=0; i<6; i++) fatal("%02x", packet[i]); fatal(" ");
431 for (i=6; i<12; i++) fatal("%02x", packet[i]); fatal(" ");
432 for (i=12; i<14; i++) fatal("%02x", packet[i]); fatal(" ");
433 for (i=14; i<len; i++) fatal("%02x", packet[i]); fatal(" ]\n");
434 #endif
435
436 eth_type = (packet[12] << 8) + packet[13];
437
438 /* IP: */
439 if (eth_type == ETHERTYPE_IP) {
440 /* Routed via the gateway? */
441 if (for_the_gateway) {
442 net_ip(net, extra, packet, len);
443 return;
444 }
445
446 /* Broadcast? (DHCP does this.) */
447 if (packet[0] == 0xff && packet[1] == 0xff &&
448 packet[2] == 0xff && packet[3] == 0xff &&
449 packet[4] == 0xff && packet[5] == 0xff) {
450 net_ip_broadcast(net, extra, packet, len);
451 return;
452 }
453
454 if (net->n_nics < 2) {
455 fatal("[ net_ethernet_tx: IP packet not for gateway, "
456 "and not broadcast: ");
457 for (i=0; i<14; i++)
458 fatal("%02x", packet[i]);
459 fatal(" ]\n");
460 }
461 return;
462 }
463
464 /* ARP: */
465 if (eth_type == ETHERTYPE_ARP) {
466 if (len != 42 && len != 60)
467 fatal("[ net_ethernet_tx: WARNING! unusual "
468 "ARP len (%i) ]\n", len);
469 net_arp(net, extra, packet + 14, len - 14, 0);
470 return;
471 }
472
473 /* RARP: */
474 if (eth_type == ETHERTYPE_REVARP) {
475 net_arp(net, extra, packet + 14, len - 14, 1);
476 return;
477 }
478
479 /* Sprite: */
480 if (eth_type == ETHERTYPE_SPRITE) {
481 /* TODO. */
482 fatal("[ net: TX: UNIMPLEMENTED Sprite packet ]\n");
483 return;
484 }
485
486 /* IPv6: */
487 if (eth_type == ETHERTYPE_IPV6) {
488 /* TODO. */
489 fatal("[ net_ethernet_tx: IPv6 is not implemented yet! ]\n");
490 return;
491 }
492
493 fatal("[ net_ethernet_tx: ethernet packet type 0x%04x not yet "
494 "implemented ]\n", eth_type);
495 }
496
497
498 /*
499 * parse_resolvconf():
500 *
501 * This function parses "/etc/resolv.conf" to figure out the nameserver
502 * and domain used by the host.
503 */
504 static void parse_resolvconf(struct net *net)
505 {
506 FILE *f;
507 char buf[8000];
508 size_t len;
509 int res;
510 unsigned int i, start;
511
512 /*
513 * This is a very ugly hack, which tries to figure out which
514 * nameserver the host uses by looking for the string 'nameserver'
515 * in /etc/resolv.conf.
516 *
517 * This can later on be used for DHCP autoconfiguration. (TODO)
518 *
519 * TODO: This is hardcoded to use /etc/resolv.conf. Not all
520 * operating systems use that filename.
521 *
522 * TODO: This is hardcoded for AF_INET (that is, IPv4).
523 *
524 * TODO: This assumes that the first nameserver listed is the
525 * one to use.
526 */
527 f = fopen("/etc/resolv.conf", "r");
528 if (f == NULL)
529 return;
530
531 /* TODO: get rid of the hardcoded values */
532 memset(buf, 0, sizeof(buf));
533 len = fread(buf, 1, sizeof(buf) - 100, f);
534 fclose(f);
535 buf[sizeof(buf) - 1] = '\0';
536
537 for (i=0; i<len; i++)
538 if (strncmp(buf+i, "nameserver", 10) == 0) {
539 char *p;
540
541 /*
542 * "nameserver" (1 or more whitespace)
543 * "x.y.z.w" (non-digit)
544 */
545
546 /* debug("found nameserver at offset %i\n", i); */
547 i += 10;
548 while (i<len && (buf[i]==' ' || buf[i]=='\t'))
549 i++;
550 if (i >= len)
551 break;
552 start = i;
553
554 p = buf+start;
555 while ((*p >= '0' && *p <= '9') || *p == '.')
556 p++;
557 *p = '\0';
558
559 #ifdef HAVE_INET_PTON
560 res = inet_pton(AF_INET, buf + start,
561 &net->nameserver_ipv4);
562 #else
563 res = inet_aton(buf + start, &net->nameserver_ipv4);
564 #endif
565 if (res < 1)
566 break;
567
568 net->nameserver_known = 1;
569 break;
570 }
571
572 for (i=0; i<len; i++)
573 if (strncmp(buf+i, "domain", 6) == 0) {
574 /* "domain" (1 or more whitespace) domain_name */
575 i += 6;
576 while (i<len && (buf[i]==' ' || buf[i]=='\t'))
577 i++;
578 if (i >= len)
579 break;
580
581 start = i;
582 while (i<len && buf[i]!='\n' && buf[i]!='\r')
583 i++;
584 if (i < len)
585 buf[i] = '\0';
586 /* fatal("DOMAIN='%s'\n", buf + start); */
587 CHECK_ALLOCATION(net->domain_name = strdup(buf+start));
588 break;
589 }
590 }
591
592
593 /*
594 * net_add_nic():
595 *
596 * Add a NIC to a network. (All NICs on a network will see each other's
597 * packets.)
598 */
599 void net_add_nic(struct net *net, void *extra, unsigned char *macaddr)
600 {
601 if (net == NULL)
602 return;
603
604 if (extra == NULL) {
605 fprintf(stderr, "net_add_nic(): extra = NULL\n");
606 exit(1);
607 }
608
609 net->n_nics ++;
610 CHECK_ALLOCATION(net->nic_extra = realloc(net->nic_extra, sizeof(void *)
611 * net->n_nics));
612
613 net->nic_extra[net->n_nics - 1] = extra;
614 }
615
616
617 /*
618 * net_gateway_init():
619 *
620 * This function creates a "gateway" machine (for example at IPv4 address
621 * 10.0.0.254, if the net is 10.0.0.0/8), which acts as a gateway/router/
622 * nameserver etc.
623 */
624 static void net_gateway_init(struct net *net)
625 {
626 unsigned char *p = (void *) &net->netmask_ipv4;
627 uint32_t x;
628 int xl;
629
630 x = (p[0] << 24) + (p[1] << 16) + (p[2] << 8) + p[3];
631 xl = 32 - net->netmask_ipv4_len;
632 if (xl > 8)
633 xl = 8;
634 x |= ((1 << xl) - 1) & ~1;
635
636 net->gateway_ipv4_addr[0] = x >> 24;
637 net->gateway_ipv4_addr[1] = x >> 16;
638 net->gateway_ipv4_addr[2] = x >> 8;
639 net->gateway_ipv4_addr[3] = x;
640
641 net->gateway_ethernet_addr[0] = 0x60;
642 net->gateway_ethernet_addr[1] = 0x50;
643 net->gateway_ethernet_addr[2] = 0x40;
644 net->gateway_ethernet_addr[3] = 0x30;
645 net->gateway_ethernet_addr[4] = 0x20;
646 net->gateway_ethernet_addr[5] = 0x10;
647 }
648
649
650 /*
651 * net_dumpinfo():
652 *
653 * Called from the debugger's "machine" command, to print some info about
654 * a network.
655 */
656 void net_dumpinfo(struct net *net)
657 {
658 int iadd = DEBUG_INDENTATION;
659 struct remote_net *rnp;
660
661 debug("net: simulating ");
662
663 net_debugaddr(&net->netmask_ipv4, NET_ADDR_IPV4);
664 debug("/%i", net->netmask_ipv4_len);
665
666 debug(" (max outgoing: TCP=%i, UDP=%i)\n",
667 MAX_TCP_CONNECTIONS, MAX_UDP_CONNECTIONS);
668
669 debug_indentation(iadd);
670
671 debug("simulated gateway: ");
672 net_debugaddr(&net->gateway_ipv4_addr, NET_ADDR_IPV4);
673 debug(" (");
674 net_debugaddr(&net->gateway_ethernet_addr, NET_ADDR_ETHERNET);
675 debug(")\n");
676
677 debug_indentation(iadd);
678 if (!net->nameserver_known) {
679 debug("(could not determine nameserver)");
680 } else {
681 debug("using nameserver ");
682 net_debugaddr(&net->nameserver_ipv4, NET_ADDR_IPV4);
683 }
684 if (net->domain_name != NULL && net->domain_name[0])
685 debug(", domain \"%s\"", net->domain_name);
686 debug("\n");
687 debug_indentation(-iadd);
688
689 rnp = net->remote_nets;
690 if (net->local_port != 0)
691 debug("distributed network: local port = %i\n",
692 net->local_port);
693 debug_indentation(iadd);
694 while (rnp != NULL) {
695 debug("remote \"%s\": ", rnp->name);
696 net_debugaddr(&rnp->ipv4_addr, NET_ADDR_IPV4);
697 debug(" port %i\n", rnp->portnr);
698 rnp = rnp->next;
699 }
700 debug_indentation(-iadd);
701
702 debug_indentation(-iadd);
703 }
704
705
706 /*
707 * net_init():
708 *
709 * This function creates a network, and returns a pointer to it.
710 *
711 * ipv4addr should be something like "10.0.0.0", netipv4len = 8.
712 *
713 * If n_remote is more than zero, remote should be a pointer to an array
714 * of strings of the following format: "host:portnr".
715 *
716 * Network settings are registered if settings_prefix is non-NULL.
717 * (The one calling net_init() is also responsible for calling net_deinit().)
718 *
719 * On failure, exit() is called.
720 */
721 struct net *net_init(struct emul *emul, int init_flags,
722 const char *ipv4addr, int netipv4len,
723 char **remote, int n_remote, int local_port,
724 const char *settings_prefix)
725 {
726 struct net *net;
727 int res;
728
729 CHECK_ALLOCATION(net = malloc(sizeof(struct net)));
730 memset(net, 0, sizeof(struct net));
731
732 /* Set the back pointer: */
733 net->emul = emul;
734
735 /* Sane defaults: */
736 net->timestamp = 0;
737 net->first_ethernet_packet = net->last_ethernet_packet = NULL;
738
739 #ifdef HAVE_INET_PTON
740 res = inet_pton(AF_INET, ipv4addr, &net->netmask_ipv4);
741 #else
742 res = inet_aton(ipv4addr, &net->netmask_ipv4);
743 #endif
744 if (res < 1) {
745 fprintf(stderr, "net_init(): could not parse IPv4 address"
746 " '%s'\n", ipv4addr);
747 exit(1);
748 }
749
750 if (netipv4len < 1 || netipv4len > 30) {
751 fprintf(stderr, "net_init(): extremely weird ipv4 "
752 "network length (%i)\n", netipv4len);
753 exit(1);
754 }
755 net->netmask_ipv4_len = netipv4len;
756
757 net->nameserver_known = 0;
758 net->domain_name = "";
759 parse_resolvconf(net);
760
761 /* Distributed network? Then add remote hosts: */
762 if (local_port != 0) {
763 struct sockaddr_in si_self;
764
765 net->local_port = local_port;
766 net->local_port_socket = socket(AF_INET, SOCK_DGRAM, 0);
767 if (net->local_port_socket < 0) {
768 perror("socket");
769 exit(1);
770 }
771
772 memset((char *)&si_self, 0, sizeof(si_self));
773 si_self.sin_family = AF_INET;
774 si_self.sin_port = htons(local_port);
775 si_self.sin_addr.s_addr = htonl(INADDR_ANY);
776 if (bind(net->local_port_socket, (struct sockaddr *)&si_self,
777 sizeof(si_self)) < 0) {
778 perror("bind");
779 exit(1);
780 }
781
782 /* Set the socket to non-blocking: */
783 res = fcntl(net->local_port_socket, F_GETFL);
784 fcntl(net->local_port_socket, F_SETFL, res | O_NONBLOCK);
785 }
786 if (n_remote != 0) {
787 struct remote_net *rnp;
788 while ((n_remote--) != 0) {
789 struct hostent *hp;
790
791 /* debug("adding '%s'\n", remote[n_remote]); */
792 CHECK_ALLOCATION(rnp =
793 malloc(sizeof(struct remote_net)));
794 memset(rnp, 0, sizeof(struct remote_net));
795
796 rnp->next = net->remote_nets;
797 net->remote_nets = rnp;
798
799 CHECK_ALLOCATION(rnp->name = strdup(remote[n_remote]));
800 if (strchr(rnp->name, ':') != NULL)
801 strchr(rnp->name, ':')[0] = '\0';
802
803 hp = gethostbyname(rnp->name);
804 if (hp == NULL) {
805 fprintf(stderr, "could not resolve '%s'\n",
806 rnp->name);
807 exit(1);
808 }
809 memcpy(&rnp->ipv4_addr, hp->h_addr, hp->h_length);
810 free(rnp->name);
811
812 /* And again: */
813 CHECK_ALLOCATION(rnp->name = strdup(remote[n_remote]));
814 if (strchr(rnp->name, ':') == NULL) {
815 fprintf(stderr, "Remote network '%s' is not "
816 "'host:portnr'?\n", rnp->name);
817 exit(1);
818 }
819 rnp->portnr = atoi(strchr(rnp->name, ':') + 1);
820 }
821 }
822
823 if (init_flags & NET_INIT_FLAG_GATEWAY)
824 net_gateway_init(net);
825
826 net_dumpinfo(net);
827
828 /* This is necessary when using the real network: */
829 signal(SIGPIPE, SIG_IGN);
830
831 return net;
832 }
833

  ViewVC Help
Powered by ViewVC 1.1.26