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

Contents of /trunk/src/net/net.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 40 - (show annotations)
Mon Oct 8 16:22:11 2007 UTC (16 years, 6 months ago) by dpavlin
File MIME type: text/plain
File size: 22060 byte(s)
++ trunk/HISTORY	(local)
$Id: HISTORY,v 1.1539 2007/05/01 04:03:51 debug Exp $
20070415	Landisk PCLOCK should be 33.33 MHz, not 50 MHz. (This makes
		the clock run at correct speed.)
		FINALLY found and fixed the bug which caused OpenBSD/landisk
		to randomly bug out: an &-sign was missing in the special case
		handling of FPSCR in the 'LDS.L @Rm+,FPSCR' instruction.
		Adding similar special case handling for 'LDC.L @Rm+,SR'
		(calling sh_update_sr() instead of just loading).
		Implementing the 'FCNVSD FPUL,DRn' and 'FCNVDS DRm,FPUL'
		SuperH instructions.
		The 'LDC Rm,SR' instruction now immediately breaks out of the
		dyntrans loop if an interrupt is to be triggered.
20070416	In memory_rw.c, if mapping a page as writable, make sure to
		invalidate code translations even if the data access was a
		read.
		Minor SuperH updates.
20070418	Removing the dummy M68K emulation mode.
		Minor SH update (turning unnecessary sts_mach_rn, sts_macl_rn,
		and sts_pr_rn instruction handlers into mov_rm_rn).
20070419	Beginning to add a skeleton for an M88K mode: Adding a hack to
		allow OpenBSD/m88k a.out binaries to be loaded, and disassembly
		of a few simple 88K instructions.
		Commenting out the 'LDC Rm,SR' fix from a few days ago, because
		it made Linux/dreamcast bug out.
		Adding a hack to dev_sh4.c (an extra translation cache
		invalidation), which allows OpenBSD/landisk to boot ok after
		an install. Upgrading the Landisk machine mode to stable,
		updating documentation, etc.
20070420	Experimenting with adding a PCI controller (pcic) to dev_sh4.
		Adding a dummy Realtek 8139C+ skeleton device (dev_rtl8139c).
		Implementing the first M88K instructions (br, or[.u] imm), and
		adding disassembly of some more instructions.
20070421	Continuing a little on dev_rtl8139c.
20070422	Implementing the 9346 EEPROM "read" command for dev_rtl8139c.
		Finally found and fixed an old bug in the log n symbol search
		(it sometimes missed symbols). Debug trace (-i, -t etc) should
		now show more symbols. :-)
20070423	Continuing a little on M88K disassembly.
20070428	Fixing a memset arg order bug in src/net/net.c (thanks to
		Nigel Horne for noticing the bug).
		Applying parts of a patch from Carl van Schaik to clear out
		bottom bits of MIPS addresses more correctly, when using large
		page sizes, and doing some other minor cleanup/refactoring.
		Fixing a couple of warnings given by gcc with the -W option (a
		few more warnings than just plain -Wall).
		Reducing SuperH dyntrans physical address space from 64-bit to
		32-bit (since SH5/SH64 isn't imlemented yet anyway).
		Adding address-to-symbol annotation to a few more instructions
		in the SuperH instruction trace output.
		Beginning regression testing for the next release.
		Reverting the value of SCIF_DELAYED_TX_VALUE from 1 to 2,
		because OpenBSD/landisk may otherwise hang randomly.
20070429	The ugly hack/workaround to get OpenBSD/landisk booting without
		crashing does NOT work anymore (with the April 21 snapshot
		of OpenBSD/landisk). Strangely enough, removing the hack
		completely causes OpenBSD/landisk to work (!).
		More regression testing (re-testing everything SuperH-related,
		and some other things).
		Cobalt interrupts were actually broken; fixing by commenting
		out the DEC21143s in the Cobalt machine.
20070430	More regression testing.
20070501	Updating the OpenBSD/landisk install instructions to use
		4.1 instead of the current snapshot.
		GAAAH! OpenBSD/landisk 4.1 _needs_ the ugly hack/workaround;
		reintroducing it again. (The 4.1 kernel is actually from
		2007-03-11.)
		Simplifying the NetBSD/evbarm install instructions a bit.
		More regression testing.

==============  RELEASE 0.4.5.1  ==============


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

  ViewVC Help
Powered by ViewVC 1.1.26