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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 32 - (show annotations)
Mon Oct 8 16:20:58 2007 UTC (16 years, 6 months ago) by dpavlin
File MIME type: text/plain
File size: 44463 byte(s)
++ trunk/HISTORY	(local)
$Id: HISTORY,v 1.1421 2006/11/06 05:32:37 debug Exp $
20060816	Adding a framework for emulated/virtual timers (src/timer.c),
		using only setitimer().
		Rewriting the mc146818 to use the new timer framework.
20060817	Adding a call to gettimeofday() every now and then (once every
		second, at the moment) to resynch the timer if it drifts.
		Beginning to convert the ISA timer interrupt mechanism (8253
		and 8259) to use the new timer framework.
		Removing the -I command line option.
20060819	Adding the -I command line option again, with new semantics.
		Working on Footbridge timer interrupts; NetBSD/NetWinder and
		NetBSD/CATS now run at correct speed, but unfortunately with
		HUGE delays during bootup.
20060821	Some minor m68k updates. Adding the first instruction: nop. :)
		Minor Alpha emulation updates.
20060822	Adding a FreeBSD development specific YAMON environment
		variable ("khz") (as suggested by Bruce M. Simpson).
		Moving YAMON environment variable initialization from
		machine_evbmips.c into promemul/yamon.c, and adding some more
		variables.
		Continuing on the LCA PCI bus controller (for Alpha machines).
20060823	Continuing on the timer stuff: experimenting with MIPS count/
		compare interrupts connected to the timer framework.
20060825	Adding bogus SCSI commands 0x51 (SCSICDROM_READ_DISCINFO) and
		0x52 (SCSICDROM_READ_TRACKINFO) to the SCSI emulation layer,
		to allow NetBSD/pmax 4.0_BETA to be installed from CDROM.
		Minor updates to the LCA PCI controller.
20060827	Implementing a CHIP8 cpu mode, and a corresponding CHIP8
		machine, for fun. Disassembly support for all instructions,
		and most of the common instructions have been implemented: mvi,
		mov_imm, add_imm, jmp, rand, cls, sprite, skeq_imm, jsr,
		skne_imm, bcd, rts, ldr, str, mov, or, and, xor, add, sub,
		font, ssound, sdelay, gdelay, bogus skup/skpr, skeq, skne.
20060828	Beginning to convert the CHIP8 cpu in the CHIP8 machine to a
		(more correct) RCA 180x cpu. (Disassembly for all 1802
		instructions has been implemented, but no execution yet, and
		no 1805 extended instructions.)
20060829	Minor Alpha emulation updates.
20060830	Beginning to experiment a little with PCI IDE for SGI O2.
		Fixing the cursor key mappings for MobilePro 770 emulation.
		Fixing the LK201 warning caused by recent NetBSD/pmax.
		The MIPS R41xx standby, suspend, and hibernate instructions now
		behave like the RM52xx/MIPS32/MIPS64 wait instruction.
		Fixing dev_wdc so it calculates correct (64-bit) offsets before
		giving them to diskimage_access().
20060831	Continuing on Alpha emulation (OSF1 PALcode).
20060901	Minor Alpha updates; beginning on virtual memory pagetables.
		Removed the limit for max nr of devices (in preparation for
		allowing devices' base addresses to be changed during runtime).
		Adding a hack for MIPS [d]mfc0 select 0 (except the count
		register), so that the coproc register is simply copied.
		The MIPS suspend instruction now exits the emulator, instead
		of being treated as a wait instruction (this causes NetBSD/
		hpcmips to get correct 'halt' behavior).
		The VR41xx RTC now returns correct time.
		Connecting the VR41xx timer to the timer framework (fixed at
		128 Hz, for now).
		Continuing on SPARC emulation, adding more instructions:
		restore, ba_xcc, ble. The rectangle drawing demo works :)
		Removing the last traces of the old ENABLE_CACHE_EMULATION
		MIPS stuff (not usable with dyntrans anyway).
20060902	Splitting up src/net.c into several smaller files in its own
		subdirectory (src/net/).
20060903	Cleanup of the files in src/net/, to make them less ugly.
20060904	Continuing on the 'settings' subsystem.
		Minor progress on the SPARC emulation mode.
20060905	Cleanup of various things, and connecting the settings
		infrastructure to various subsystems (emul, machine, cpu, etc).
		Changing the lk201 mouse update routine to not rely on any
		emulated hardware framebuffer cursor coordinates, but instead
		always do (semi-usable) relative movements.
20060906	Continuing on the lk201 mouse stuff. Mouse behaviour with
		multiple framebuffers (which was working in Ultrix) is now
		semi-broken (but it still works, in a way).
		Moving the documentation about networking into its own file
		(networking.html), and refreshing it a bit. Adding an example
		of how to use ethernet frame direct-access (udp_snoop).
20060907	Continuing on the settings infrastructure.
20060908	Minor updates to SH emulation: for 32-bit emulation: delay
		slots and the 'jsr @Rn' instruction. I'm putting 64-bit SH5 on
		ice, for now.
20060909-10	Implementing some more 32-bit SH instructions. Removing the
		64-bit mode completely. Enough has now been implemented to run
		the rectangle drawing demo. :-)
20060912	Adding more SH instructions.
20060916	Continuing on SH emulation (some more instructions: div0u,
		div1, rotcl/rotcr, more mov instructions, dt, braf, sets, sett,
		tst_imm, dmuls.l, subc, ldc_rm_vbr, movt, clrt, clrs, clrmac).
		Continuing on the settings subsystem (beginning on reading/
		writing settings, removing bugs, and connecting more cpus to
		the framework).
20060919	More work on SH emulation; adding an ldc banked instruction,
		and attaching a 640x480 framebuffer to the Dreamcast machine
		mode (NetBSD/dreamcast prints the NetBSD copyright banner :-),
		and then panics).
20060920	Continuing on the settings subsystem.
20060921	Fixing the Footbridge timer stuff so that NetBSD/cats and
		NetBSD/netwinder boot up without the delays.
20060922	Temporarily hardcoding MIPS timer interrupt to 100 Hz. With
		'wait' support disabled, NetBSD/malta and Linux/malta run at
		correct speed.
20060923	Connecting dev_gt to the timer framework, so that NetBSD/cobalt
		runs at correct speed.
		Moving SH4-specific memory mapped registers into its own
		device (dev_sh4.c).
		Running with -N now prints "idling" instead of bogus nr of
		instrs/second (which isn't valid anyway) while idling.
20060924	Algor emulation should now run at correct speed.
		Adding disassembly support for some MIPS64 revision 2
		instructions: ext, dext, dextm, dextu.
20060926	The timer framework now works also when the MIPS wait
		instruction is used.
20060928	Re-implementing checks for coprocessor availability for MIPS
		cop0 instructions. (Thanks to Carl van Schaik for noticing the
		lack of cop0 availability checks.)
20060929	Implementing an instruction combination hack which treats
		NetBSD/pmax' idle loop as a wait-like instruction.
20060930	The ENTRYHI_R_MASK was missing in (at least) memory_mips_v2p.c,
		causing TLB lookups to sometimes succeed when they should have
		failed. (A big thank you to Juli Mallett for noticing the
		problem.)
		Adding disassembly support for more MIPS64 revision 2 opcodes
		(seb, seh, wsbh, jalr.hb, jr.hb, synci, ins, dins, dinsu,
		dinsm, dsbh, dshd, ror, dror, rorv, drorv, dror32). Also
		implementing seb, seh, dsbh, dshd, and wsbh.
		Implementing an instruction combination hack for Linux/pmax'
		idle loop, similar to the NetBSD/pmax case.
20061001	Changing the NetBSD/sgimips install instructions to extract
		files from an iso image, instead of downloading them via ftp.
20061002	More-than-31-bit userland addresses in memory_mips_v2p.c were
		not actually working; applying a fix from Carl van Schaik to
		enable them to work + making some other updates (adding kuseg
		support).
		Fixing hpcmips (vr41xx) timer initialization.
		Experimenting with O(n)->O(1) reduction in the MIPS TLB lookup
		loop. Seems to work both for R3000 and non-R3000.
20061003	Continuing a little on SH emulation (adding more control
		registers; mini-cleanup of memory_sh.c).
20061004	Beginning on a dev_rtc, a clock/timer device for the test
		machines; also adding a demo, and some documentation.
		Fixing a bug in SH "mov.w @(disp,pc),Rn" (the result wasn't
		sign-extended), and adding the addc and ldtlb instructions.
20061005	Contining on SH emulation: virtual to physical address
		translation, and a skeleton exception mechanism.
20061006	Adding more SH instructions (various loads and stores, rte,
		negc, muls.w, various privileged register-move instructions).
20061007	More SH instructions: various move instructions, trapa, div0s,
		float, fdiv, ftrc.
		Continuing on dev_rtc; removing the rtc demo.
20061008	Adding a dummy Dreamcast PROM module. (Homebrew Dreamcast
		programs using KOS libs need this.)
		Adding more SH instructions: "stc vbr,rn", rotl, rotr, fsca,
		fmul, fadd, various floating-point moves, etc. A 256-byte
		demo for Dreamcast runs :-)
20061012	Adding the SH "lds Rm,pr" and bsr instructions.
20061013	More SH instructions: "sts fpscr,rn", tas.b, and some more
		floating point instructions, cmp/str, and more moves.
		Adding a dummy dev_pvr (Dreamcast graphics controller).
20061014	Generalizing the expression evaluator (used in the built-in
		debugger) to support parentheses and +-*/%^&|.
20061015	Removing the experimental tlb index hint code in
		mips_memory_v2p.c, since it didn't really have any effect.
20061017	Minor SH updates; adding the "sts pr,Rn", fcmp/gt, fneg,
		frchg, and some other instructions. Fixing missing sign-
		extension in an 8-bit load instruction.
20061019	Adding a simple dev_dreamcast_rtc.
		Implementing memory-mapped access to the SH ITLB/UTLB arrays.
20061021	Continuing on various SH and Dreamcast things: sh4 timers,
		debug messages for dev_pvr, fixing some virtual address
		translation bugs, adding the bsrf instruction.
		The NetBSD/dreamcast GENERIC_MD kernel now reaches userland :)
		Adding a dummy dev_dreamcast_asic.c (not really useful yet).
		Implementing simple support for Store Queues.
		Beginning on the PVR Tile Accelerator.
20061022	Generalizing the PVR framebuffer to support off-screen drawing,
		multiple bit-depths, etc. (A small speed penalty, but most
		likely worth it.)
		Adding more SH instructions (mulu.w, fcmp/eq, fsub, fmac,
		fschg, and some more); correcting bugs in "fsca" and "float".
20061024	Adding the SH ftrv (matrix * vector) instruction. Marcus
		Comstedt's "tatest" example runs :) (wireframe only).
		Correcting disassembly for SH floating point instructions that
		use the xd* registers.
		Adding the SH fsts instruction.
		In memory_device_dyntrans_access(), only the currently used
		range is now invalidated, and not the entire device range.
20061025	Adding a dummy AVR32 cpu mode skeleton.
20061026	Various Dreamcast updates; beginning on a Maple bus controller.
20061027	Continuing on the Maple bus. A bogus Controller, Keyboard, and
		Mouse can now be detected by NetBSD and KOS homebrew programs.
		Cleaning up the SH4 Timer Management Unit, and beginning on
		SH4 interrupts.
		Implementing the Dreamcast SYSASIC.
20061028	Continuing on the SYSASIC.
		Adding the SH fsqrt instruction.
		memory_sh.c now actually scans the ITLB.
		Fixing a bug in dev_sh4.c, related to associative writes into
		the memory-mapped UTLB array. NetBSD/dreamcast now reaches
		userland stably, and prints the "Terminal type?" message :-]
		Implementing enough of the Dreamcast keyboard to make NetBSD
		accept it for input.
		Enabling SuperH for stable (non-development) builds.
		Adding NetBSD/dreamcast to the documentation, although it
		doesn't support root-on-nfs yet.
20061029	Changing usleep(1) calls in the debugger to to usleep(10000)
		(according to Brian Foley, this makes GXemul run better on
		MacOS X).
		Making the Maple "Controller" do something (enough to barely
		interact with dcircus.elf).
20061030-31	Some progress on the PVR. More test programs start running (but
		with strange output).
		Various other SH4-related updates.
20061102	Various Dreamcast and SH4 updates; more KOS demos run now.
20061104	Adding a skeleton dev_mb8696x.c (the Dreamcast's LAN adapter).
20061105	Continuing on the MB8696x; NetBSD/dreamcast detects it as mbe0.
		Testing for the release.

==============  RELEASE 0.4.3  ==============


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

  ViewVC Help
Powered by ViewVC 1.1.26