/[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

Annotation of /trunk/src/net/net.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 42 - (hide annotations)
Mon Oct 8 16:22:32 2007 UTC (16 years, 8 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 dpavlin 32 /*
2 dpavlin 34 * Copyright (C) 2004-2007 Anders Gavare. All rights reserved.
3 dpavlin 32 *
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 dpavlin 42 * $Id: net.c,v 1.10 2007/06/15 17:02:39 debug Exp $
29 dpavlin 32 *
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 dpavlin 42 CHECK_ALLOCATION(lp = malloc(sizeof(struct ethernet_packet_link)));
80 dpavlin 32
81     lp->len = len;
82     lp->extra = extra;
83 dpavlin 42 CHECK_ALLOCATION(lp->data = malloc(len));
84 dpavlin 32
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 dpavlin 42 CHECK_ALLOCATION(net->domain_name = strdup(buf+start));
588 dpavlin 32 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 dpavlin 42 CHECK_ALLOCATION(net->nic_extra = realloc(net->nic_extra, sizeof(void *)
611     * net->n_nics));
612 dpavlin 32
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 dpavlin 42 CHECK_ALLOCATION(net = malloc(sizeof(struct net)));
730 dpavlin 32 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 dpavlin 40 memset((char *)&si_self, 0, sizeof(si_self));
773 dpavlin 32 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 dpavlin 42 CHECK_ALLOCATION(rnp =
793     malloc(sizeof(struct remote_net)));
794 dpavlin 32 memset(rnp, 0, sizeof(struct remote_net));
795    
796     rnp->next = net->remote_nets;
797     net->remote_nets = rnp;
798    
799 dpavlin 42 CHECK_ALLOCATION(rnp->name = strdup(remote[n_remote]));
800 dpavlin 32 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 dpavlin 42 CHECK_ALLOCATION(rnp->name = strdup(remote[n_remote]));
814 dpavlin 32 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