diff --git a/src/Makefile.mingw-network b/src/Makefile.mingw-network new file mode 100644 index 0000000..6adadca --- /dev/null +++ b/src/Makefile.mingw-network @@ -0,0 +1,51 @@ +VPATH = . dosbox resid-fp +CPP = g++.exe +CC = gcc.exe +MAKE = make.exe +WINDRES = windres.exe +CFLAGS = -O3 -march=i686 -fomit-frame-pointer -msse2 -mstackrealign -Wall -Werror -fno-strict-aliasing -DUSE_NETWORKING +OBJ = 386.o 386_dynarec.o 386_dynarec_ops.o 808x.o acer386sx.o ali1429.o amstrad.o cdrom-ioctl.o cdrom-image.o \ + codegen.o codegen_ops.o codegen_timing_486.o codegen_timing_686.o codegen_timing_common.o codegen_timing_pentium.o codegen_timing_winchip.o codegen_x86.o compaq.o config.o cpu.o dac.o \ + dells200.o device.o disc.o disc_fdi.o disc_img.o disc_sector.o dma.o esdi_at.o fdc.o fdc37c665.o fdd.o fdi2raw.o gameport.o hdd.o hdd_esdi.o hdd_file.o headland.o i430lx.o i430fx.o \ + i430vx.o ide.o intel.o intel_flash.o io.o jim.o joystick_ch_flightstick_pro.o joystick_standard.o joystick_sw_pad.o joystick_tm_fcs.o keyboard.o keyboard_amstrad.o keyboard_at.o \ + keyboard_olim24.o keyboard_pcjr.o keyboard_xt.o laserxt.o lpt.o mca.o mcr.o mem.o mfm_at.o mfm_xebec.o model.o mouse.o mouse_ps2.o \ + mouse_serial.o neat.o nmi.o nvr.o olivetti_m24.o opti495.o paths.o pc.o pci.o pic.o piix.o pit.o ppi.o ps1.o ps2.o ps2_mca.o ps2_nvr.o rom.o rtc.o \ + scat.o serial.o sio.o sis496.o sound.o sound_ad1848.o sound_adlib.o sound_adlibgold.o sound_cms.o sound_dbopl.o \ + sound_emu8k.o sound_gus.o sound_mpu401_uart.o sound_opl.o sound_pas16.o sound_ps1.o sound_pssj.o sound_resid.o \ + sound_sb.o sound_sb_dsp.o sound_sn76489.o sound_speaker.o sound_ssi2001.o sound_wss.o \ + sound_ym7128.o soundopenal.o tandy_eeprom.o tandy_rom.o timer.o um8881f.o um8669f.o vid_ati_eeprom.o vid_ati_mach64.o vid_ati18800.o \ + vid_ati28800.o vid_ati68860_ramdac.o vid_cga.o vid_cl5429.o vid_colorplus.o vid_ega.o vid_et4000.o \ + vid_et4000w32.o vid_et4000w32i.o vid_genius.o vid_hercules.o vid_icd2061.o vid_ics2595.o vid_incolor.o vid_mda.o \ + vid_olivetti_m24.o vid_oti067.o vid_paradise.o vid_pc1512.o vid_pc1640.o vid_pc200.o \ + vid_pcjr.o vid_ps1_svga.o vid_s3.o vid_s3_virge.o vid_sdac_ramdac.o vid_stg_ramdac.o vid_svga.o \ + vid_svga_render.o vid_tandy.o vid_tandysl.o vid_tgui9440.o vid_tkd8001_ramdac.o vid_tvga.o vid_unk_ramdac.o \ + vid_vga.o vid_voodoo.o vid_wy700.o video.o wd76c10.o win.o win-config.o win-config_sel.o win-d3d.o win-d3d-fs.o win-ddraw.o \ + win-ddraw-fs.o win-deviceconfig.o win-hdconf.o win-joystick.o win-joystickconfig.o win-keyboard.o win-midi.o win-mouse.o \ + win-status.o win-video.o x86seg.o x87.o xtide.o pc.res +DBOBJ = cdrom_image.o dbopl.o nukedopl.o vid_cga_comp.o +SIDOBJ = convolve.o convolve-sse.o envelope.o extfilt.o filter.o pot.o sid.o voice.o wave6581__ST.o wave6581_P_T.o wave6581_PS_.o wave6581_PST.o wave8580__ST.o wave8580_P_T.o wave8580_PS_.o wave8580_PST.o wave.o +NETOBJ = ne2000.o nethandler.o + +LIBS = -mwindows -lwinmm -lopenal32 -lddraw -ldinput -ldxguid -ld3d9 -lwsock32 -liphlpapi -lstdc++ + +PCem.exe: $(OBJ) $(DBOBJ) $(SIDOBJ) $(NETOBJ) libslirp.a + $(CC) $(OBJ) $(DBOBJ) $(SIDOBJ) $(NETOBJ) slirp/libslirp.a -o "PCem.exe" $(LIBS) + +libslirp.a: + $(MAKE) -C slirp + +all : libslirp.a PCem.exe + +clean : + rm *.o + rm *.exe + rm *.res + +%.o : %.c + $(CC) $(CFLAGS) -c $< + +%.o : %.cc + $(CPP) $(CFLAGS) -c $< + +pc.res: pc.rc + $(WINDRES) -i pc.rc --input-format=rc -o pc.res -O coff -D USE_NETWORKING diff --git a/src/bswap.h b/src/bswap.h new file mode 100644 index 0000000..c473c50 --- /dev/null +++ b/src/bswap.h @@ -0,0 +1,202 @@ +#ifndef BSWAP_H +#define BSWAP_H + +//#include "config-host.h" + +#include + +#ifdef HAVE_BYTESWAP_H +#include +#else + +#define bswap_16(x) \ +({ \ + uint16_t __x = (x); \ + ((uint16_t)( \ + (((uint16_t)(__x) & (uint16_t)0x00ffU) << 8) | \ + (((uint16_t)(__x) & (uint16_t)0xff00U) >> 8) )); \ +}) + +#define bswap_32(x) \ +({ \ + uint32_t __x = (x); \ + ((uint32_t)( \ + (((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \ + (((uint32_t)(__x) & (uint32_t)0x0000ff00UL) << 8) | \ + (((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >> 8) | \ + (((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) )); \ +}) + +#define bswap_64(x) \ +({ \ + uint64_t __x = (x); \ + ((uint64_t)( \ + (uint64_t)(((uint64_t)(__x) & (uint64_t)0x00000000000000ffULL) << 56) | \ + (uint64_t)(((uint64_t)(__x) & (uint64_t)0x000000000000ff00ULL) << 40) | \ + (uint64_t)(((uint64_t)(__x) & (uint64_t)0x0000000000ff0000ULL) << 24) | \ + (uint64_t)(((uint64_t)(__x) & (uint64_t)0x00000000ff000000ULL) << 8) | \ + (uint64_t)(((uint64_t)(__x) & (uint64_t)0x000000ff00000000ULL) >> 8) | \ + (uint64_t)(((uint64_t)(__x) & (uint64_t)0x0000ff0000000000ULL) >> 24) | \ + (uint64_t)(((uint64_t)(__x) & (uint64_t)0x00ff000000000000ULL) >> 40) | \ + (uint64_t)(((uint64_t)(__x) & (uint64_t)0xff00000000000000ULL) >> 56) )); \ +}) + +#endif /* !HAVE_BYTESWAP_H */ + +static inline uint16_t bswap16(uint16_t x) +{ + return bswap_16(x); +} + +static inline uint32_t bswap32(uint32_t x) +{ + return bswap_32(x); +} + +static inline uint64_t bswap64(uint64_t x) +{ + return bswap_64(x); +} + +static inline void bswap16s(uint16_t *s) +{ + *s = bswap16(*s); +} + +static inline void bswap32s(uint32_t *s) +{ + *s = bswap32(*s); +} + +static inline void bswap64s(uint64_t *s) +{ + *s = bswap64(*s); +} + +#if defined(WORDS_BIGENDIAN) +#define be_bswap(v, size) (v) +#define le_bswap(v, size) bswap ## size(v) +#define be_bswaps(v, size) +#define le_bswaps(p, size) *p = bswap ## size(*p); +#else +#define le_bswap(v, size) (v) +#define be_bswap(v, size) bswap ## size(v) +#define le_bswaps(v, size) +#define be_bswaps(p, size) *p = bswap ## size(*p); +#endif + +#define CPU_CONVERT(endian, size, type)\ +static inline type endian ## size ## _to_cpu(type v)\ +{\ + return endian ## _bswap(v, size);\ +}\ +\ +static inline type cpu_to_ ## endian ## size(type v)\ +{\ + return endian ## _bswap(v, size);\ +}\ +\ +static inline void endian ## size ## _to_cpus(type *p)\ +{\ + endian ## _bswaps(p, size)\ +}\ +\ +static inline void cpu_to_ ## endian ## size ## s(type *p)\ +{\ + endian ## _bswaps(p, size)\ +}\ +\ +static inline type endian ## size ## _to_cpup(const type *p)\ +{\ + return endian ## size ## _to_cpu(*p);\ +}\ +\ +static inline void cpu_to_ ## endian ## size ## w(type *p, type v)\ +{\ + *p = cpu_to_ ## endian ## size(v);\ +} + +CPU_CONVERT(be, 16, uint16_t) +CPU_CONVERT(be, 32, uint32_t) +CPU_CONVERT(be, 64, uint64_t) + +CPU_CONVERT(le, 16, uint16_t) +CPU_CONVERT(le, 32, uint32_t) +CPU_CONVERT(le, 64, uint64_t) + +/* unaligned versions (optimized for frequent unaligned accesses)*/ + +#if defined(__i386__) || defined(__powerpc__) + +#define cpu_to_le16wu(p, v) cpu_to_le16w(p, v) +#define cpu_to_le32wu(p, v) cpu_to_le32w(p, v) +#define le16_to_cpupu(p) le16_to_cpup(p) +#define le32_to_cpupu(p) le32_to_cpup(p) + +#define cpu_to_be16wu(p, v) cpu_to_be16w(p, v) +#define cpu_to_be32wu(p, v) cpu_to_be32w(p, v) + +#else + +static inline void cpu_to_le16wu(uint16_t *p, uint16_t v) +{ + uint8_t *p1 = (uint8_t *)p; + + p1[0] = v; + p1[1] = v >> 8; +} + +static inline void cpu_to_le32wu(uint32_t *p, uint32_t v) +{ + uint8_t *p1 = (uint8_t *)p; + + p1[0] = v; + p1[1] = v >> 8; + p1[2] = v >> 16; + p1[3] = v >> 24; +} + +static inline uint16_t le16_to_cpupu(const uint16_t *p) +{ + const uint8_t *p1 = (const uint8_t *)p; + return p1[0] | (p1[1] << 8); +} + +static inline uint32_t le32_to_cpupu(const uint32_t *p) +{ + const uint8_t *p1 = (const uint8_t *)p; + return p1[0] | (p1[1] << 8) | (p1[2] << 16) | (p1[3] << 24); +} + +static inline void cpu_to_be16wu(uint16_t *p, uint16_t v) +{ + uint8_t *p1 = (uint8_t *)p; + + p1[0] = v >> 8; + p1[1] = v; +} + +static inline void cpu_to_be32wu(uint32_t *p, uint32_t v) +{ + uint8_t *p1 = (uint8_t *)p; + + p1[0] = v >> 24; + p1[1] = v >> 16; + p1[2] = v >> 8; + p1[3] = v; +} + +#endif + +#ifdef WORDS_BIGENDIAN +#define cpu_to_32wu cpu_to_be32wu +#else +#define cpu_to_32wu cpu_to_le32wu +#endif + +#undef le_bswap +#undef be_bswap +#undef le_bswaps +#undef be_bswaps + +#endif /* BSWAP_H */ diff --git a/src/ne2000.c b/src/ne2000.c new file mode 100644 index 0000000..afbabb2 --- /dev/null +++ b/src/ne2000.c @@ -0,0 +1,1748 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: ne2k.cc,v 1.56.2.1 2004/02/02 22:37:22 cbothamy Exp $ +///////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2002 MandrakeSoft S.A. +// +// MandrakeSoft S.A. +// 43, rue d'Aboukir +// 75002 Paris - France +// http://www.linux-mandrake.com/ +// http://www.mandrakesoft.com/ +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +// Peter Grehan (grehan@iprg.nokia.com) coded all of this +// NE2000/ether stuff. +//#include "vl.h" +#include +#include +#include +#include + +#include "slirp/slirp.h" +#include "slirp/queue.h" +#include + +#include "ibm.h" +#include "device.h" + +#include "nethandler.h" + +#include "config.h" +#include "io.h" +#include "ne2000.h" +#include "pic.h" +#include "timer.h" + +//THIS IS THE DEFAULT MAC ADDRESS .... so it wont place nice with multiple VMs. YET. +uint8_t maclocal[6] = {0xac, 0xde, 0x48, 0x88, 0xbb, 0xaa}; + +#define NETBLOCKING 0 //we won't block our pcap + +static HINSTANCE net_hLib = 0; /* handle to DLL */ +static char *net_lib_name = "wpcap.dll"; +pcap_t *net_pcap; +typedef pcap_t* (__cdecl * PCAP_OPEN_LIVE)(const char *, int, int, int, char *); +typedef int (__cdecl * PCAP_SENDPACKET)(pcap_t* handle, const u_char* msg, int len); +typedef int (__cdecl * PCAP_SETNONBLOCK)(pcap_t *, int, char *); +typedef const u_char*(__cdecl *PCAP_NEXT)(pcap_t *, struct pcap_pkthdr *); +typedef const char*(__cdecl *PCAP_LIB_VERSION)(void); +typedef void (__cdecl *PCAP_CLOSE)(pcap_t *); +typedef int (__cdecl *PCAP_GETNONBLOCK)(pcap_t *p, char *errbuf); +typedef int (__cdecl *PCAP_COMPILE)(pcap_t *p, struct bpf_program *fp, const char *str, int optimize, bpf_u_int32 netmask); +typedef int (__cdecl *PCAP_SETFILTER)(pcap_t *p, struct bpf_program *fp); + +PCAP_LIB_VERSION _pcap_lib_version; +PCAP_OPEN_LIVE _pcap_open_live; +PCAP_SENDPACKET _pcap_sendpacket; +PCAP_SETNONBLOCK _pcap_setnonblock; +PCAP_NEXT _pcap_next; +PCAP_CLOSE _pcap_close; +PCAP_GETNONBLOCK _pcap_getnonblock; +PCAP_COMPILE _pcap_compile; +PCAP_SETFILTER _pcap_setfilter; + +queueADT slirpq; +int net_slirp_inited = 0; +int net_is_slirp = 1; //by default we go with slirp +int net_is_pcap = 0; //and pretend pcap is dead. +int fizz = 0; +void slirp_tic(); + +#define BX_RESET_HARDWARE 0 +#define BX_RESET_SOFTWARE 1 + +//Never completely fill the ne2k ring so that we never +// hit the unclear completely full buffer condition. +#define BX_NE2K_NEVER_FULL_RING (1) + +#define BX_NE2K_MEMSIZ (32*1024) +#define BX_NE2K_MEMSTART (16*1024) +#define BX_NE2K_MEMEND (BX_NE2K_MEMSTART + BX_NE2K_MEMSIZ) + +typedef struct ne2000_t +{ + // + // ne2k register state + + // + // Page 0 + // + // Command Register - 00h read/write + struct CR_t + { + int stop; // STP - Software Reset command + int start; // START - start the NIC + int tx_packet; // TXP - initiate packet transmission + uint8_t rdma_cmd; // RD0,RD1,RD2 - Remote DMA command + uint8_t pgsel; // PS0,PS1 - Page select + } CR; + // Interrupt Status Register - 07h read/write + struct ISR_t + { + int pkt_rx; // PRX - packet received with no errors + int pkt_tx; // PTX - packet transmitted with no errors + int rx_err; // RXE - packet received with 1 or more errors + int tx_err; // TXE - packet tx'd " " " " " + int overwrite; // OVW - rx buffer resources exhausted + int cnt_oflow; // CNT - network tally counter MSB's set + int rdma_done; // RDC - remote DMA complete + int reset; // RST - reset status + } ISR; + // Interrupt Mask Register - 0fh write + struct IMR_t + { + int rx_inte; // PRXE - packet rx interrupt enable + int tx_inte; // PTXE - packet tx interrput enable + int rxerr_inte; // RXEE - rx error interrupt enable + int txerr_inte; // TXEE - tx error interrupt enable + int overw_inte; // OVWE - overwrite warn int enable + int cofl_inte; // CNTE - counter o'flow int enable + int rdma_inte; // RDCE - remote DMA complete int enable + int reserved; // D7 - reserved + } IMR; + // Data Configuration Register - 0eh write + struct DCR_t + { + int wdsize; // WTS - 8/16-bit select + int endian; // BOS - byte-order select + int longaddr; // LAS - long-address select + int loop; // LS - loopback select + int auto_rx; // AR - auto-remove rx packets with remote DMA + uint8_t fifo_size; // FT0,FT1 - fifo threshold + } DCR; + // Transmit Configuration Register - 0dh write + struct TCR_t + { + int crc_disable; // CRC - inhibit tx CRC + uint8_t loop_cntl; // LB0,LB1 - loopback control + int ext_stoptx; // ATD - allow tx disable by external mcast + int coll_prio; // OFST - backoff algorithm select + uint8_t reserved; // D5,D6,D7 - reserved + } TCR; + // Transmit Status Register - 04h read + struct TSR_t + { + int tx_ok; // PTX - tx complete without error + int reserved; // D1 - reserved + int collided; // COL - tx collided >= 1 times + int aborted; // ABT - aborted due to excessive collisions + int no_carrier; // CRS - carrier-sense lost + int fifo_ur; // FU - FIFO underrun + int cd_hbeat; // CDH - no tx cd-heartbeat from transceiver + int ow_coll; // OWC - out-of-window collision + } TSR; + // Receive Configuration Register - 0ch write + struct RCR_t + { + int errors_ok; // SEP - accept pkts with rx errors + int runts_ok; // AR - accept < 64-byte runts + int broadcast; // AB - accept eth broadcast address + int multicast; // AM - check mcast hash array + int promisc; // PRO - accept all packets + int monitor; // MON - check pkts, but don't rx + uint8_t reserved; // D6,D7 - reserved + } RCR; + // Receive Status Register - 0ch read + struct RSR_t + { + int rx_ok; // PRX - rx complete without error + int bad_crc; // CRC - Bad CRC detected + int bad_falign; // FAE - frame alignment error + int fifo_or; // FO - FIFO overrun + int rx_missed; // MPA - missed packet error + int rx_mbit; // PHY - unicast or mcast/bcast address match + int rx_disabled; // DIS - set when in monitor mode + int deferred; // DFR - collision active + } RSR; + + uint16_t local_dma; // 01,02h read ; current local DMA addr + uint8_t page_start; // 01h write ; page start register + uint8_t page_stop; // 02h write ; page stop register + uint8_t bound_ptr; // 03h read/write ; boundary pointer + uint8_t tx_page_start; // 04h write ; transmit page start register + uint8_t num_coll; // 05h read ; number-of-collisions register + uint16_t tx_bytes; // 05,06h write ; transmit byte-count register + uint8_t fifo; // 06h read ; FIFO + uint16_t remote_dma; // 08,09h read ; current remote DMA addr + uint16_t remote_start; // 08,09h write ; remote start address register + uint16_t remote_bytes; // 0a,0bh write ; remote byte-count register + uint8_t tallycnt_0; // 0dh read ; tally counter 0 (frame align errors) + uint8_t tallycnt_1; // 0eh read ; tally counter 1 (CRC errors) + uint8_t tallycnt_2; // 0fh read ; tally counter 2 (missed pkt errors) + + // + // Page 1 + // + // Command Register 00h (repeated) + // + uint8_t physaddr[6]; // 01-06h read/write ; MAC address + uint8_t curr_page; // 07h read/write ; current page register + uint8_t mchash[8]; // 08-0fh read/write ; multicast hash array + + // + // Page 2 - diagnostic use only + // + // Command Register 00h (repeated) + // + // Page Start Register 01h read (repeated) + // Page Stop Register 02h read (repeated) + // Current Local DMA Address 01,02h write (repeated) + // Transmit Page start address 04h read (repeated) + // Receive Configuration Register 0ch read (repeated) + // Transmit Configuration Register 0dh read (repeated) + // Data Configuration Register 0eh read (repeated) + // Interrupt Mask Register 0fh read (repeated) + // + uint8_t rempkt_ptr; // 03h read/write ; remote next-packet pointer + uint8_t localpkt_ptr; // 05h read/write ; local next-packet pointer + uint16_t address_cnt; // 06,07h read/write ; address counter + + // + // Page 3 - should never be modified. + // + + // Novell ASIC state + uint8_t macaddr[32]; // ASIC ROM'd MAC address, even bytes + uint8_t mem[BX_NE2K_MEMSIZ]; // on-chip packet memory + + // ne2k internal state + uint32_t base_address; + int base_irq; + int tx_timer_index; + int tx_timer_active; +} ne2000_t; + +static void ne2000_tx_event(int val, void *p); + +void ne2000_rx_frame(void *p, const void *buf, int io_len); + +static void ne2000_setirq(ne2000_t *ne2000, int irq) +{ + ne2000->base_irq = irq; +} +// +// reset - restore state to power-up, cancelling all i/o +// +static void ne2000_reset(int type, void *p) +{ + ne2000_t *ne2000 = (ne2000_t *)p; + int i; + + pclog("ne2000 reset\n"); + + // Initialise the mac address area by doubling the physical address + ne2000->macaddr[0] = ne2000->physaddr[0]; + ne2000->macaddr[1] = ne2000->physaddr[0]; + ne2000->macaddr[2] = ne2000->physaddr[1]; + ne2000->macaddr[3] = ne2000->physaddr[1]; + ne2000->macaddr[4] = ne2000->physaddr[2]; + ne2000->macaddr[5] = ne2000->physaddr[2]; + ne2000->macaddr[6] = ne2000->physaddr[3]; + ne2000->macaddr[7] = ne2000->physaddr[3]; + ne2000->macaddr[8] = ne2000->physaddr[4]; + ne2000->macaddr[9] = ne2000->physaddr[4]; + ne2000->macaddr[10] = ne2000->physaddr[5]; + ne2000->macaddr[11] = ne2000->physaddr[5]; + + // ne2k signature + for (i = 12; i < 32; i++) + ne2000->macaddr[i] = 0x57; + + // Zero out registers and memory + memset(&ne2000->CR, 0, sizeof(ne2000->CR) ); + memset(&ne2000->ISR, 0, sizeof(ne2000->ISR)); + memset(&ne2000->IMR, 0, sizeof(ne2000->IMR)); + memset(&ne2000->DCR, 0, sizeof(ne2000->DCR)); + memset(&ne2000->TCR, 0, sizeof(ne2000->TCR)); + memset(&ne2000->TSR, 0, sizeof(ne2000->TSR)); + //memset( & ne2000->RCR, 0, sizeof(ne2000->RCR)); + memset(&ne2000->RSR, 0, sizeof(ne2000->RSR)); + ne2000->tx_timer_active = 0; + ne2000->local_dma = 0; + ne2000->page_start = 0; + ne2000->page_stop = 0; + ne2000->bound_ptr = 0; + ne2000->tx_page_start = 0; + ne2000->num_coll = 0; + ne2000->tx_bytes = 0; + ne2000->fifo = 0; + ne2000->remote_dma = 0; + ne2000->remote_start = 0; + ne2000->remote_bytes = 0; + ne2000->tallycnt_0 = 0; + ne2000->tallycnt_1 = 0; + ne2000->tallycnt_2 = 0; + + //memset( & ne2000->physaddr, 0, sizeof(ne2000->physaddr)); + //memset( & ne2000->mchash, 0, sizeof(ne2000->mchash)); + ne2000->curr_page = 0; + + ne2000->rempkt_ptr = 0; + ne2000->localpkt_ptr = 0; + ne2000->address_cnt = 0; + + memset(&ne2000->mem, 0, sizeof(ne2000->mem)); + + // Set power-up conditions + ne2000->CR.stop = 1; + ne2000->CR.rdma_cmd = 4; + ne2000->ISR.reset = 1; + ne2000->DCR.longaddr = 1; + picint(1 << ne2000->base_irq); + picintc(1 << ne2000->base_irq); + //DEV_pic_lower_irq(ne2000->base_irq); +} + +#include "bswap.h" + +// +// chipmem_read/chipmem_write - access the 64K private RAM. +// The ne2000 memory is accessed through the data port of +// the asic (offset 0) after setting up a remote-DMA transfer. +// Both byte and word accesses are allowed. +// The first 16 bytes contains the MAC address at even locations, +// and there is 16K of buffer memory starting at 16K +// +static inline uint8_t ne2000_chipmem_read_b(uint32_t address, ne2000_t *ne2000) +{ + // ROM'd MAC address + if ((address >= 0) && (address <= 31)) + return ne2000->macaddr[address]; + + if ((address >= BX_NE2K_MEMSTART) && (address < BX_NE2K_MEMEND)) + return ne2000->mem[address - BX_NE2K_MEMSTART]; + else + return 0xff; +} + + +static inline uint16_t ne2000_chipmem_read_w(uint32_t address, ne2000_t *ne2000) +{ + // ROM'd MAC address + if ((address >=0) && (address <= 31)) + return le16_to_cpu(*(uint16_t *)(ne2000->macaddr + address)); + + if ((address >= BX_NE2K_MEMSTART) && (address < BX_NE2K_MEMEND)) + return le16_to_cpu(*(uint16_t *)(ne2000->mem + (address - BX_NE2K_MEMSTART))); + else + return 0xffff; +} + +static inline void ne2000_chipmem_write_b(uint32_t address, uint8_t value, ne2000_t *ne2000) +{ + if ((address >= BX_NE2K_MEMSTART) && (address < BX_NE2K_MEMEND)) + ne2000->mem[address - BX_NE2K_MEMSTART] = value & 0xff; +} + + +static inline void ne2000_chipmem_write_w(uint32_t address, uint16_t value, ne2000_t *ne2000) +{ + if ((address >= BX_NE2K_MEMSTART) && (address < BX_NE2K_MEMEND)) + *(uint16_t *)(ne2000->mem + (address - BX_NE2K_MEMSTART)) = cpu_to_le16(value); +} + +// +// asic_read/asic_write - This is the high 16 bytes of i/o space +// (the lower 16 bytes is for the DS8390). Only two locations +// are used: offset 0, which is used for data transfer, and +// offset 0xf, which is used to reset the device. +// The data transfer port is used to as 'external' DMA to the +// DS8390. The chip has to have the DMA registers set up, and +// after that, insw/outsw instructions can be used to move +// the appropriate number of bytes to/from the device. +// +uint16_t ne2000_dma_read(int io_len, void *p) +{ + ne2000_t *ne2000 = (ne2000_t *)p; + + // + // The 8390 bumps the address and decreases the byte count + // by the selected word size after every access, not by + // the amount of data requested by the host (io_len). + // + ne2000->remote_dma += io_len; + if (ne2000->remote_dma == ne2000->page_stop << 8) + ne2000->remote_dma = ne2000->page_start << 8; + + // keep s.remote_bytes from underflowing + if (ne2000->remote_bytes > 1) + ne2000->remote_bytes -= io_len; + else + ne2000->remote_bytes = 0; + + // If all bytes have been written, signal remote-DMA complete + if (ne2000->remote_bytes == 0) + { + ne2000->ISR.rdma_done = 1; + if (ne2000->IMR.rdma_inte) + { + picint(1 << ne2000->base_irq); + picintc(1 << ne2000->base_irq); + } + } + + return 0; +} + +uint16_t ne2000_asic_read_w(uint16_t offset, void *p) +{ + ne2000_t *ne2000 = (ne2000_t *)p; + int retval; + + if (ne2000->DCR.wdsize & 0x01) + { + /* 16 bit access */ + retval = ne2000_chipmem_read_w(ne2000->remote_dma, ne2000); + ne2000_dma_read(2, ne2000); + } + else + { + /* 8 bit access */ + retval = ne2000_chipmem_read_b(ne2000->remote_dma, ne2000); + ne2000_dma_read(1, ne2000); + } + + pclog("asic read val=0x%04x\n", retval); + + return retval; +} + +void ne2000_dma_write(int io_len, void *p) +{ + ne2000_t *ne2000 = (ne2000_t *)p; + + // is this right ??? asic_read uses DCR.wordsize + ne2000->remote_dma += io_len; + if (ne2000->remote_dma == ne2000->page_stop << 8) + ne2000->remote_dma = ne2000->page_start << 8; + + ne2000->remote_bytes -= io_len; + if (ne2000->remote_bytes > BX_NE2K_MEMSIZ) + ne2000->remote_bytes = 0; + + // If all bytes have been written, signal remote-DMA complete + if (ne2000->remote_bytes == 0) + { + ne2000->ISR.rdma_done = 1; + if (ne2000->IMR.rdma_inte) + { + picint(1 << ne2000->base_irq); + picintc(1 << ne2000->base_irq); + } + } +} + +void ne2000_asic_write_w(uint16_t offset, uint16_t value, void *p) +{ + ne2000_t *ne2000 = (ne2000_t *)p; + + pclog("asic write val=0x%04x\n", value); + + if (ne2000->remote_bytes == 0) + return; + if (ne2000->DCR.wdsize & 0x01) { + /* 16 bit access */ + ne2000_chipmem_write_w(ne2000->remote_dma, value, ne2000); + ne2000_dma_write(2, ne2000); + } else { + /* 8 bit access */ + ne2000_chipmem_write_b(ne2000->remote_dma, value, ne2000); + ne2000_dma_write(1, ne2000); + } +} + +uint8_t ne2000_asic_read_b(uint16_t offset, void *p) +{ + if (offset & 1) + return ne2000_asic_read_w(offset & ~1, p) >> 1; + return ne2000_asic_read_w(offset, p) & 0xff; +} + +void ne2000_asic_write_b(uint16_t offset, uint8_t value, void *p) +{ + if (offset & 1) + ne2000_asic_write_w(offset & ~1, value << 8, p); + else + ne2000_asic_write_w(offset, value, p); +} + +uint8_t ne2000_reset_read(uint16_t offset, void *p) +{ + ne2000_t *ne2000 = (ne2000_t *)p; + ne2000_reset(BX_RESET_SOFTWARE, ne2000); + return 0; +} + +void ne2000_reset_write(uint16_t offset, uint8_t value, void *p) +{ +} + +// +// read_handler/read - i/o 'catcher' function called from BOCHS +// mainline when the CPU attempts a read in the i/o space registered +// by this ne2000 instance +// +uint8_t ne2000_read(uint16_t address, void *p) +{ + ne2000_t *ne2000 = (ne2000_t *)p; + + pclog("read addr %x\n", address); + int ret = 0; + + address &= 0xf; + + if (address == 0x00) + { + ret = (((ne2000->CR.pgsel & 0x03) << 6) | + ((ne2000->CR.rdma_cmd & 0x07) << 3) | + (ne2000->CR.tx_packet << 2) | + (ne2000->CR.start << 1) | + (ne2000->CR.stop)); + pclog("read CR returns 0x%08x\n", ret); + } + else + { + switch (ne2000->CR.pgsel) + { + case 0x00: + pclog("page 0 read from port %04x\n", address); + + switch (address) + { + case 0x1: // CLDA0 + return ne2000->local_dma & 0xff; + + case 0x2: // CLDA1 + return ne2000->local_dma >> 8; + + case 0x3: // BNRY + return ne2000->bound_ptr; + + case 0x4: // TSR + return ((ne2000->TSR.ow_coll << 7) | + (ne2000->TSR.cd_hbeat << 6) | + (ne2000->TSR.fifo_ur << 5) | + (ne2000->TSR.no_carrier << 4) | + (ne2000->TSR.aborted << 3) | + (ne2000->TSR.collided << 2) | + (ne2000->TSR.tx_ok)); + + case 0x5: // NCR + return ne2000->num_coll; + + case 0x6: // FIFO + // reading FIFO is only valid in loopback mode + pclog("reading FIFO not supported yet\n"); + return ne2000->fifo; + + case 0x7: // ISR + return ((ne2000->ISR.reset << 7) | + (ne2000->ISR.rdma_done << 6) | + (ne2000->ISR.cnt_oflow << 5) | + (ne2000->ISR.overwrite << 4) | + (ne2000->ISR.tx_err << 3) | + (ne2000->ISR.rx_err << 2) | + (ne2000->ISR.pkt_tx << 1) | + (ne2000->ISR.pkt_rx)); + + case 0x8: // CRDA0 + return ne2000->remote_dma & 0xff; + + case 0x9: // CRDA1 + return ne2000->remote_dma >> 8; + + case 0xa: // reserved + pclog("reserved read - page 0, 0xa\n"); + return 0xff; + + case 0xb: // reserved + pclog("reserved read - page 0, 0xb\n"); + return 0xff; + + case 0xc: // RSR + return ((ne2000->RSR.deferred << 7) | + (ne2000->RSR.rx_disabled << 6) | + (ne2000->RSR.rx_mbit << 5) | + (ne2000->RSR.rx_missed << 4) | + (ne2000->RSR.fifo_or << 3) | + (ne2000->RSR.bad_falign << 2) | + (ne2000->RSR.bad_crc << 1) | + (ne2000->RSR.rx_ok)); + + case 0xd: // CNTR0 + return ne2000->tallycnt_0; + + case 0xe: // CNTR1 + return ne2000->tallycnt_1; + + case 0xf: // CNTR2 + return ne2000->tallycnt_2; + } + + return 0; + + case 0x01: + pclog("page 1 read from port %04x\n", address); + + switch (address) + { + case 0x1: // PAR0-5 + case 0x2: + case 0x3: + case 0x4: + case 0x5: + case 0x6: + return ne2000->physaddr[address - 1]; + + case 0x7: // CURR + pclog("returning current page: %02x\n", (ne2000->curr_page)); + return ne2000->curr_page; + + case 0x8: // MAR0-7 + case 0x9: + case 0xa: + case 0xb: + case 0xc: + case 0xd: + case 0xe: + case 0xf: + return ne2000->mchash[address - 8]; + } + + return 0; + + case 0x02: + pclog("page 2 read from port %04x\n", address); + + switch (address) + { + case 0x1: // PSTART + return (ne2000->page_start); + + case 0x2: // PSTOP + return ne2000->page_stop; + + case 0x3: // Remote Next-packet pointer + return ne2000->rempkt_ptr; + + case 0x4: // TPSR + return ne2000->tx_page_start; + + case 0x5: // Local Next-packet pointer + return ne2000->localpkt_ptr; + + case 0x6: // Address counter (upper) + return ne2000->address_cnt >> 8; + + case 0x7: // Address counter (lower) + return ne2000->address_cnt & 0xff; + + case 0x8: // Reserved + case 0x9: + case 0xa: + case 0xb: + pclog("reserved read - page 2, 0x%02x\n", address); + break; + + case 0xc: // RCR + return ((ne2000->RCR.monitor << 5) | + (ne2000->RCR.promisc << 4) | + (ne2000->RCR.multicast << 3) | + (ne2000->RCR.broadcast << 2) | + (ne2000->RCR.runts_ok << 1) | + (ne2000->RCR.errors_ok)); + + case 0xd: // TCR + return ((ne2000->TCR.coll_prio << 4) | + (ne2000->TCR.ext_stoptx << 3) | + ((ne2000->TCR.loop_cntl & 0x3) << 1) | + (ne2000->TCR.crc_disable)); + + case 0xe: // DCR + return (((ne2000->DCR.fifo_size & 0x3) << 5) | + (ne2000->DCR.auto_rx << 4) | + (ne2000->DCR.loop << 3) | + (ne2000->DCR.longaddr << 2) | + (ne2000->DCR.endian << 1) | + (ne2000->DCR.wdsize)); + + case 0xf: // IMR + return ((ne2000->IMR.rdma_inte << 6) | + (ne2000->IMR.cofl_inte << 5) | + (ne2000->IMR.overw_inte << 4) | + (ne2000->IMR.txerr_inte << 3) | + (ne2000->IMR.rxerr_inte << 2) | + (ne2000->IMR.tx_inte << 1) | + (ne2000->IMR.rx_inte)); + } + break; + + default: + fatal("ne2000 unknown value of pgsel in read - %d\n", ne2000->CR.pgsel); + } + } + + return ret; +} + +// +// write_handler/write - i/o 'catcher' function called from BOCHS +// mainline when the CPU attempts a write in the i/o space registered +// by this ne2000 instance +// +void ne2000_write(uint16_t address, uint8_t value, void *p) +{ + ne2000_t *ne2000 = (ne2000_t *)p; + + pclog("write address %x, val=%x\n", address, value); + + address &= 0xf; + + // + // The high 16 bytes of i/o space are for the ne2000 asic - + // the low 16 bytes are for the DS8390, with the current + // page being selected by the PS0,PS1 registers in the + // command register + // + if (address == 0x00) + { + pclog("wrote 0x%02x to CR\n", value); + + // Validate remote-DMA + if ((value & 0x38) == 0x00) + { + pclog("CR write - invalid rDMA value 0\n"); + value |= 0x20; /* dma_cmd == 4 is a safe default */ + //value = 0x22; /* dma_cmd == 4 is a safe default */ + } + + // Check for s/w reset + if (value & 0x01) + { + ne2000->ISR.reset = 1; + ne2000->CR.stop = 1; + } + else + ne2000->CR.stop = 0; + + ne2000->CR.rdma_cmd = (value & 0x38) >> 3; + + // If start command issued, the RST bit in the ISR + // must be cleared + if ((value & 0x02) && !ne2000->CR.start) + ne2000->ISR.reset = 0; + + ne2000->CR.start = ((value & 0x02) == 0x02); + ne2000->CR.pgsel = (value & 0xc0) >> 6; + + // Check for send-packet command + if (ne2000->CR.rdma_cmd == 3) + { + // Set up DMA read from receive ring + ne2000->remote_start = ne2000->remote_dma = + ne2000->bound_ptr * 256; + ne2000->remote_bytes = *((uint16_t*) & + ne2000->mem[ne2000->bound_ptr * 256 + 2 - BX_NE2K_MEMSTART]); + pclog("Sending buffer #x%x length %d\n", + ne2000->remote_start, + ne2000->remote_bytes); + } + + // Check for start-tx + if ((value & 0x04) && ne2000->TCR.loop_cntl) + { + // loopback mode + if (ne2000->TCR.loop_cntl != 1) + { + pclog("Loop mode %d not supported.\n", ne2000->TCR.loop_cntl); + } + else + { + ne2000_rx_frame (ne2000, &ne2000->mem[ne2000->tx_page_start*256 - + BX_NE2K_MEMSTART], + ne2000->tx_bytes); + + // do a TX interrupt + // Generate an interrupt if not masked and not one in progress + if (ne2000->IMR.tx_inte && !ne2000->ISR.pkt_tx) + { + //LOG_MSG("tx complete interrupt"); + picint(1 << ne2000->base_irq); + } + ne2000->ISR.pkt_tx = 1; + } + } + else if (value & 0x04) + { + // start-tx and no loopback + if (ne2000->CR.stop || !ne2000->CR.start) + pclog("CR write - tx start, dev in reset\n"); + + if (ne2000->tx_bytes == 0) + pclog("CR write - tx start, tx bytes == 0\n"); + + // Send the packet to the system driver + /* TODO: Transmit packet */ + //BX_NE2K_THIS ethdev->sendpkt(& ne2000->mem[ne2000->tx_page_start*256 - BX_NE2K_MEMSTART], ne2000->tx_bytes); + //pcap_sendpacket(adhandle,&ne2000->mem[ne2000->tx_page_start*256 - BX_NE2K_MEMSTART], ne2000->tx_bytes); + if (net_is_slirp) + { + slirp_input(&ne2000->mem[ne2000->tx_page_start*256 - BX_NE2K_MEMSTART], ne2000->tx_bytes); + pclog("ne2000 slirp sending packet\n"); + } + if(net_is_pcap && net_pcap!=NULL) + { + _pcap_sendpacket(net_pcap, &ne2000->mem[ne2000->tx_page_start*256 - BX_NE2K_MEMSTART], ne2000->tx_bytes); + pclog("ne2000 pcap sending packet\n"); + } + + ne2000_tx_event(value, ne2000); + // Schedule a timer to trigger a tx-complete interrupt + // The number of microseconds is the bit-time / 10. + // The bit-time is the preamble+sfd (64 bits), the + // inter-frame gap (96 bits), the CRC (4 bytes), and the + // the number of bits in the frame (s.tx_bytes * 8). + // + + /* TODO: Code transmit timer */ + /* + bx_pc_system.activate_timer(ne2000->tx_timer_index, + (64 + 96 + 4*8 + ne2000->tx_bytes*8)/10, + 0); // not continuous + */ + } // end transmit-start branch + + // Linux probes for an interrupt by setting up a remote-DMA read + // of 0 bytes with remote-DMA completion interrupts enabled. + // Detect this here + if (ne2000->CR.rdma_cmd == 0x01 && + ne2000->CR.start && + ne2000->remote_bytes == 0) + { + ne2000->ISR.rdma_done = 1; + if (ne2000->IMR.rdma_inte) + { + picint(1 << ne2000->base_irq); + picintc(1 << ne2000->base_irq); + //DEV_pic_raise_irq(ne2000->base_irq); + } + } + } + else + { + switch (ne2000->CR.pgsel) + { + case 0x00: + pclog("page 0 write to port %04x\n", address); + + // It appears to be a common practice to use outw on page0 regs... + + switch (address) + { + case 0x1: // PSTART + ne2000->page_start = value; + break; + + case 0x2: // PSTOP + // BX_INFO(("Writing to PSTOP: %02x", value)); + ne2000->page_stop = value; + break; + + case 0x3: // BNRY + ne2000->bound_ptr = value; + break; + + case 0x4: // TPSR + ne2000->tx_page_start = value; + break; + + case 0x5: // TBCR0 + // Clear out low byte and re-insert + ne2000->tx_bytes &= 0xff00; + ne2000->tx_bytes |= (value & 0xff); + break; + + case 0x6: // TBCR1 + // Clear out high byte and re-insert + ne2000->tx_bytes &= 0x00ff; + ne2000->tx_bytes |= ((value & 0xff) << 8); + break; + + case 0x7: // ISR + value &= 0x7f; // clear RST bit - status-only bit + // All other values are cleared iff the ISR bit is 1 + ne2000->ISR.pkt_rx &= ~((int)((value & 0x01) == 0x01)); + ne2000->ISR.pkt_tx &= ~((int)((value & 0x02) == 0x02)); + ne2000->ISR.rx_err &= ~((int)((value & 0x04) == 0x04)); + ne2000->ISR.tx_err &= ~((int)((value & 0x08) == 0x08)); + ne2000->ISR.overwrite &= ~((int)((value & 0x10) == 0x10)); + ne2000->ISR.cnt_oflow &= ~((int)((value & 0x20) == 0x20)); + ne2000->ISR.rdma_done &= ~((int)((value & 0x40) == 0x40)); + value = ((ne2000->ISR.rdma_done << 6) | + (ne2000->ISR.cnt_oflow << 5) | + (ne2000->ISR.overwrite << 4) | + (ne2000->ISR.tx_err << 3) | + (ne2000->ISR.rx_err << 2) | + (ne2000->ISR.pkt_tx << 1) | + (ne2000->ISR.pkt_rx)); + value &= ((ne2000->IMR.rdma_inte << 6) | + (ne2000->IMR.cofl_inte << 5) | + (ne2000->IMR.overw_inte << 4) | + (ne2000->IMR.txerr_inte << 3) | + (ne2000->IMR.rxerr_inte << 2) | + (ne2000->IMR.tx_inte << 1) | + (ne2000->IMR.rx_inte)); + if (value == 0) + picintc(1 << ne2000->base_irq); + //DEV_pic_lower_irq(ne2000->base_irq); + break; + + case 0x8: // RSAR0 + // Clear out low byte and re-insert + ne2000->remote_start &= 0xff00; + ne2000->remote_start |= (value & 0xff); + ne2000->remote_dma = ne2000->remote_start; + break; + + case 0x9: // RSAR1 + // Clear out high byte and re-insert + ne2000->remote_start &= 0x00ff; + ne2000->remote_start |= ((value & 0xff) << 8); + ne2000->remote_dma = ne2000->remote_start; + break; + + case 0xa: // RBCR0 + // Clear out low byte and re-insert + ne2000->remote_bytes &= 0xff00; + ne2000->remote_bytes |= (value & 0xff); + break; + + case 0xb: // RBCR1 + // Clear out high byte and re-insert + ne2000->remote_bytes &= 0x00ff; + ne2000->remote_bytes |= ((value & 0xff) << 8); + break; + + case 0xc: // RCR + // Check if the reserved bits are set + if (value & 0xc0) + pclog("RCR write, reserved bits set\n"); + + // Set all other bit-fields + ne2000->RCR.errors_ok = ((value & 0x01) == 0x01); + ne2000->RCR.runts_ok = ((value & 0x02) == 0x02); + ne2000->RCR.broadcast = ((value & 0x04) == 0x04); + ne2000->RCR.multicast = ((value & 0x08) == 0x08); + ne2000->RCR.promisc = ((value & 0x10) == 0x10); + ne2000->RCR.monitor = ((value & 0x20) == 0x20); + + // Monitor bit is a little suspicious... + if (value & 0x20) + pclog("RCR write, monitor bit set!\n"); + break; + + case 0xd: // TCR + // Check reserved bits + if (value & 0xe0) + pclog("TCR write, reserved bits set\n"); + + // Test loop mode (not supported) + if (value & 0x06) + { + ne2000->TCR.loop_cntl = (value & 0x6) >> 1; + pclog("TCR write, loop mode %d not supported\n", ne2000->TCR.loop_cntl); + } + else + ne2000->TCR.loop_cntl = 0; + + // Inhibit-CRC not supported. + if (value & 0x01) + { + //fatal("ne2000 TCR write, inhibit-CRC not supported\n"); + pclog("ne2000 TCR write, inhibit-CRC not supported\n"); + return; + } + + // Auto-transmit disable very suspicious + if (value & 0x08) + { + //fatal("ne2000 TCR write, auto transmit disable not supported\n"); + pclog("ne2000 TCR write, auto transmit disable not supported\n"); + } + + // Allow collision-offset to be set, although not used + ne2000->TCR.coll_prio = ((value & 0x08) == 0x08); + break; + + case 0xe: // DCR + // the loopback mode is not suppported yet + if (!(value & 0x08)) + pclog("DCR write, loopback mode selected\n"); + + // It is questionable to set longaddr and auto_rx, since they + // aren't supported on the ne2000. Print a warning and continue + if (value & 0x04) + pclog("DCR write - LAS set ???\n"); + if (value & 0x10) + pclog("DCR write - AR set ???\n"); + + // Set other values. + ne2000->DCR.wdsize = ((value & 0x01) == 0x01); + ne2000->DCR.endian = ((value & 0x02) == 0x02); + ne2000->DCR.longaddr = ((value & 0x04) == 0x04); // illegal ? + ne2000->DCR.loop = ((value & 0x08) == 0x08); + ne2000->DCR.auto_rx = ((value & 0x10) == 0x10); // also illegal ? + ne2000->DCR.fifo_size = (value & 0x50) >> 5; + break; + + case 0xf: // IMR + // Check for reserved bit + if (value & 0x80) + pclog("IMR write, reserved bit set\n"); + + // Set other values + ne2000->IMR.rx_inte = ((value & 0x01) == 0x01); + ne2000->IMR.tx_inte = ((value & 0x02) == 0x02); + ne2000->IMR.rxerr_inte = ((value & 0x04) == 0x04); + ne2000->IMR.txerr_inte = ((value & 0x08) == 0x08); + ne2000->IMR.overw_inte = ((value & 0x10) == 0x10); + ne2000->IMR.cofl_inte = ((value & 0x20) == 0x20); + ne2000->IMR.rdma_inte = ((value & 0x40) == 0x40); + if (ne2000->ISR.pkt_tx && ne2000->IMR.tx_inte) + { + pclog("tx irq retrigger\n"); + picint(1 << ne2000->base_irq); + picintc(1 << ne2000->base_irq); + } + break; + } + break; + + case 0x01: + pclog("page 1 w offset %04x\n", address); + switch (address) + { + case 0x1: // PAR0-5 + case 0x2: + case 0x3: + case 0x4: + case 0x5: + case 0x6: + ne2000->physaddr[address - 1] = value; + break; + + case 0x7: // CURR + ne2000->curr_page = value; + break; + + case 0x8: // MAR0-7 + case 0x9: + case 0xa: + case 0xb: + case 0xc: + case 0xd: + case 0xe: + case 0xf: + ne2000->mchash[address - 8] = value; + break; + } + break; + + case 0x02: + if (address != 0) + pclog("page 2 write ?\n"); + + switch (address) + { + case 0x1: // CLDA0 + // Clear out low byte and re-insert + ne2000->local_dma &= 0xff00; + ne2000->local_dma |= (value & 0xff); + break; + + case 0x2: // CLDA1 + // Clear out high byte and re-insert + ne2000->local_dma &= 0x00ff; + ne2000->local_dma |= ((value & 0xff) << 8); + break; + + case 0x3: // Remote Next-pkt pointer + ne2000->rempkt_ptr = value; + break; + + case 0x4: + //fatal("page 2 write to reserved offset 4\n"); + //OS/2 Warp can cause this to freak out. + pclog("ne2000 page 2 write to reserved offset 4\n"); + break; + + case 0x5: // Local Next-packet pointer + ne2000->localpkt_ptr = value; + break; + + case 0x6: // Address counter (upper) + // Clear out high byte and re-insert + ne2000->address_cnt &= 0x00ff; + ne2000->address_cnt |= ((value & 0xff) << 8); + break; + + case 0x7: // Address counter (lower) + // Clear out low byte and re-insert + ne2000->address_cnt &= 0xff00; + ne2000->address_cnt |= (value & 0xff); + break; + + case 0x8: + case 0x9: + case 0xa: + case 0xb: + case 0xc: + case 0xd: + case 0xe: + case 0xf: + //fatal("page 2 write to reserved offset %0x\n", address); + pclog("ne2000 page 2 write to reserved offset %0x\n", address); + default: + break; + } + break; + + default: + //fatal("ne2K: unknown value of pgsel in write - %d\n", ne2000->CR.pgsel); + pclog("ne2000 unknown value of pgsel in write - %d\n", ne2000->CR.pgsel); + break; + } + } +} + +/* + * mcast_index() - return the 6-bit index into the multicast + * table. Stolen unashamedly from FreeBSD's if_ed.c + */ +static int mcast_index(const void *dst) +{ +#define POLYNOMIAL 0x04c11db6 + unsigned long crc = 0xffffffffL; + int carry, i, j; + unsigned char b; + unsigned char *ep = (unsigned char *) dst; + + for (i = 6; --i >= 0;) + { + b = *ep++; + for (j = 8; --j >= 0;) + { + carry = ((crc & 0x80000000L) ? 1 : 0) ^ (b & 0x01); + crc <<= 1; + b >>= 1; + if (carry) + crc = ((crc ^ POLYNOMIAL) | carry); + } + } + return crc >> 26; +#undef POLYNOMIAL +} + +/* + * rx_frame() - called by the platform-specific code when an + * ethernet frame has been received. The destination address + * is tested to see if it should be accepted, and if the + * rx ring has enough room, it is copied into it and + * the receive process is updated + */ +void ne2000_rx_frame(void *p, const void *buf, int io_len) +{ + ne2000_t *ne2000 = (ne2000_t *)p; + + int pages; + int avail; + int idx; + int nextpage; + uint8_t pkthdr[4]; + uint8_t *pktbuf = (uint8_t *) buf; + uint8_t *startptr; + static uint8_t bcast_addr[6] = {0xff,0xff,0xff,0xff,0xff,0xff}; + + if(io_len != 60) + pclog("rx_frame with length %d\n", io_len); + + //LOG_MSG("stop=%d, pagestart=%x, dcr_loop=%x, tcr_loopcntl=%x", + // ne2000->CR.stop, ne2000->page_start, + // ne2000->DCR.loop, ne2000->TCR.loop_cntl); + if ((ne2000->CR.stop != 0) || + (ne2000->page_start == 0) /*|| + ((ne2000->DCR.loop == 0) && + (ne2000->TCR.loop_cntl != 0))*/) + return; + + // Add the pkt header + CRC to the length, and work + // out how many 256-byte pages the frame would occupy + pages = (io_len + 4 + 4 + 255)/256; + + if (ne2000->curr_page < ne2000->bound_ptr) + avail = ne2000->bound_ptr - ne2000->curr_page; + else + avail = (ne2000->page_stop - ne2000->page_start) - + (ne2000->curr_page - ne2000->bound_ptr); + + // Avoid getting into a buffer overflow condition by not attempting + // to do partial receives. The emulation to handle this condition + // seems particularly painful. + if ((avail < pages) +#if BX_NE2K_NEVER_FULL_RING + || (avail == pages) +#endif + ) + { + pclog("no space\n"); + return; + } + + if ((io_len < 40/*60*/) && !ne2000->RCR.runts_ok) + { + pclog("rejected small packet, length %d\n", io_len); + return; + } + // some computers don't care... + if (io_len < 60) + io_len = 60; + + // Do address filtering if not in promiscuous mode + if (!ne2000->RCR.promisc) + { + if (!memcmp(buf, bcast_addr, 6)) + { + if (!ne2000->RCR.broadcast) + return; + } + else if (pktbuf[0] & 0x01) + { + if (!ne2000->RCR.multicast) + return; + idx = mcast_index(buf); + if (!(ne2000->mchash[idx >> 3] & (1 << (idx & 0x7)))) + return; + } + else if (0 != memcmp(buf, ne2000->physaddr, 6)) + return; + } + else + pclog("rx_frame promiscuous receive\n"); + + pclog("rx_frame %d to %x:%x:%x:%x:%x:%x from %x:%x:%x:%x:%x:%x\n", + io_len, + pktbuf[0], pktbuf[1], pktbuf[2], pktbuf[3], pktbuf[4], pktbuf[5], + pktbuf[6], pktbuf[7], pktbuf[8], pktbuf[9], pktbuf[10], pktbuf[11]); + + nextpage = ne2000->curr_page + pages; + if (nextpage >= ne2000->page_stop) + nextpage -= ne2000->page_stop - ne2000->page_start; + + // Setup packet header + pkthdr[0] = 0; // rx status - old behavior + pkthdr[0] = 1; // Probably better to set it all the time + // rather than set it to 0, which is clearly wrong. + if (pktbuf[0] & 0x01) + pkthdr[0] |= 0x20; // rx status += multicast packet + + pkthdr[1] = nextpage; // ptr to next packet + pkthdr[2] = (io_len + 4) & 0xff; // length-low + pkthdr[3] = (io_len + 4) >> 8; // length-hi + + // copy into buffer, update curpage, and signal interrupt if config'd + startptr = & ne2000->mem[ne2000->curr_page * 256 - + BX_NE2K_MEMSTART]; + if ((nextpage > ne2000->curr_page) || + ((ne2000->curr_page + pages) == ne2000->page_stop)) + { + memcpy(startptr, pkthdr, 4); + memcpy(startptr + 4, buf, io_len); + ne2000->curr_page = nextpage; + } + else + { + int endbytes = (ne2000->page_stop - ne2000->curr_page) + * 256; + memcpy(startptr, pkthdr, 4); + memcpy(startptr + 4, buf, endbytes - 4); + startptr = & ne2000->mem[ne2000->page_start * 256 - + BX_NE2K_MEMSTART]; + memcpy(startptr, (void *)(pktbuf + endbytes - 4), + io_len - endbytes + 8); + ne2000->curr_page = nextpage; + } + + ne2000->RSR.rx_ok = 1; + if (pktbuf[0] & 0x80) + ne2000->RSR.rx_mbit = 1; + + ne2000->ISR.pkt_rx = 1; + + if (ne2000->IMR.rx_inte) + { + //LOG_MSG("packet rx interrupt"); + picint(1 << ne2000->base_irq); + //DEV_pic_raise_irq(ne2000->base_irq); + } + //else LOG_MSG("no packet rx interrupt"); +} + +void ne2000_tx_timer(void *p) +{ + ne2000_t *ne2000 = (ne2000_t *)p; + + pclog("tx_timer\n"); + ne2000->TSR.tx_ok = 1; + // Generate an interrupt if not masked and not one in progress + if (ne2000->IMR.tx_inte && !ne2000->ISR.pkt_tx) + { + //LOG_MSG("tx complete interrupt"); + picint(1 << ne2000->base_irq); + //DEV_pic_raise_irq(ne2000->base_irq); + } + //else LOG_MSG("no tx complete interrupt"); + ne2000->ISR.pkt_tx = 1; + ne2000->tx_timer_active = 0; +} + +static void ne2000_tx_event(int val, void *p) +{ + ne2000_t *ne2000 = (ne2000_t *)p; + ne2000_tx_timer(ne2000); +} + +static void ne2000_poller(void *p) +{ + ne2000_t *ne2000 = (ne2000_t *)p; + struct queuepacket *qp; + const unsigned char *data; + struct pcap_pkthdr h; + + if (net_is_slirp) + { + while (QueuePeek(slirpq) > 0) + { + qp = QueueDelete(slirpq); + if ((ne2000->DCR.loop == 0) || (ne2000->TCR.loop_cntl != 0)) + { + free(qp); + return; + } + ne2000_rx_frame(ne2000,&qp->data,qp->len); + pclog("ne2000 inQ:%d got a %dbyte packet @%d\n",QueuePeek(slirpq),qp->len,qp); + free(qp); + } + fizz++; + if(fizz > 1200) + { + fizz=0; + slirp_tic(); + } + }//end slirp + if (net_is_pcap && net_pcap!=NULL) + { + data = _pcap_next(net_pcap,&h); + if (data == 0x0) + return; + if ((memcmp(data+6,maclocal,6)) == 0) + pclog("ne2000 we just saw ourselves\n"); + else + { + if((ne2000->DCR.loop == 0) || (ne2000->TCR.loop_cntl != 0)) + return; + + pclog("ne2000 pcap received a frame %d bytes\n",h.caplen); + ne2000_rx_frame(ne2000,data,h.caplen); + } + } +} + + +void *ne2000_init() +{ + struct in_addr myaddr; + int rc; + + ne2000_t *ne2000 = malloc(sizeof(ne2000_t)); + memset(ne2000, 0, sizeof(ne2000_t)); + + uint16_t addr = device_get_config_int("addr"); + ne2000_setirq(ne2000, device_get_config_int("irq")); + + //net_type + //0 pcap + //1 slirp + // + net_is_slirp = config_get_int(CFG_MACHINE, NULL, "net_type", 1); + pclog("ne2000 pcap device %s\n",config_get_string(CFG_MACHINE, NULL,"pcap_device","nothing")); + + //Check that we have a string setup, otherwise turn pcap off + if (!strcmp("nothing",config_get_string(CFG_MACHINE, NULL,"pcap_device","nothing"))) + net_is_pcap = 0; + else if (net_is_slirp == 0) + net_is_pcap = 1; + + io_sethandler(addr, 0x0010, ne2000_read, NULL, NULL, ne2000_write, NULL, NULL, ne2000); + io_sethandler(addr+0x10, 0x0010, ne2000_asic_read_b, ne2000_asic_read_w, NULL, ne2000_asic_write_b, ne2000_asic_write_w, NULL, ne2000); + io_sethandler(addr+0x1f, 0x0001, ne2000_reset_read, NULL, NULL, ne2000_reset_write, NULL, NULL, ne2000); + memcpy(ne2000->physaddr, maclocal, 6); + + ne2000_reset(BX_RESET_HARDWARE, ne2000); + vlan_handler(ne2000_poller, ne2000); + + pclog("ne2000 init 0x%X %d\tslirp is %d net_is_pcap is %d\n",addr,device_get_config_int("irq"),net_is_slirp,net_is_pcap); + + //need a switch statment for more network types. + + if (net_is_slirp) + { + pclog("ne2000 initalizing SLiRP\n"); + net_is_pcap = 0; + rc = slirp_init(); + pclog("ne2000 slirp_init returned: %d\n",rc); + if (rc == 0) + { + pclog("ne2000 slirp initalized!\n"); + inet_aton("10.0.2.15",&myaddr); + //YES THIS NEEDS TO PULL FROM A CONFIG FILE... but for now. + rc=slirp_redir(0,42323,myaddr,23); + pclog("ne2000 slirp redir returned %d on port 42323 -> 23\n",rc); + rc=slirp_redir(0,42380,myaddr,80); + pclog("ne2000 slirp redir returned %d on port 42380 -> 80\n",rc); + rc=slirp_redir(0,42443,myaddr,443); + pclog("ne2000 slirp redir returned %d on port 42443 -> 443\n",rc); + rc=slirp_redir(0,42322,myaddr,22); + pclog("ne2000 slirp redir returned %d on port 42322 -> 22\n",rc); + + //Kali + rc=slirp_redir(1,2213,myaddr,2213); + pclog("ne2000 slirp redir returned %d on port 2213 -> 2213\n",rc); + rc=slirp_redir(1,2231,myaddr,2231); + pclog("ne2000 slirp redir returned %d on port 2231 -> 2231\n",rc); + rc=slirp_redir(1,2271,myaddr,2271); + pclog("ne2000 slirp redir returned %d on port 2271 -> 2271\n",rc); + + net_slirp_inited=1; + slirpq = QueueCreate(); + net_is_slirp=1; + fizz=0; + pclog("ne2000 slirpq is %x\n",&slirpq); + } + else + { + net_slirp_inited=0; + net_is_slirp=0; + } + } + if (net_is_pcap) + { //pcap + char errbuf[32768]; + + pclog("ne2000 initalizing libpcap\n"); + net_is_slirp=0; + net_hLib = LoadLibraryA(net_lib_name); + if(net_hLib==0) + { + pclog("ne2000 Failed to load %s\n",net_lib_name); + net_is_pcap=0; + } + _pcap_lib_version =(PCAP_LIB_VERSION)GetProcAddress(net_hLib,"pcap_lib_version"); + _pcap_open_live=(PCAP_OPEN_LIVE)GetProcAddress(net_hLib,"pcap_open_live"); + _pcap_sendpacket=(PCAP_SENDPACKET)GetProcAddress(net_hLib,"pcap_sendpacket"); + _pcap_setnonblock=(PCAP_SETNONBLOCK)GetProcAddress(net_hLib,"pcap_setnonblock"); + _pcap_next=(PCAP_NEXT)GetProcAddress(net_hLib,"pcap_next"); + _pcap_close=(PCAP_CLOSE)GetProcAddress(net_hLib,"pcap_close"); + _pcap_getnonblock=(PCAP_GETNONBLOCK)GetProcAddress(net_hLib,"pcap_getnonblock"); + _pcap_compile=(PCAP_COMPILE)GetProcAddress(net_hLib,"pcap_compile"); + _pcap_setfilter=(PCAP_SETFILTER)GetProcAddress(net_hLib,"pcap_setfilter"); + + if (_pcap_lib_version && _pcap_open_live && _pcap_sendpacket && _pcap_setnonblock && _pcap_next && _pcap_close && _pcap_getnonblock) + { + pclog("ne2000 Pcap version [%s]\n",_pcap_lib_version()); + + if ((net_pcap=_pcap_open_live(config_get_string(CFG_MACHINE, NULL,"pcap_device","nothing"),1518,1,15,errbuf))==0) + { + pclog("ne2000 pcap_open_live error on %s!\n",config_get_string(CFG_MACHINE, NULL,"pcap_device","whatever the ethernet is")); + net_is_pcap=0; return(ne2000); //YUCK!!! + } + } + else + { + pclog("%d %d %d %d %d %d %d\n",_pcap_lib_version, _pcap_open_live,_pcap_sendpacket,_pcap_setnonblock,_pcap_next,_pcap_close,_pcap_getnonblock); + net_is_pcap=1; + } + + //Time to check that we are in non-blocking mode. + rc=_pcap_getnonblock(net_pcap,errbuf); + pclog("ne2000 pcap is currently in %s mode\n",rc? "non-blocking":"blocking"); + switch(rc) + { + case 0: + pclog("ne2000 Setting interface to non-blocking mode.."); + rc=_pcap_setnonblock(net_pcap,1,errbuf); + if (rc==0) + { + pclog(".."); + rc=_pcap_getnonblock(net_pcap,errbuf); + if (rc==1) + { + pclog("..!",rc); + net_is_pcap=1; + } + else + { + pclog("\tunable to set pcap into non-blocking mode!\nContinuining without pcap.\n"); + net_is_pcap=0; + } + } + else + { + pclog("There was an unexpected error of [%s]\n\nexiting.\n",errbuf); + net_is_pcap=0; + } + pclog("\n"); + break; + case 1: + pclog("non blocking\n"); + break; + default: + pclog("this isn't right!!!\n"); + net_is_pcap=0; + break; + } + if( net_is_pcap ) + { + if(_pcap_compile && _pcap_setfilter) + { + struct bpf_program fp; + char filter_exp[255]; + + pclog("ne2000 Building packet filter..."); + sprintf(filter_exp,"( ((ether dst ff:ff:ff:ff:ff:ff) or (ether dst %02x:%02x:%02x:%02x:%02x:%02x)) and not (ether src %02x:%02x:%02x:%02x:%02x:%02x) )", + maclocal[0], maclocal[1], maclocal[2], maclocal[3], maclocal[4], maclocal[5], + maclocal[0], maclocal[1], maclocal[2], maclocal[3], maclocal[4], maclocal[5]); + + //I'm doing a MAC level filter so TCP/IP doesn't matter. + if (_pcap_compile(net_pcap, &fp, filter_exp, 0, 0xffffffff) == -1) + { + pclog("\nne2000 Couldn't compile filter\n"); + } + else + { + pclog("..."); + if (_pcap_setfilter(net_pcap, &fp) == -1) + { + pclog("\nError installing pcap filter.\n"); + } + else + { + pclog("...!\n"); + } + } + pclog("ne2000 Using filter\t[%s]\n",filter_exp); + } + else + { + pclog("ne2000 Your platform lacks pcap_compile & pcap_setfilter\n"); + net_is_pcap=0; + } + pclog("ne2000 net_is_pcap is %d and net_pcap is %x\n",net_is_pcap,net_pcap); + } + } //end pcap setup + + pclog("ne2000 is_slirp %d is_pcap %d\n",net_is_slirp,net_is_pcap); + + return ne2000; +} + +void ne2000_close(void *p) +{ + ne2000_t *ne2000 = (ne2000_t *)p; + free(ne2000); + if(net_is_slirp) + { + QueueDestroy(slirpq); + slirp_exit(0); + net_slirp_inited=0; + pclog("ne2000 exiting slirp\n"); + } + if(net_is_pcap && net_pcap!=NULL) + { + _pcap_close(net_pcap); + FreeLibrary(net_hLib); + pclog("ne2000 closing pcap\n"); + } + pclog("ne2000 close\n"); +} + +static device_config_t ne2000_config[] = +{ + { + .name = "addr", + .description = "Address", + .type = CONFIG_BINARY, + .type = CONFIG_SELECTION, + .selection = + { + { + .description = "0x280", + .value = 0x280 + }, + { + .description = "0x300", + .value = 0x300 + }, + { + .description = "0x320", + .value = 0x320 + }, + { + .description = "0x340", + .value = 0x340 + }, + { + .description = "0x360", + .value = 0x360 + }, + { + .description = "0x380", + .value = 0x380 + }, + { + .description = "" + } + }, + .default_int = 0x300 + }, + { + .name = "irq", + .description = "IRQ", + .type = CONFIG_SELECTION, + .selection = + { + { + .description = "IRQ 3", + .value = 3 + }, + { + .description = "IRQ 5", + .value = 5 + }, + { + .description = "IRQ 7", + .value = 7 + }, + { + .description = "IRQ 10", + .value = 10 + }, + { + .description = "IRQ 11", + .value = 11 + }, + { + .description = "" + } + }, + .default_int = 10 + }, + { + .type = -1 + } +}; + +device_t ne2000_device = +{ + "Novell NE2000", + 0, + ne2000_init, + ne2000_close, + NULL, + NULL, + NULL, + NULL, + ne2000_config +}; + + +//SLIRP stuff +int slirp_can_output(void) +{ + return net_slirp_inited; +} + +void slirp_output (const unsigned char *pkt, int pkt_len) +{ + struct queuepacket *p; + p=(struct queuepacket *)malloc(sizeof(struct queuepacket)); + p->len=pkt_len; + memcpy(p->data,pkt,pkt_len); + QueueEnter(slirpq,p); + pclog("ne2000 slirp_output %d @%d\n",pkt_len,p); +} + +// Instead of calling this and crashing some times +// or experencing jitter, this is called by the +// 60Hz clock which seems to do the job. +void slirp_tic() +{ + int ret2,nfds; + struct timeval tv; + fd_set rfds, wfds, xfds; + int timeout; + nfds=-1; + + if(net_slirp_inited) + { + FD_ZERO(&rfds); + FD_ZERO(&wfds); + FD_ZERO(&xfds); + timeout=slirp_select_fill(&nfds,&rfds,&wfds,&xfds); //this can crash + + if(timeout<0) + timeout=500; + tv.tv_sec=0; + tv.tv_usec = timeout; //basilisk default 10000 + + ret2 = select(nfds + 1, &rfds, &wfds, &xfds, &tv); + if (ret2>=0) + { + slirp_select_poll(&rfds, &wfds, &xfds); + } + //pclog("ne2000 slirp_tic()\n"); + }//end if slirp inited +} diff --git a/src/ne2000.h b/src/ne2000.h new file mode 100644 index 0000000..a3f1fea --- /dev/null +++ b/src/ne2000.h @@ -0,0 +1 @@ +extern device_t ne2000_device; diff --git a/src/nethandler.c b/src/nethandler.c new file mode 100644 index 0000000..221a69a --- /dev/null +++ b/src/nethandler.c @@ -0,0 +1,117 @@ +#include +#include +#include +#include +#include +#include +#include "nethandler.h" + +#include "ibm.h" +#include "device.h" + +#include "ne2000.h" +#include "timer.h" + +int network_card_current = 0; +static int network_card_last = 0; + +typedef struct +{ + char name[32]; + char internal_name[24]; + device_t *device; +} NETWORK_CARD; + +static NETWORK_CARD network_cards[] = +{ + {"None", "", NULL}, + {"Novell NE2000", "ne2000", &ne2000_device}, + {"", "", NULL} +}; + +int network_card_available(int card) +{ + if (network_cards[card].device) + return device_available(network_cards[card].device); + + return 1; +} + +char *network_card_getname(int card) +{ + return network_cards[card].name; +} + +device_t *network_card_getdevice(int card) +{ + return network_cards[card].device; +} + +int network_card_has_config(int card) +{ + if (!network_cards[card].device) + return 0; + return network_cards[card].device->config ? 1 : 0; +} + +char *network_card_get_internal_name(int card) +{ + return network_cards[card].internal_name; +} + +int network_card_get_from_internal_name(char *s) +{ + int c = 0; + + while (strlen(network_cards[c].name)) + { + if (!strcmp(network_cards[c].internal_name, s)) + return c; + c++; + } + + return 0; +} + +void network_card_init() +{ + if (network_cards[network_card_current].device) + device_add(network_cards[network_card_current].device); + network_card_last = network_card_current; +} + +static struct +{ + void (*poller)(void *p); + void *priv; +} vlan_handlers[8]; + +static int vlan_handlers_num; + +static int vlan_poller_time = 0; + +void vlan_handler(void (*poller)(void *p), void *p) +//void vlan_handler(int (*can_receive)(void *p), void (*receive)(void *p, const uint8_t *buf, int size), void *p) +{ + /* vlan_handlers[vlan_handlers_num].can_receive = can_receive; */ + vlan_handlers[vlan_handlers_num].poller = poller; + vlan_handlers[vlan_handlers_num].priv = p; + vlan_handlers_num++; +} + +void vlan_poller(void *priv) +{ + int c; + + vlan_poller_time += (int)((double)TIMER_USEC * (1000000.0 / 8.0 / 1500.0)); + + for (c = 0; c < vlan_handlers_num; c++) + vlan_handlers[c].poller(vlan_handlers[c].priv); +} + +void vlan_reset() +{ + timer_add(vlan_poller, &vlan_poller_time, TIMER_ALWAYS_ENABLED, NULL); + + vlan_handlers_num = 0; +} diff --git a/src/nethandler.h b/src/nethandler.h new file mode 100644 index 0000000..024fad8 --- /dev/null +++ b/src/nethandler.h @@ -0,0 +1,20 @@ +#include + +//void vlan_handler(int (*can_receive)(void *p), void (*receive)(void *p, const uint8_t *buf, int size), void *p); +void vlan_handler(void (*poller)(void *p), void *p); + +extern int network_card_current; + +int network_card_available(int card); +char *network_card_getname(int card); +struct device_t *network_card_getdevice(int card); +int network_card_has_config(int card); +void network_card_init(); + +char *network_card_get_internal_name(int card); +int network_card_get_from_internal_name(char *s); + +void initpcap(); +void closepcap(); + +void vlan_reset(); diff --git a/src/pc.c b/src/pc.c index 3a652dd..b836136 100644 --- a/src/pc.c +++ b/src/pc.c @@ -49,6 +49,13 @@ #include "x86.h" #include "paths.h" +#ifdef USE_NETWORKING +#include "nethandler.h" +#define NE2000 1 +uint8_t ethif; +int inum; +#endif + int window_w, window_h, window_x, window_y, window_remember; int start_in_fullscreen = 0; @@ -276,6 +283,10 @@ void initpc(int argc, char *argv[]) disc_init(); fdi_init(); img_init(); +#ifdef USE_NETWORKING + vlan_reset(); //NETWORK + network_card_init(network_card_current); +#endif //loadfont(); loadnvr(); @@ -371,6 +382,12 @@ void resetpchard() mouse_emu_init(); video_init(); speaker_init(); + +#ifdef USE_NETWORKING + vlan_reset(); //NETWORK + network_card_init(network_card_current); +#endif + sound_card_init(sound_card_current); if (GUS) device_add(&gus_device); @@ -757,6 +774,18 @@ void loadconfig(char *fn) enable_sync = config_get_int(CFG_MACHINE, NULL, "enable_sync", 1); +#ifdef USE_NETWORKING + //network + ethif = config_get_int(CFG_GLOBAL, NULL, "netinterface", 1); + if (ethif >= inum) + inum = ethif + 1; + + p = (char *)config_get_string(CFG_MACHINE, NULL, "netcard", ""); + if (p) + network_card_current = network_card_get_from_internal_name(p); + else + network_card_current = 0; +#endif for (d = 0; d < num_config_callbacks; ++d) if (config_callbacks[d].loadconfig) @@ -874,6 +903,11 @@ void saveconfig(char *fn) config_set_int(CFG_MACHINE, NULL, "enable_sync", enable_sync); +#ifdef USE_NETWORKING + config_set_int(CFG_GLOBAL, NULL, "netinterface", ethif); + config_set_string(CFG_MACHINE, NULL, "netcard", network_card_get_internal_name(network_card_current)); +#endif + for (d = 0; d < num_config_callbacks; ++d) if (config_callbacks[d].saveconfig) config_callbacks[d].saveconfig(); diff --git a/src/pc.rc b/src/pc.rc index c2a4b64..2307a57 100644 --- a/src/pc.rc +++ b/src/pc.rc @@ -69,13 +69,22 @@ BEGIN END END +#ifdef USE_NETWORKING +ConfigureDlg DIALOGEX 0, 0, 292, 432 +#else ConfigureDlg DIALOGEX 0, 0, 292, 412 +#endif STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Configure PCem" FONT 8, "MS Sans Serif" BEGIN +#ifdef USE_NETWORKING + DEFPUSHBUTTON "OK",IDOK,64,408,50,14, WS_TABSTOP + PUSHBUTTON "Cancel",IDCANCEL,128,408,50,14, WS_TABSTOP +#else DEFPUSHBUTTON "OK",IDOK,64,388,50,14, WS_TABSTOP PUSHBUTTON "Cancel",IDCANCEL,128,388,50,14, WS_TABSTOP +#endif COMBOBOX IDC_COMBO1,62,16,157,120,CBS_DROPDOWN | WS_VSCROLL | WS_TABSTOP PUSHBUTTON "Configure", IDC_CONFIGUREMOD, 224, 16, 40, 14, WS_TABSTOP COMBOBOX IDC_COMBOVID,62,36,157,120,CBS_DROPDOWN | WS_VSCROLL | WS_TABSTOP @@ -90,12 +99,6 @@ BEGIN EDITTEXT IDC_MEMTEXT, 62, 172, 36, 14, ES_AUTOHSCROLL | ES_NUMBER CONTROL "", IDC_MEMSPIN, UPDOWN_CLASS, UDS_ALIGNRIGHT | UDS_ARROWKEYS | UDS_NOTHOUSANDS | UDS_SETBUDDYINT, 98, 172, 12, 14 LTEXT "MB", IDC_TEXT_MB, 98, 172, 40, 10 - CONTROL "CMS / Game Blaster",IDC_CHECK3,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,312,118,10 - CONTROL "Gravis Ultrasound",IDC_CHECKGUS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,328,118,10 - CONTROL "Innovation SSI-2001",IDC_CHECKSSI,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,344,118,10 - CONTROL "Synchronise time to host clock",IDC_CHECKSYNC,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,360,118,10 - CONTROL "Voodoo Graphics",IDC_CHECKVOODOO,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,376,118,10 - PUSHBUTTON "Configure", IDC_CONFIGUREVOODOO, 224, 376, 40, 14, WS_TABSTOP LTEXT "Machine :",IDC_STATIC,15,16,40,10 LTEXT "Video :",IDC_STATIC,15,36,34,10 LTEXT "CPU type :",IDC_STATIC,15,56,34,10 @@ -110,6 +113,28 @@ BEGIN LTEXT "Drive B: :",IDC_STATIC,15,236,40,10 COMBOBOX IDC_COMBODRA,62,216,157,120,CBS_DROPDOWN | WS_VSCROLL | WS_TABSTOP COMBOBOX IDC_COMBODRB,62,236,157,120,CBS_DROPDOWN | WS_VSCROLL | WS_TABSTOP + +#ifdef USE_NETWORKING + LTEXT "Network card:",IDC_STATIC,15,256,45,10 + COMBOBOX IDC_COMBONETCARD,62,256,157,120,CBS_DROPDOWN | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Configure", IDC_CONFIGURENETCARD, 224, 256, 40, 14, WS_TABSTOP + + LTEXT "Mouse :",IDC_STATIC,15,276,40,10 + COMBOBOX IDC_COMBOMOUSE,62,276,157,120,CBS_DROPDOWN | WS_VSCROLL | WS_TABSTOP + LTEXT "Joystick :",IDC_STATIC,15,296,40,10 + COMBOBOX IDC_COMBOJOY,62,296,157,120,CBS_DROPDOWN | WS_VSCROLL | WS_TABSTOP + DEFPUSHBUTTON "Joystick 1...",IDC_JOY1,16,312,50,14, WS_TABSTOP + PUSHBUTTON "Joystick 2...",IDC_JOY2,80,312,50,14, WS_TABSTOP + DEFPUSHBUTTON "Joystick 3...",IDC_JOY3,144,312,50,14, WS_TABSTOP + PUSHBUTTON "Joystick 4...",IDC_JOY4,208,312,50,14, WS_TABSTOP + + CONTROL "CMS / Game Blaster",IDC_CHECK3,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,332,118,10 + CONTROL "Gravis Ultrasound",IDC_CHECKGUS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,348,118,10 + CONTROL "Innovation SSI-2001",IDC_CHECKSSI,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,364,118,10 + CONTROL "Synchronise time to host clock",IDC_CHECKSYNC,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,380,118,10 + CONTROL "Voodoo Graphics",IDC_CHECKVOODOO,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,396,118,10 + PUSHBUTTON "Configure", IDC_CONFIGUREVOODOO, 224, 356, 40, 14, WS_TABSTOP +#else LTEXT "Mouse :",IDC_STATIC,15,256,40,10 COMBOBOX IDC_COMBOMOUSE,62,256,157,120,CBS_DROPDOWN | WS_VSCROLL | WS_TABSTOP LTEXT "Joystick :",IDC_STATIC,15,276,40,10 @@ -118,6 +143,14 @@ BEGIN PUSHBUTTON "Joystick 2...",IDC_JOY2,80,292,50,14, WS_TABSTOP DEFPUSHBUTTON "Joystick 3...",IDC_JOY3,144,292,50,14, WS_TABSTOP PUSHBUTTON "Joystick 4...",IDC_JOY4,208,292,50,14, WS_TABSTOP + + CONTROL "CMS / Game Blaster",IDC_CHECK3,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,312,118,10 + CONTROL "Gravis Ultrasound",IDC_CHECKGUS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,328,118,10 + CONTROL "Innovation SSI-2001",IDC_CHECKSSI,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,344,118,10 + CONTROL "Synchronise time to host clock",IDC_CHECKSYNC,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,360,118,10 + CONTROL "Voodoo Graphics",IDC_CHECKVOODOO,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,376,118,10 + PUSHBUTTON "Configure", IDC_CONFIGUREVOODOO, 224, 376, 40, 14, WS_TABSTOP +#endif END HdConfDlg DIALOGEX 0, 0, 210, 312+4*16 diff --git a/src/resources.h b/src/resources.h index 9e1b226..4d9193d 100644 --- a/src/resources.h +++ b/src/resources.h @@ -44,6 +44,7 @@ #define IDC_COMBOWS 1065 #define IDC_COMBOMOUSE 1066 #define IDC_COMBOHDD 1067 +#define IDC_COMBONETCARD 1068 #define IDC_CHECK1 1010 #define IDC_CHECK2 1011 #define IDC_CHECK3 1012 @@ -126,6 +127,7 @@ #define IDC_CONFIGURESND 1201 #define IDC_CONFIGUREVOODOO 1202 #define IDC_CONFIGUREMOD 1203 +#define IDC_CONFIGURENETCARD 1204 #define IDC_JOY1 1210 #define IDC_JOY2 1211 #define IDC_JOY3 1212 diff --git a/src/slirp/COPYRIGHT.txt b/src/slirp/COPYRIGHT.txt new file mode 100644 index 0000000..01d4400 --- /dev/null +++ b/src/slirp/COPYRIGHT.txt @@ -0,0 +1,61 @@ +Slirp was written by Danny Gasparovski. +Copyright (c), 1995,1996 All Rights Reserved. + +Slirp is maintained by Kelly Price + +Slirp is free software; "free" as in you don't have to pay for it, and you +are free to do whatever you want with it. I do not accept any donations, +monetary or otherwise, for Slirp. Instead, I would ask you to pass this +potential donation to your favorite charity. In fact, I encourage +*everyone* who finds Slirp useful to make a small donation to their +favorite charity (for example, GreenPeace). This is not a requirement, but +a suggestion from someone who highly values the service they provide. + +The copyright terms and conditions: + +---BEGIN--- + + Copyright (c) 1995,1996 Danny Gasparovski. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + DANNY GASPAROVSKI OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---END--- + +This basically means you can do anything you want with the software, except +1) call it your own, and 2) claim warranty on it. There is no warranty for +this software. None. Nada. If you lose a million dollars while using +Slirp, that's your loss not mine. So, ***USE AT YOUR OWN RISK!***. + +If these conditions cannot be met due to legal restrictions (E.g. where it +is against the law to give out Software without warranty), you must cease +using the software and delete all copies you have. + +Slirp uses code that is copyrighted by the following people/organizations: + +Juha Pirkola. +Gregory M. Christy. +The Regents of the University of California. +Carnegie Mellon University. +The Australian National University. +RSA Data Security, Inc. + +Please read the top of each source file for the details on the various +copyrights. \ No newline at end of file diff --git a/src/slirp/Makefile b/src/slirp/Makefile new file mode 100644 index 0000000..e513410 --- /dev/null +++ b/src/slirp/Makefile @@ -0,0 +1,26 @@ +CC=gcc +CFLAGS=-I. -Ofast -Wall +DEPS = bootp.h config-host.h config.h ctl.h \ + debug.h icmp_var.h if.h ip.h \ + ip_icmp.h libslirp.h main.h mbuf.h \ + misc.h queue.h sbuf.h slirp.h \ + slirp_config.h socket.h tcp.h tcpip.h \ + tcp_timer.h tcp_var.h tftp.h udp.h + +OBJ = bootp.o cksum.o debug.o if.o ip_icmp.o \ + ip_input.o ip_output.o mbuf.o misc.o queue.o \ + sbuf.o slirp.o socket.o tcp_input.o tcp_output.o \ + tcp_subr.o tcp_timer.o tftp.o udp.o + +%.o: %.c $(DEPS) + $(CC) $(CFLAGS) -c $< -o $@ + +default: libslirp.a + +clean: + rm -f $(OBJ) + rm -f libslirp.a + +libslirp.a: $(OBJ) + ar rcs $@ $^ + ranlib $@ \ No newline at end of file diff --git a/src/slirp/VERSION.txt b/src/slirp/VERSION.txt new file mode 100644 index 0000000..0dd1c2b --- /dev/null +++ b/src/slirp/VERSION.txt @@ -0,0 +1 @@ +qemu 0.9.0 (2007/02/05) \ No newline at end of file diff --git a/src/slirp/bootp.c b/src/slirp/bootp.c new file mode 100644 index 0000000..34913b6 --- /dev/null +++ b/src/slirp/bootp.c @@ -0,0 +1,242 @@ +/* + * QEMU BOOTP/DHCP server + * + * Copyright (c) 2004 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "slirp.h" + +/* XXX: only DHCP is supported */ + +#define NB_ADDR 16 + +#define START_ADDR 15 + +#define LEASE_TIME (24 * 3600) + +typedef struct { + uint8_t allocated; + uint8_t macaddr[6]; +} BOOTPClient; + +BOOTPClient bootp_clients[NB_ADDR]; + +static const uint8_t rfc1533_cookie[] = { RFC1533_COOKIE }; + +static BOOTPClient *get_new_addr(struct in_addr *paddr) +{ + BOOTPClient *bc; + int i; + + for(i = 0; i < NB_ADDR; i++) { + if (!bootp_clients[i].allocated) + goto found; + } + return NULL; + found: + bc = &bootp_clients[i]; + bc->allocated = 1; + paddr->s_addr = htonl(ntohl(special_addr.s_addr) | (i + START_ADDR)); + return bc; +} + +static BOOTPClient *find_addr(struct in_addr *paddr, const uint8_t *macaddr) +{ + BOOTPClient *bc; + int i; + + for(i = 0; i < NB_ADDR; i++) { + if (!memcmp(macaddr, bootp_clients[i].macaddr, 6)) + goto found; + } + return NULL; + found: + bc = &bootp_clients[i]; + bc->allocated = 1; + paddr->s_addr = htonl(ntohl(special_addr.s_addr) | (i + START_ADDR)); + return bc; +} + +static void dhcp_decode(const uint8_t *buf, int size, + int *pmsg_type) +{ + const uint8_t *p, *p_end; + int len, tag; + + *pmsg_type = 0; + + p = buf; + p_end = buf + size; + if (size < 5) + return; + if (memcmp(p, rfc1533_cookie, 4) != 0) + return; + p += 4; + while (p < p_end) { + tag = p[0]; + if (tag == RFC1533_PAD) { + p++; + } else if (tag == RFC1533_END) { + break; + } else { + p++; + if (p >= p_end) + break; + len = *p++; + + switch(tag) { + case RFC2132_MSG_TYPE: + if (len >= 1) + *pmsg_type = p[0]; + break; + default: + break; + } + p += len; + } + } +} + +static void bootp_reply(struct bootp_t *bp) +{ + BOOTPClient *bc; + struct SLIRPmbuf *m; + struct bootp_t *rbp; + struct sockaddr_in saddr, daddr; + struct in_addr dns_addr; + int dhcp_msg_type, val; + uint8_t *q; + + /* extract exact DHCP msg type */ + dhcp_decode(bp->bp_vend, DHCP_OPT_LEN, &dhcp_msg_type); + + if (dhcp_msg_type == 0) + dhcp_msg_type = DHCPREQUEST; /* Force reply for old BOOTP clients */ + + if (dhcp_msg_type != DHCPDISCOVER && + dhcp_msg_type != DHCPREQUEST) + return; + /* XXX: this is a hack to get the client mac address */ + memcpy(client_ethaddr, bp->bp_hwaddr, 6); + + if ((m = m_get()) == NULL) + return; + m->m_data += if_maxlinkhdr; + rbp = (struct bootp_t *)m->m_data; + m->m_data += sizeof(struct udpiphdr); + memset(rbp, 0, sizeof(struct bootp_t)); + + if (dhcp_msg_type == DHCPDISCOVER) { + new_addr: + bc = get_new_addr(&daddr.sin_addr); + if (!bc) + return; + memcpy(bc->macaddr, client_ethaddr, 6); + } else { + bc = find_addr(&daddr.sin_addr, bp->bp_hwaddr); + if (!bc) { + /* if never assigned, behaves as if it was already + assigned (windows fix because it remembers its address) */ + goto new_addr; + } + } + + saddr.sin_addr.s_addr = htonl(ntohl(special_addr.s_addr) | CTL_ALIAS); + saddr.sin_port = htons(BOOTP_SERVER); + + daddr.sin_port = htons(BOOTP_CLIENT); + + rbp->bp_op = BOOTP_REPLY; + rbp->bp_xid = bp->bp_xid; + rbp->bp_htype = 1; + rbp->bp_hlen = 6; + memcpy(rbp->bp_hwaddr, bp->bp_hwaddr, 6); + + rbp->bp_yiaddr = daddr.sin_addr; /* Client IP address */ + rbp->bp_siaddr = saddr.sin_addr; /* Server IP address */ + + q = rbp->bp_vend; + memcpy(q, rfc1533_cookie, 4); + q += 4; + + if (dhcp_msg_type == DHCPDISCOVER) { + *q++ = RFC2132_MSG_TYPE; + *q++ = 1; + *q++ = DHCPOFFER; + } else if (dhcp_msg_type == DHCPREQUEST) { + *q++ = RFC2132_MSG_TYPE; + *q++ = 1; + *q++ = DHCPACK; + } + + if (dhcp_msg_type == DHCPDISCOVER || + dhcp_msg_type == DHCPREQUEST) { + *q++ = RFC2132_SRV_ID; + *q++ = 4; + memcpy(q, &saddr.sin_addr, 4); + q += 4; + + *q++ = RFC1533_NETMASK; + *q++ = 4; + *q++ = 0xff; + *q++ = 0xff; + *q++ = 0xff; + *q++ = 0x00; + + *q++ = RFC1533_GATEWAY; + *q++ = 4; + memcpy(q, &saddr.sin_addr, 4); + q += 4; + + *q++ = RFC1533_DNS; + *q++ = 4; + dns_addr.s_addr = htonl(ntohl(special_addr.s_addr) | CTL_DNS); + memcpy(q, &dns_addr, 4); + q += 4; + + *q++ = RFC2132_LEASE_TIME; + *q++ = 4; + val = htonl(LEASE_TIME); + memcpy(q, &val, 4); + q += 4; + + if (*slirp_hostname) { + val = strlen(slirp_hostname); + *q++ = RFC1533_HOSTNAME; + *q++ = val; + memcpy(q, slirp_hostname, val); + q += val; + } + } + *q++ = RFC1533_END; + + m->m_len = sizeof(struct bootp_t) - + sizeof(struct ip) - sizeof(struct udphdr); + udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY); +} + +void bootp_input(struct SLIRPmbuf *m) +{ + struct bootp_t *bp = mtod(m, struct bootp_t *); + + if (bp->bp_op == BOOTP_REQUEST) { + bootp_reply(bp); + } +} diff --git a/src/slirp/bootp.h b/src/slirp/bootp.h new file mode 100644 index 0000000..0625161 --- /dev/null +++ b/src/slirp/bootp.h @@ -0,0 +1,121 @@ +/* bootp/dhcp defines */ + +#define BOOTP_SERVER 67 +#define BOOTP_CLIENT 68 + +#define BOOTP_REQUEST 1 +#define BOOTP_REPLY 2 + +#define RFC1533_COOKIE 99, 130, 83, 99 +#define RFC1533_PAD 0 +#define RFC1533_NETMASK 1 +#define RFC1533_TIMEOFFSET 2 +#define RFC1533_GATEWAY 3 +#define RFC1533_TIMESERVER 4 +#define RFC1533_IEN116NS 5 +#define RFC1533_DNS 6 +#define RFC1533_LOGSERVER 7 +#define RFC1533_COOKIESERVER 8 +#define RFC1533_LPRSERVER 9 +#define RFC1533_IMPRESSSERVER 10 +#define RFC1533_RESOURCESERVER 11 +#define RFC1533_HOSTNAME 12 +#define RFC1533_BOOTFILESIZE 13 +#define RFC1533_MERITDUMPFILE 14 +#define RFC1533_DOMAINNAME 15 +#define RFC1533_SWAPSERVER 16 +#define RFC1533_ROOTPATH 17 +#define RFC1533_EXTENSIONPATH 18 +#define RFC1533_IPFORWARDING 19 +#define RFC1533_IPSOURCEROUTING 20 +#define RFC1533_IPPOLICYFILTER 21 +#define RFC1533_IPMAXREASSEMBLY 22 +#define RFC1533_IPTTL 23 +#define RFC1533_IPMTU 24 +#define RFC1533_IPMTUPLATEAU 25 +#define RFC1533_INTMTU 26 +#define RFC1533_INTLOCALSUBNETS 27 +#define RFC1533_INTBROADCAST 28 +#define RFC1533_INTICMPDISCOVER 29 +#define RFC1533_INTICMPRESPOND 30 +#define RFC1533_INTROUTEDISCOVER 31 +#define RFC1533_INTROUTESOLICIT 32 +#define RFC1533_INTSTATICROUTES 33 +#define RFC1533_LLTRAILERENCAP 34 +#define RFC1533_LLARPCACHETMO 35 +#define RFC1533_LLETHERNETENCAP 36 +#define RFC1533_TCPTTL 37 +#define RFC1533_TCPKEEPALIVETMO 38 +#define RFC1533_TCPKEEPALIVEGB 39 +#define RFC1533_NISDOMAIN 40 +#define RFC1533_NISSERVER 41 +#define RFC1533_NTPSERVER 42 +#define RFC1533_VENDOR 43 +#define RFC1533_NBNS 44 +#define RFC1533_NBDD 45 +#define RFC1533_NBNT 46 +#define RFC1533_NBSCOPE 47 +#define RFC1533_XFS 48 +#define RFC1533_XDM 49 + +#define RFC2132_REQ_ADDR 50 +#define RFC2132_LEASE_TIME 51 +#define RFC2132_MSG_TYPE 53 +#define RFC2132_SRV_ID 54 +#define RFC2132_PARAM_LIST 55 +#define RFC2132_MAX_SIZE 57 +#define RFC2132_RENEWAL_TIME 58 +#define RFC2132_REBIND_TIME 59 + +#define DHCPDISCOVER 1 +#define DHCPOFFER 2 +#define DHCPREQUEST 3 +#define DHCPACK 5 + +#define RFC1533_VENDOR_MAJOR 0 +#define RFC1533_VENDOR_MINOR 0 + +#define RFC1533_VENDOR_MAGIC 128 +#define RFC1533_VENDOR_ADDPARM 129 +#define RFC1533_VENDOR_ETHDEV 130 +#define RFC1533_VENDOR_HOWTO 132 +#define RFC1533_VENDOR_MNUOPTS 160 +#define RFC1533_VENDOR_SELECTION 176 +#define RFC1533_VENDOR_MOTD 184 +#define RFC1533_VENDOR_NUMOFMOTD 8 +#define RFC1533_VENDOR_IMG 192 +#define RFC1533_VENDOR_NUMOFIMG 16 + +#define RFC1533_END 255 +#define BOOTP_VENDOR_LEN 64 +#define DHCP_OPT_LEN 312 + +#ifdef PRAGMA_PACK_SUPPORTED +#pragma pack(1) +#endif + +struct bootp_t { + struct ip ip; + struct udphdr udp; + uint8_t bp_op; + uint8_t bp_htype; + uint8_t bp_hlen; + uint8_t bp_hops; + uint32_t bp_xid; + uint16_t bp_secs; + uint16_t unused; + struct in_addr bp_ciaddr; + struct in_addr bp_yiaddr; + struct in_addr bp_siaddr; + struct in_addr bp_giaddr; + uint8_t bp_hwaddr[16]; + uint8_t bp_sname[64]; + uint8_t bp_file[128]; + uint8_t bp_vend[DHCP_OPT_LEN]; +} PACKED__; + +#ifdef PRAGMA_PACK_SUPPORTED +#pragma pack(PACK_END) +#endif + +void bootp_input(struct SLIRPmbuf *m); diff --git a/src/slirp/cksum.c b/src/slirp/cksum.c new file mode 100644 index 0000000..eeccb88 --- /dev/null +++ b/src/slirp/cksum.c @@ -0,0 +1,137 @@ +/* + * Copyright (c) 1988, 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)in_cksum.c 8.1 (Berkeley) 6/10/93 + * in_cksum.c,v 1.2 1994/08/02 07:48:16 davidg Exp + */ + +#include "slirp.h" + +/* + * Checksum routine for Internet Protocol family headers (Portable Version). + * + * This routine is very heavily used in the network + * code and should be modified for each CPU to be as fast as possible. + * + * XXX Since we will never span more than 1 SLIRPmbuf, we can optimise this + */ + +#define ADDCARRY(x) (x > 65535 ? x -= 65535 : x) +#define REDUCE {l_util.l = sum; sum = l_util.s[0] + l_util.s[1]; ADDCARRY(sum);} + +int cksum(struct SLIRPmbuf *m, int len) +{ + register u_int16_t *w; + register int sum = 0; + register int mlen = 0; + int byte_swapped = 0; + + union { + u_int8_t c[2]; + u_int16_t s; + } s_util; + union { + u_int16_t s[2]; + u_int32_t l; + } l_util; + + if (m->m_len == 0) + goto cont; + w = mtod(m, u_int16_t *); + + mlen = m->m_len; + + if (len < mlen) + mlen = len; + len -= mlen; + /* + * Force to even boundary. + */ + if ((1 & (long) w) && (mlen > 0)) { + REDUCE; + sum <<= 8; + s_util.c[0] = *(u_int8_t *)w; + w = (u_int16_t *)((int8_t *)w + 1); + mlen--; + byte_swapped = 1; + } + /* + * Unroll the loop to make overhead from + * branches &c small. + */ + while ((mlen -= 32) >= 0) { + sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3]; + sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7]; + sum += w[8]; sum += w[9]; sum += w[10]; sum += w[11]; + sum += w[12]; sum += w[13]; sum += w[14]; sum += w[15]; + w += 16; + } + mlen += 32; + while ((mlen -= 8) >= 0) { + sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3]; + w += 4; + } + mlen += 8; + if (mlen == 0 && byte_swapped == 0) + goto cont; + REDUCE; + while ((mlen -= 2) >= 0) { + sum += *w++; + } + + if (byte_swapped) { + REDUCE; + sum <<= 8; + byte_swapped = 0; + if (mlen == -1) { + s_util.c[1] = *(u_int8_t *)w; + sum += s_util.s; + mlen = 0; + } else + + mlen = -1; + } else if (mlen == -1) + s_util.c[0] = *(u_int8_t *)w; + +cont: +#ifdef SLIRP_DEBUG + if (len) { + DEBUG_ERROR((dfd, "cksum: out of data\n")); + DEBUG_ERROR((dfd, " len = %d\n", len)); + } +#endif + if (mlen == -1) { + /* The last SLIRPmbuf has odd # of bytes. Follow the + standard (the odd byte may be shifted left by 8 bits + or not as determined by endian-ness of the machine) */ + s_util.c[1] = 0; + sum += s_util.s; + } + REDUCE; + return (~sum & 0xffff); +} diff --git a/src/slirp/config-host.h b/src/slirp/config-host.h new file mode 100644 index 0000000..90e7dd3 --- /dev/null +++ b/src/slirp/config-host.h @@ -0,0 +1,9 @@ +/* Automatically generated by configure - do not modify */ +#define CONFIG_QEMU_SHAREDIR "/c/Program Files/Qemu" +#define HOST_I386 1 +#define HOST_LONG_BITS 32 +#define CONFIG_WIN32 1 +#define CONFIG_GDBSTUB 1 +#define CONFIG_SLIRP 1 +#define QEMU_VERSION "0.9.0" +#define CONFIG_UNAME_RELEASE "" diff --git a/src/slirp/config.h b/src/slirp/config.h new file mode 100644 index 0000000..09047ab --- /dev/null +++ b/src/slirp/config.h @@ -0,0 +1,9 @@ +/* Automatically generated by configure - do not modify */ +#include "config-host.h" +#define CONFIG_QEMU_PREFIX "/usr/gnemul/qemu-i386" +#define TARGET_ARCH "i386" +#define TARGET_I386 1 +#define USE_KQEMU 1 +#define CONFIG_SOFTMMU 1 +#define CONFIG_SDL 1 +#define HAVE_STRDUP 1 diff --git a/src/slirp/ctl.h b/src/slirp/ctl.h new file mode 100644 index 0000000..efac7cb --- /dev/null +++ b/src/slirp/ctl.h @@ -0,0 +1,7 @@ +#define CTL_CMD 0 +#define CTL_EXEC 1 +#define CTL_ALIAS 2 +#define CTL_DNS 3 + +#define CTL_SPECIAL "10.0.2.0" +#define CTL_LOCAL "10.0.2.15" diff --git a/src/slirp/debug.c b/src/slirp/debug.c new file mode 100644 index 0000000..f6f244b --- /dev/null +++ b/src/slirp/debug.c @@ -0,0 +1,440 @@ +/* + * Copyright (c) 1995 Danny Gasparovski. + * Portions copyright (c) 2000 Kelly Price. + * + * Please read the file COPYRIGHT for the + * terms and conditions of the copyright. + */ + +#include "slirp.h" + +FILE *dfd = NULL; +#ifdef SLIRP_DEBUG +int dostats = 1; +#else +int dostats = 0; +#endif +int slirp_debug = 0; + +extern char *strerror _P((int)); + +/* Carry over one item from main.c so that the tty's restored. + * Only done when the tty being used is /dev/tty --RedWolf */ +extern struct termios slirp_tty_settings; +extern int slirp_tty_restore; + + +void +debug_init(file, dbg) + char *file; + int dbg; +{ + /* Close the old debugging file */ + if (dfd) + fclose(dfd); + + dfd = fopen(file,"w"); + if (dfd != NULL) { +#if 1 + fprintf(dfd,"Slirp %s - Debugging Started.\n", SLIRP_VERSION); +#endif + fprintf(dfd,"Debugging Started level %i.\r\n",dbg); + fflush(dfd); + slirp_debug = dbg; + } else { + lprint("Error: Debugging file \"%s\" could not be opened: %s\r\n", + file, strerror(errno)); + } +} + +/* + * Dump a packet in the same format as tcpdump -x + */ +#ifdef SLIRP_DEBUG +void +dump_packet(dat, n) + void *dat; + int n; +{ + u_char *pptr = (u_char *)dat; + int j,k; + + n /= 16; + n++; + DEBUG_MISC((dfd, "PACKET DUMPED: \n")); + for(j = 0; j < n; j++) { + for(k = 0; k < 6; k++) + DEBUG_MISC((dfd, "%02x ", *pptr++)); + DEBUG_MISC((dfd, "\n")); + fflush(dfd); + } +} +#endif + +#if 0 +/* + * Statistic routines + * + * These will print statistics to the screen, the debug file (dfd), or + * a buffer, depending on "type", so that the stats can be sent over + * the link as well. + */ + +void +ttystats(ttyp) + struct ttys *ttyp; +{ + struct slirp_ifstats *is = &ttyp->ifstats; + char buff[512]; + + lprint(" \r\n"); + + if (if_comp & IF_COMPRESS) + strcpy(buff, "on"); + else if (if_comp & IF_NOCOMPRESS) + strcpy(buff, "off"); + else + strcpy(buff, "off (for now)"); + lprint("Unit %d:\r\n", ttyp->unit); + lprint(" using %s encapsulation (VJ compression is %s)\r\n", ( +#ifdef USE_PPP + ttyp->proto==PROTO_PPP?"PPP": +#endif + "SLIP"), buff); + lprint(" %d baudrate\r\n", ttyp->baud); + lprint(" interface is %s\r\n", ttyp->up?"up":"down"); + lprint(" using fd %d, guardian pid is %d\r\n", ttyp->fd, ttyp->pid); +#ifndef FULL_BOLT + lprint(" towrite is %d bytes\r\n", ttyp->towrite); +#endif + if (ttyp->zeros) + lprint(" %d zeros have been typed\r\n", ttyp->zeros); + else if (ttyp->ones) + lprint(" %d ones have been typed\r\n", ttyp->ones); + lprint("Interface stats:\r\n"); + lprint(" %6d output packets sent (%d bytes)\r\n", is->out_pkts, is->out_bytes); + lprint(" %6d output packets dropped (%d bytes)\r\n", is->out_errpkts, is->out_errbytes); + lprint(" %6d input packets received (%d bytes)\r\n", is->in_pkts, is->in_bytes); + lprint(" %6d input packets dropped (%d bytes)\r\n", is->in_errpkts, is->in_errbytes); + lprint(" %6d bad input packets\r\n", is->in_mbad); +} + +void +allttystats() +{ + struct ttys *ttyp; + + for (ttyp = ttys; ttyp; ttyp = ttyp->next) + ttystats(ttyp); +} +#endif + +void +ipstats() +{ + lprint(" \r\n"); + + lprint("IP stats:\r\n"); + lprint(" %6d total packets received (%d were unaligned)\r\n", + ipstat.ips_total, ipstat.ips_unaligned); + lprint(" %6d with incorrect version\r\n", ipstat.ips_badvers); + lprint(" %6d with bad header checksum\r\n", ipstat.ips_badsum); + lprint(" %6d with length too short (len < sizeof(iphdr))\r\n", ipstat.ips_tooshort); + lprint(" %6d with length too small (len < ip->len)\r\n", ipstat.ips_toosmall); + lprint(" %6d with bad header length\r\n", ipstat.ips_badhlen); + lprint(" %6d with bad packet length\r\n", ipstat.ips_badlen); + lprint(" %6d fragments received\r\n", ipstat.ips_fragments); + lprint(" %6d fragments dropped\r\n", ipstat.ips_fragdropped); + lprint(" %6d fragments timed out\r\n", ipstat.ips_fragtimeout); + lprint(" %6d packets reassembled ok\r\n", ipstat.ips_reassembled); + lprint(" %6d outgoing packets fragmented\r\n", ipstat.ips_fragmented); + lprint(" %6d total outgoing fragments\r\n", ipstat.ips_ofragments); + lprint(" %6d with bad protocol field\r\n", ipstat.ips_noproto); + lprint(" %6d total packets delivered\r\n", ipstat.ips_delivered); +} + +#if 0 +void +vjstats() +{ + lprint(" \r\n"); + + lprint("VJ compression stats:\r\n"); + + lprint(" %6d outbound packets (%d compressed)\r\n", + comp_s.sls_packets, comp_s.sls_compressed); + lprint(" %6d searches for connection stats (%d misses)\r\n", + comp_s.sls_searches, comp_s.sls_misses); + lprint(" %6d inbound uncompressed packets\r\n", comp_s.sls_uncompressedin); + lprint(" %6d inbound compressed packets\r\n", comp_s.sls_compressedin); + lprint(" %6d inbound unknown type packets\r\n", comp_s.sls_errorin); + lprint(" %6d inbound packets tossed due to error\r\n", comp_s.sls_tossed); +} +#endif + +void +tcpstats() +{ + lprint(" \r\n"); + + lprint("TCP stats:\r\n"); + + lprint(" %6d packets sent\r\n", tcpstat.tcps_sndtotal); + lprint(" %6d data packets (%d bytes)\r\n", + tcpstat.tcps_sndpack, tcpstat.tcps_sndbyte); + lprint(" %6d data packets retransmitted (%d bytes)\r\n", + tcpstat.tcps_sndrexmitpack, tcpstat.tcps_sndrexmitbyte); + lprint(" %6d ack-only packets (%d delayed)\r\n", + tcpstat.tcps_sndacks, tcpstat.tcps_delack); + lprint(" %6d URG only packets\r\n", tcpstat.tcps_sndurg); + lprint(" %6d window probe packets\r\n", tcpstat.tcps_sndprobe); + lprint(" %6d window update packets\r\n", tcpstat.tcps_sndwinup); + lprint(" %6d control (SYN/FIN/RST) packets\r\n", tcpstat.tcps_sndctrl); + lprint(" %6d times tcp_output did nothing\r\n", tcpstat.tcps_didnuttin); + + lprint(" %6d packets received\r\n", tcpstat.tcps_rcvtotal); + lprint(" %6d acks (for %d bytes)\r\n", + tcpstat.tcps_rcvackpack, tcpstat.tcps_rcvackbyte); + lprint(" %6d duplicate acks\r\n", tcpstat.tcps_rcvdupack); + lprint(" %6d acks for unsent data\r\n", tcpstat.tcps_rcvacktoomuch); + lprint(" %6d packets received in sequence (%d bytes)\r\n", + tcpstat.tcps_rcvpack, tcpstat.tcps_rcvbyte); + lprint(" %6d completely duplicate packets (%d bytes)\r\n", + tcpstat.tcps_rcvduppack, tcpstat.tcps_rcvdupbyte); + + lprint(" %6d packets with some duplicate data (%d bytes duped)\r\n", + tcpstat.tcps_rcvpartduppack, tcpstat.tcps_rcvpartdupbyte); + lprint(" %6d out-of-order packets (%d bytes)\r\n", + tcpstat.tcps_rcvoopack, tcpstat.tcps_rcvoobyte); + lprint(" %6d packets of data after window (%d bytes)\r\n", + tcpstat.tcps_rcvpackafterwin, tcpstat.tcps_rcvbyteafterwin); + lprint(" %6d window probes\r\n", tcpstat.tcps_rcvwinprobe); + lprint(" %6d window update packets\r\n", tcpstat.tcps_rcvwinupd); + lprint(" %6d packets received after close\r\n", tcpstat.tcps_rcvafterclose); + lprint(" %6d discarded for bad checksums\r\n", tcpstat.tcps_rcvbadsum); + lprint(" %6d discarded for bad header offset fields\r\n", + tcpstat.tcps_rcvbadoff); + + lprint(" %6d connection requests\r\n", tcpstat.tcps_connattempt); + lprint(" %6d connection accepts\r\n", tcpstat.tcps_accepts); + lprint(" %6d connections established (including accepts)\r\n", tcpstat.tcps_connects); + lprint(" %6d connections closed (including %d drop)\r\n", + tcpstat.tcps_closed, tcpstat.tcps_drops); + lprint(" %6d embryonic connections dropped\r\n", tcpstat.tcps_conndrops); + lprint(" %6d segments we tried to get rtt (%d succeeded)\r\n", + tcpstat.tcps_segstimed, tcpstat.tcps_rttupdated); + lprint(" %6d retransmit timeouts\r\n", tcpstat.tcps_rexmttimeo); + lprint(" %6d connections dropped by rxmt timeout\r\n", + tcpstat.tcps_timeoutdrop); + lprint(" %6d persist timeouts\r\n", tcpstat.tcps_persisttimeo); + lprint(" %6d keepalive timeouts\r\n", tcpstat.tcps_keeptimeo); + lprint(" %6d keepalive probes sent\r\n", tcpstat.tcps_keepprobe); + lprint(" %6d connections dropped by keepalive\r\n", tcpstat.tcps_keepdrops); + lprint(" %6d correct ACK header predictions\r\n", tcpstat.tcps_predack); + lprint(" %6d correct data packet header predictions\n", tcpstat.tcps_preddat); + lprint(" %6d TCP cache misses\r\n", tcpstat.tcps_socachemiss); + + +/* lprint(" Packets received too short: %d\r\n", tcpstat.tcps_rcvshort); */ +/* lprint(" Segments dropped due to PAWS: %d\r\n", tcpstat.tcps_pawsdrop); */ + +} + +void +udpstats() +{ + lprint(" \r\n"); + + lprint("UDP stats:\r\n"); + lprint(" %6d datagrams received\r\n", udpstat.udps_ipackets); + lprint(" %6d with packets shorter than header\r\n", udpstat.udps_hdrops); + lprint(" %6d with bad checksums\r\n", udpstat.udps_badsum); + lprint(" %6d with data length larger than packet\r\n", udpstat.udps_badlen); + lprint(" %6d UDP socket cache misses\r\n", udpstat.udpps_pcbcachemiss); + lprint(" %6d datagrams sent\r\n", udpstat.udps_opackets); +} + +void +icmpstats() +{ + lprint(" \r\n"); + lprint("ICMP stats:\r\n"); + lprint(" %6d ICMP packets received\r\n", icmpstat.icps_received); + lprint(" %6d were too short\r\n", icmpstat.icps_tooshort); + lprint(" %6d with bad checksums\r\n", icmpstat.icps_checksum); + lprint(" %6d with type not supported\r\n", icmpstat.icps_notsupp); + lprint(" %6d with bad type feilds\r\n", icmpstat.icps_badtype); + lprint(" %6d ICMP packets sent in reply\r\n", icmpstat.icps_reflect); +} + +void +mbufstats() +{ + struct SLIRPmbuf *m; + int i; + + lprint(" \r\n"); + + lprint("Mbuf stats:\r\n"); + + lprint(" %6d mbufs allocated (%d max)\r\n", mbuf_alloced, mbuf_max); + + i = 0; + for (m = m_freelist.m_next; m != &m_freelist; m = m->m_next) + i++; + lprint(" %6d mbufs on free list\r\n", i); + + i = 0; + for (m = m_usedlist.m_next; m != &m_usedlist; m = m->m_next) + i++; + lprint(" %6d mbufs on used list\r\n", i); + lprint(" %6d mbufs queued as packets\r\n\r\n", if_queued); +} + + +void sockstats(void) +{ + char buff[256]; + int n; + struct SLIRPsocket *so; + + lprint(" \r\n"); + + lprint( + "Proto[state] Sock Local Address, Port Remote Address, Port RecvQ SendQ\r\n"); + + for (so = tcb.so_next; so != &tcb; so = so->so_next) { + + n = sprintf(buff, "tcp[%s]", so->so_tcpcb?tcpstates[so->so_tcpcb->t_state]:"NONE"); + while (n < 17) + buff[n++] = ' '; + buff[17] = 0; + lprint("%s %3d %15s %5d ", + buff, so->s, + inet_ntoa(so->so_laddr), ntohs(so->so_lport)); + lprint("%15s %5d %5d %5d\r\n", + inet_ntoa(so->so_faddr), ntohs(so->so_fport), + so->so_rcv.sb_cc, so->so_snd.sb_cc); + + } + + for (so = udb.so_next; so != &udb; so = so->so_next) { + + n = sprintf(buff, "udp[%d sec]", (so->so_expire - curtime) / 1000); + while (n < 17) + buff[n++] = ' '; + buff[17] = 0; + lprint("%s %3d %15s %5d ", + buff, so->s, + inet_ntoa(so->so_laddr), ntohs(so->so_lport)); + lprint("%15s %5d %5d %5d\r\n", + inet_ntoa(so->so_faddr), ntohs(so->so_fport), + so->so_rcv.sb_cc, so->so_snd.sb_cc); + } +} + + + +void printf_sockstats(void) +{ + char buff[256]; + int n; + struct SLIRPsocket *so; + + printf(" \r\n"); + + printf( + "Proto[state] Sock Local Address, Port Remote Address, Port RecvQ SendQ\r\n"); + + for (so = tcb.so_next; so != &tcb; so = so->so_next) { + + n = sprintf(buff, "tcp[%s]", so->so_tcpcb?tcpstates[so->so_tcpcb->t_state]:"NONE"); + while (n < 17) + buff[n++] = ' '; + buff[17] = 0; + printf("%s %3d %15s %5d ", + buff, so->s, + inet_ntoa(so->so_laddr), ntohs(so->so_lport)); + printf("%15s %5d %5d %5d\r\n", + inet_ntoa(so->so_faddr), ntohs(so->so_fport), + so->so_rcv.sb_cc, so->so_snd.sb_cc); + + } + + for (so = udb.so_next; so != &udb; so = so->so_next) { + + n = sprintf(buff, "udp[%d sec]", (so->so_expire - curtime) / 1000); + while (n < 17) + buff[n++] = ' '; + buff[17] = 0; + printf("%s %3d %15s %5d ", + buff, so->s, + inet_ntoa(so->so_laddr), ntohs(so->so_lport)); + printf("%15s %5d %5d %5d\r\n", + inet_ntoa(so->so_faddr), ntohs(so->so_fport), + so->so_rcv.sb_cc, so->so_snd.sb_cc); + } +printf("\n\n"); +} + +//Simple code to purge and close open sockets. +//This way we can open/close/open/close.. +void purgesocks(void) +{ + struct SLIRPsocket *so; + + for (so = tcb.so_next; so != &tcb; so = so->so_next) { + + closesocket(so->s); //close the socket + } +} + +#if 1 +void +slirp_exit(exit_status) + int exit_status; +{ +// struct ttys *ttyp; + + DEBUG_CALL("slirp_exit"); + DEBUG_ARG("exit_status = %d", exit_status); + + if (dostats) { + lprint_print = (int (*) _P((void *, const char *, va_list)))vfprintf; + if (!dfd) + debug_init("slirp_stats", 0xf); + lprint_arg = (char **)&dfd; + + ipstats(); + tcpstats(); + udpstats(); + icmpstats(); + mbufstats(); + sockstats(); + fclose(dfd); +// allttystats(); +// vjstats(); + } + +// for (ttyp = ttys; ttyp; ttyp = ttyp->next) +// tty_detached(ttyp, 1); + +// if (slirp_forked) { +// /* Menendez time */ +// if (kill(getppid(), SIGQUIT) < 0) +// lprint("Couldn't kill parent process %ld!\n", +// (long) getppid()); +// } + + /* Restore the terminal if we gotta */ +// if(slirp_tty_restore) +// tcsetattr(0,TCSANOW, &slirp_tty_settings); /* NOW DAMMIT! */ +// exit(exit_status); + + //This will iterate though the sockets, and close them all (think redirects) + //PCem will have SLiRP open, close several times, which trips up SLiRP + //So for now I go through the sockets and close them + purgesocks(); + +} +#endif diff --git a/src/slirp/debug.h b/src/slirp/debug.h new file mode 100644 index 0000000..7b96c1a --- /dev/null +++ b/src/slirp/debug.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 1995 Danny Gasparovski. + * + * Please read the file COPYRIGHT for the + * terms and conditions of the copyright. + */ + +#define PRN_STDERR 1 +#define PRN_SPRINTF 2 + +extern FILE *dfd; +extern FILE *lfd; +extern int dostats; +extern int slirp_debug; + +#define DBG_CALL 0x1 +#define DBG_MISC 0x2 +#define DBG_ERROR 0x4 +#define DEBUG_DEFAULT DBG_CALL|DBG_MISC|DBG_ERROR + +#ifdef SLIRP_DEBUG +#define DEBUG_CALL(x) if (slirp_debug & DBG_CALL) { fprintf(dfd, "%s...\n", x); fflush(dfd); } +#define DEBUG_ARG(x, y) if (slirp_debug & DBG_CALL) { fputc(' ', dfd); fprintf(dfd, x, y); fputc('\n', dfd); fflush(dfd); } +#define DEBUG_ARGS(x) if (slirp_debug & DBG_CALL) { fprintf x ; fflush(dfd); } +#define DEBUG_MISC(x) if (slirp_debug & DBG_MISC) { fprintf x ; fflush(dfd); } +#define DEBUG_ERROR(x) if (slirp_debug & DBG_ERROR) {fprintf x ; fflush(dfd); } + + +#else + +#define DEBUG_CALL(x) +#define DEBUG_ARG(x, y) +#define DEBUG_ARGS(x) +#define DEBUG_MISC(x) +#define DEBUG_ERROR(x) + +#endif + +void debug_init _P((char *, int)); +//void ttystats _P((struct ttys *)); +void allttystats _P((void)); +void ipstats _P((void)); +void vjstats _P((void)); +void tcpstats _P((void)); +void udpstats _P((void)); +void icmpstats _P((void)); +void mbufstats _P((void)); +void sockstats _P((void)); +void slirp_exit _P((int)); + diff --git a/src/slirp/icmp_var.h b/src/slirp/icmp_var.h new file mode 100644 index 0000000..8bacc63 --- /dev/null +++ b/src/slirp/icmp_var.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 1982, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)icmp_var.h 8.1 (Berkeley) 6/10/93 + * icmp_var.h,v 1.4 1995/02/16 00:27:40 wollman Exp + */ + +#ifndef _NETINET_ICMP_VAR_H_ +#define _NETINET_ICMP_VAR_H_ + +/* + * Variables related to this implementation + * of the internet control message protocol. + */ +struct icmpstat { +/* statistics related to input messages processed */ + u_long icps_received; /* #ICMP packets received */ + u_long icps_tooshort; /* packet < ICMP_MINLEN */ + u_long icps_checksum; /* bad checksum */ + u_long icps_notsupp; /* #ICMP packets not supported */ + u_long icps_badtype; /* #with bad type feild */ + u_long icps_reflect; /* number of responses */ +}; + +/* + * Names for ICMP sysctl objects + */ +#define ICMPCTL_MASKREPL 1 /* allow replies to netmask requests */ +#define ICMPCTL_STATS 2 /* statistics (read-only) */ +#define ICMPCTL_MAXID 3 + +#define ICMPCTL_NAMES { \ + { 0, 0 }, \ + { "maskrepl", CTLTYPE_INT }, \ + { "stats", CTLTYPE_STRUCT }, \ +} + +extern struct icmpstat icmpstat; + +#endif diff --git a/src/slirp/if.c b/src/slirp/if.c new file mode 100644 index 0000000..aa0cea9 --- /dev/null +++ b/src/slirp/if.c @@ -0,0 +1,322 @@ +/* + * Copyright (c) 1995 Danny Gasparovski. + * + * Please read the file COPYRIGHT for the + * terms and conditions of the copyright. + */ + +#include "slirp.h" + +int if_mtu, if_mru; +int if_comp; +int if_maxlinkhdr; +int if_queued = 0; /* Number of packets queued so far */ +int if_thresh = 10; /* Number of packets queued before we start sending + * (to prevent allocing too many SLIRPmbufs) */ + +struct SLIRPmbuf if_fastq; /* fast queue (for interactive data) */ +struct SLIRPmbuf if_batchq; /* queue for non-interactive data */ +struct SLIRPmbuf *next_m; /* Pointer to next SLIRPmbuf to output */ + +#define ifs_init(ifm) ((ifm)->ifs_next = (ifm)->ifs_prev = (ifm)) + +void +ifs_insque(ifm, ifmhead) + struct SLIRPmbuf *ifm, *ifmhead; +{ + ifm->ifs_next = ifmhead->ifs_next; + ifmhead->ifs_next = ifm; + ifm->ifs_prev = ifmhead; + ifm->ifs_next->ifs_prev = ifm; +} + +void +ifs_remque(ifm) + struct SLIRPmbuf *ifm; +{ + ifm->ifs_prev->ifs_next = ifm->ifs_next; + ifm->ifs_next->ifs_prev = ifm->ifs_prev; +} + +void +if_init() +{ +#if 0 + /* + * Set if_maxlinkhdr to 48 because it's 40 bytes for TCP/IP, + * and 8 bytes for PPP, but need to have it on an 8byte boundary + */ +#ifdef USE_PPP + if_maxlinkhdr = 48; +#else + if_maxlinkhdr = 40; +#endif +#else + /* 2 for alignment, 14 for ethernet, 40 for TCP/IP */ + if_maxlinkhdr = 2 + 14 + 40; +#endif + if_mtu = 1500; + if_mru = 1500; + if_comp = IF_AUTOCOMP; + if_fastq.ifq_next = if_fastq.ifq_prev = &if_fastq; + if_batchq.ifq_next = if_batchq.ifq_prev = &if_batchq; + // sl_compress_init(&comp_s); + next_m = &if_batchq; +} + +#if 0 +/* + * This shouldn't be needed since the modem is blocking and + * we don't expect any signals, but what the hell.. + */ +inline int +writen(fd, bptr, n) + int fd; + char *bptr; + int n; +{ + int ret; + int total; + + /* This should succeed most of the time */ + ret = send(fd, bptr, n,0); + if (ret == n || ret <= 0) + return ret; + + /* Didn't write everything, go into the loop */ + total = ret; + while (n > total) { + ret = send(fd, bptr+total, n-total,0); + if (ret <= 0) + return ret; + total += ret; + } + return total; +} + +/* + * if_input - read() the tty, do "top level" processing (ie: check for any escapes), + * and pass onto (*ttyp->if_input) + * + * XXXXX Any zeros arriving by themselves are NOT placed into the arriving packet. + */ +#define INBUFF_SIZE 2048 /* XXX */ +void +if_input(ttyp) + struct ttys *ttyp; +{ + u_char if_inbuff[INBUFF_SIZE]; + int if_n; + + DEBUG_CALL("if_input"); + DEBUG_ARG("ttyp = %lx", (long)ttyp); + + if_n = recv(ttyp->fd, (char *)if_inbuff, INBUFF_SIZE,0); + + DEBUG_MISC((dfd, " read %d bytes\n", if_n)); + + if (if_n <= 0) { + if (if_n == 0 || (errno != EINTR && errno != EAGAIN)) { + if (ttyp->up) + link_up--; + tty_detached(ttyp, 0); + } + return; + } + if (if_n == 1) { + if (*if_inbuff == '0') { + ttyp->ones = 0; + if (++ttyp->zeros >= 5) + slirp_exit(0); + return; + } + if (*if_inbuff == '1') { + ttyp->zeros = 0; + if (++ttyp->ones >= 5) + tty_detached(ttyp, 0); + return; + } + } + ttyp->ones = ttyp->zeros = 0; + + (*ttyp->if_input)(ttyp, if_inbuff, if_n); +} +#endif + +/* + * if_output: Queue packet into an output queue. + * There are 2 output queue's, if_fastq and if_batchq. + * Each output queue is a doubly linked list of double linked lists + * of SLIRPmbufs, each list belonging to one "session" (socket). This + * way, we can output packets fairly by sending one packet from each + * session, instead of all the packets from one session, then all packets + * from the next session, etc. Packets on the if_fastq get absolute + * priority, but if one session hogs the link, it gets "downgraded" + * to the batchq until it runs out of packets, then it'll return + * to the fastq (eg. if the user does an ls -alR in a telnet session, + * it'll temporarily get downgraded to the batchq) + */ +void +if_output(so, ifm) + struct SLIRPsocket *so; + struct SLIRPmbuf *ifm; +{ + struct SLIRPmbuf *ifq; + int on_fastq = 1; + + DEBUG_CALL("if_output"); + DEBUG_ARG("so = %lx", (long)so); + DEBUG_ARG("ifm = %lx", (long)ifm); + + /* + * First remove the SLIRPmbuf from m_usedlist, + * since we're gonna use m_next and m_prev ourselves + * XXX Shouldn't need this, gotta change dtom() etc. + */ + if (ifm->m_flags & M_USEDLIST) { + remque(ifm); + ifm->m_flags &= ~M_USEDLIST; + } + + /* + * See if there's already a batchq list for this session. + * This can include an interactive session, which should go on fastq, + * but gets too greedy... hence it'll be downgraded from fastq to batchq. + * We mustn't put this packet back on the fastq (or we'll send it out of order) + * XXX add cache here? + */ + for (ifq = if_batchq.ifq_prev; ifq != &if_batchq; ifq = ifq->ifq_prev) { + if (so == ifq->ifq_so) { + /* A match! */ + ifm->ifq_so = so; + ifs_insque(ifm, ifq->ifs_prev); + goto diddit; + } + } + + /* No match, check which queue to put it on */ + if (so && (so->so_iptos & IPTOS_LOWDELAY)) { + ifq = if_fastq.ifq_prev; + on_fastq = 1; + /* + * Check if this packet is a part of the last + * packet's session + */ + if (ifq->ifq_so == so) { + ifm->ifq_so = so; + ifs_insque(ifm, ifq->ifs_prev); + goto diddit; + } + } else + ifq = if_batchq.ifq_prev; + + /* Create a new doubly linked list for this session */ + ifm->ifq_so = so; + ifs_init(ifm); + insque(ifm, ifq); + +diddit: + ++if_queued; + + if (so) { + /* Update *_queued */ + so->so_queued++; + so->so_nqueued++; + /* + * Check if the interactive session should be downgraded to + * the batchq. A session is downgraded if it has queued 6 + * packets without pausing, and at least 3 of those packets + * have been sent over the link + * (XXX These are arbitrary numbers, probably not optimal..) + */ + if (on_fastq && ((so->so_nqueued >= 6) && + (so->so_nqueued - so->so_queued) >= 3)) { + + /* Remove from current queue... */ + remque(ifm->ifs_next); + + /* ...And insert in the new. That'll teach ya! */ + insque(ifm->ifs_next, &if_batchq); + } + } + +#ifndef FULL_BOLT + /* + * This prevents us from malloc()ing too many SLIRPmbufs + */ + if (link_up) { + /* if_start will check towrite */ + if_start(); + } +#endif +} + +/* + * Send a packet + * We choose a packet based on it's position in the output queues; + * If there are packets on the fastq, they are sent FIFO, before + * everything else. Otherwise we choose the first packet from the + * batchq and send it. the next packet chosen will be from the session + * after this one, then the session after that one, and so on.. So, + * for example, if there are 3 ftp session's fighting for bandwidth, + * one packet will be sent from the first session, then one packet + * from the second session, then one packet from the third, then back + * to the first, etc. etc. + */ +void +if_start(void) +{ + struct SLIRPmbuf *ifm, *ifqt; + + DEBUG_CALL("if_start"); + + if (if_queued == 0) + return; /* Nothing to do */ + + again: + /* check if we can really output */ + if (!slirp_can_output()) + return; + + /* + * See which queue to get next packet from + * If there's something in the fastq, select it immediately + */ + if (if_fastq.ifq_next != &if_fastq) { + ifm = if_fastq.ifq_next; + } else { + /* Nothing on fastq, see if next_m is valid */ + if (next_m != &if_batchq) + ifm = next_m; + else + ifm = if_batchq.ifq_next; + + /* Set which packet to send on next iteration */ + next_m = ifm->ifq_next; + } + /* Remove it from the queue */ + ifqt = ifm->ifq_prev; + remque(ifm); + --if_queued; + + /* If there are more packets for this session, re-queue them */ + if (ifm->ifs_next != /* ifm->ifs_prev != */ ifm) { + insque(ifm->ifs_next, ifqt); + ifs_remque(ifm); + } + + /* Update so_queued */ + if (ifm->ifq_so) { + if (--ifm->ifq_so->so_queued == 0) + /* If there's no more queued, reset nqueued */ + ifm->ifq_so->so_nqueued = 0; + } + + /* Encapsulate the packet for sending */ + if_encap((uint8_t*)ifm->m_data, ifm->m_len); + + m_free(ifm); + + if (if_queued) + goto again; +} diff --git a/src/slirp/if.h b/src/slirp/if.h new file mode 100644 index 0000000..4f39951 --- /dev/null +++ b/src/slirp/if.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 1995 Danny Gasparovski. + * + * Please read the file COPYRIGHT for the + * terms and conditions of the copyright. + */ + +#ifndef _IF_H_ +#define _IF_H_ + +#define IF_COMPRESS 0x01 /* We want compression */ +#define IF_NOCOMPRESS 0x02 /* Do not do compression */ +#define IF_AUTOCOMP 0x04 /* Autodetect (default) */ +#define IF_NOCIDCOMP 0x08 /* CID compression */ + +/* Needed for FreeBSD */ +#undef if_mtu +extern int if_mtu; +extern int if_mru; /* MTU and MRU */ +extern int if_comp; /* Flags for compression */ +extern int if_maxlinkhdr; +extern int if_queued; /* Number of packets queued so far */ +extern int if_thresh; /* Number of packets queued before we start sending + * (to prevent allocing too many SLIRPmbufs) */ + +extern struct SLIRPmbuf if_fastq; /* fast queue (for interactive data) */ +extern struct SLIRPmbuf if_batchq; /* queue for non-interactive data */ +extern struct SLIRPmbuf *next_m; + +#define ifs_init(ifm) ((ifm)->ifs_next = (ifm)->ifs_prev = (ifm)) + +/* Interface statistics */ +struct slirp_ifstats { + u_int out_pkts; /* Output packets */ + u_int out_bytes; /* Output bytes */ + u_int out_errpkts; /* Output Error Packets */ + u_int out_errbytes; /* Output Error Bytes */ + u_int in_pkts; /* Input packets */ + u_int in_bytes; /* Input bytes */ + u_int in_errpkts; /* Input Error Packets */ + u_int in_errbytes; /* Input Error Bytes */ + + u_int bytes_saved; /* Number of bytes that compression "saved" */ + /* ie: number of bytes that didn't need to be sent over the link + * because of compression */ + + u_int in_mbad; /* Bad incoming packets */ +}; + +#endif diff --git a/src/slirp/ip.h b/src/slirp/ip.h new file mode 100644 index 0000000..f7fc59f --- /dev/null +++ b/src/slirp/ip.h @@ -0,0 +1,341 @@ +/* + * Copyright (c) 1982, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)ip.h 8.1 (Berkeley) 6/10/93 + * ip.h,v 1.3 1994/08/21 05:27:30 paul Exp + */ + +#ifndef _IP_H_ +#define _IP_H_ + +#ifdef WORDS_BIGENDIAN +# ifndef NTOHL +# define NTOHL(d) +# endif +# ifndef NTOHS +# define NTOHS(d) +# endif +# ifndef HTONL +# define HTONL(d) +# endif +# ifndef HTONS +# define HTONS(d) +# endif +#else +# ifndef NTOHL +# define NTOHL(d) ((d) = ntohl((d))) +# endif +# ifndef NTOHS +# define NTOHS(d) ((d) = ntohs((u_int16_t)(d))) +# endif +# ifndef HTONL +# define HTONL(d) ((d) = htonl((d))) +# endif +# ifndef HTONS +# define HTONS(d) ((d) = htons((u_int16_t)(d))) +# endif +#endif + +typedef u_int32_t n_long; /* long as received from the net */ + +/* + * Definitions for internet protocol version 4. + * Per RFC 791, September 1981. + */ +#define IPVERSION 4 + +#if defined(_MSC_VER) +#pragma pack(push, 1) +#endif + +/* + * Structure of an internet header, naked of options. + */ +#ifdef PRAGMA_PACK_SUPPORTED +#pragma pack(1) +#endif + +struct ip { +#ifdef WORDS_BIGENDIAN + u_char ip_v:4, /* version */ + ip_hl:4; /* header length */ +#else + u_char ip_hl:4, /* header length */ + ip_v:4; /* version */ +#endif + u_int8_t ip_tos; /* type of service */ + u_int16_t ip_len; /* total length */ + u_int16_t ip_id; /* identification */ + u_int16_t ip_off; /* fragment offset field */ +#define IP_DF 0x4000 /* don't fragment flag */ +#define IP_MF 0x2000 /* more fragments flag */ +#define IP_OFFMASK 0x1fff /* mask for fragmenting bits */ + u_int8_t ip_ttl; /* time to live */ + u_int8_t ip_p; /* protocol */ + u_int16_t ip_sum; /* checksum */ + struct in_addr ip_src,ip_dst; /* source and dest address */ +} PACKED__; + +#ifdef PRAGMA_PACK_SUPPORTED +#pragma pack(PACK_END) //WAS 0 +#endif + +#define IP_MAXPACKET 65535 /* maximum packet size */ + +/* + * Definitions for IP type of service (ip_tos) + */ +#define IPTOS_LOWDELAY 0x10 +#define IPTOS_THROUGHPUT 0x08 +#define IPTOS_RELIABILITY 0x04 + +/* + * Definitions for options. + */ +#define IPOPT_COPIED(o) ((o)&0x80) +#define IPOPT_CLASS(o) ((o)&0x60) +#define IPOPT_NUMBER(o) ((o)&0x1f) + +#define IPOPT_CONTROL 0x00 +#define IPOPT_RESERVED1 0x20 +#define IPOPT_DEBMEAS 0x40 +#define IPOPT_RESERVED2 0x60 + +#define IPOPT_EOL 0 /* end of option list */ +#define IPOPT_NOP 1 /* no operation */ + +#define IPOPT_RR 7 /* record packet route */ +#define IPOPT_TS 68 /* timestamp */ +#define IPOPT_SECURITY 130 /* provide s,c,h,tcc */ +#define IPOPT_LSRR 131 /* loose source route */ +#define IPOPT_SATID 136 /* satnet id */ +#define IPOPT_SSRR 137 /* strict source route */ + +/* + * Offsets to fields in options other than EOL and NOP. + */ +#define IPOPT_OPTVAL 0 /* option ID */ +#define IPOPT_OLEN 1 /* option length */ +#define IPOPT_OFFSET 2 /* offset within option */ +#define IPOPT_MINOFF 4 /* min value of above */ + +/* + * Time stamp option structure. + */ +#ifdef PRAGMA_PACK_SUPPORTED +#pragma pack(1) +#endif + +struct ip_timestamp { + u_int8_t ipt_code; /* IPOPT_TS */ + u_int8_t ipt_len; /* size of structure (variable) */ + u_int8_t ipt_ptr; /* index of current entry */ +#ifdef WORDS_BIGENDIAN + u_char ipt_oflw:4, /* overflow counter */ + ipt_flg:4; /* flags, see below */ +#else + u_char ipt_flg:4, /* flags, see below */ + ipt_oflw:4; /* overflow counter */ +#endif + union ipt_timestamp { + n_long ipt_time[1]; + struct ipt_ta { + struct in_addr ipt_addr; + n_long ipt_time; + } ipt_ta[1]; + } ipt_timestamp; +} PACKED__; + +#ifdef PRAGMA_PACK_SUPPORTED +#pragma pack(PACK_END) +#endif + +/* flag bits for ipt_flg */ +#define IPOPT_TS_TSONLY 0 /* timestamps only */ +#define IPOPT_TS_TSANDADDR 1 /* timestamps and addresses */ +#define IPOPT_TS_PRESPEC 3 /* specified modules only */ + +/* bits for security (not byte swapped) */ +#define IPOPT_SECUR_UNCLASS 0x0000 +#define IPOPT_SECUR_CONFID 0xf135 +#define IPOPT_SECUR_EFTO 0x789a +#define IPOPT_SECUR_MMMM 0xbc4d +#define IPOPT_SECUR_RESTR 0xaf13 +#define IPOPT_SECUR_SECRET 0xd788 +#define IPOPT_SECUR_TOPSECRET 0x6bc5 + +/* + * Internet implementation parameters. + */ +#define MAXTTL 255 /* maximum time to live (seconds) */ +#define IPDEFTTL 64 /* default ttl, from RFC 1340 */ +#define IPFRAGTTL 60 /* time to live for frags, slowhz */ +#define IPTTLDEC 1 /* subtracted when forwarding */ + +#define IP_MSS 576 /* default maximum segment size */ + +#ifdef HAVE_SYS_TYPES32_H /* Overcome some Solaris 2.x junk */ +#include +#else +#if SIZEOF_CHAR_P == 4 +typedef SLIRPcaddr_t caddr32_t; +#else +typedef u_int32_t caddr32_t; +#endif +#endif + +#if SIZEOF_CHAR_P == 4 +typedef struct ipq *ipqp_32; +typedef struct ipasfrag *ipasfragp_32; +#else +typedef caddr32_t ipqp_32; +typedef caddr32_t ipasfragp_32; +#endif + +/* + * Overlay for ip header used by other protocols (tcp, udp). + */ +#ifdef PRAGMA_PACK_SUPPORTED +#pragma pack(1) +#endif + +struct ipovly { + caddr32_t ih_next, ih_prev; /* for protocol sequence q's */ + u_int8_t ih_x1; /* (unused) */ + u_int8_t ih_pr; /* protocol */ + u_int16_t ih_len; /* protocol length */ + struct in_addr ih_src; /* source internet address */ + struct in_addr ih_dst; /* destination internet address */ +} PACKED__; + +#ifdef PRAGMA_PACK_SUPPORTED +#pragma pack(PACK_END) +#endif + +#if defined(_MSC_VER) +#pragma pack(pop) +#endif + +/* + * Ip reassembly queue structure. Each fragment + * being reassembled is attached to one of these structures. + * They are timed out after ipq_ttl drops to 0, and may also + * be reclaimed if memory becomes tight. + * size 28 bytes + */ +struct ipq { + ipqp_32 next,prev; /* to other reass headers */ + u_int8_t ipq_ttl; /* time for reass q to live */ + u_int8_t ipq_p; /* protocol of this fragment */ + u_int16_t ipq_id; /* sequence id for reassembly */ + ipasfragp_32 ipq_next,ipq_prev; + /* to ip headers of fragments */ + struct in_addr ipq_src,ipq_dst; +}; + +/* + * Ip header, when holding a fragment. + * + * Note: ipf_next must be at same offset as ipq_next above + */ +struct ipasfrag { +#ifdef WORDS_BIGENDIAN + u_char ip_v:4, + ip_hl:4; +#else + u_char ip_hl:4, + ip_v:4; +#endif + /* BUG : u_int changed to u_int8_t. + * sizeof(u_int)==4 on linux 2.0 + */ + u_int8_t ipf_mff; /* XXX overlays ip_tos: use low bit + * to avoid destroying tos (PPPDTRuu); + * copied from (ip_off&IP_MF) */ + u_int16_t ip_len; + u_int16_t ip_id; + u_int16_t ip_off; + u_int8_t ip_ttl; + u_int8_t ip_p; + u_int16_t ip_sum; + ipasfragp_32 ipf_next; /* next fragment */ + ipasfragp_32 ipf_prev; /* previous fragment */ +}; + +/* + * Structure stored in mbuf in inpcb.ip_options + * and passed to ip_output when ip options are in use. + * The actual length of the options (including ipopt_dst) + * is in m_len. + */ +#define MAX_IPOPTLEN 40 + +struct ipoption { + struct in_addr ipopt_dst; /* first-hop dst if source routed */ + int8_t ipopt_list[MAX_IPOPTLEN]; /* options proper */ +}; + +/* + * Structure attached to inpcb.ip_moptions and + * passed to ip_output when IP multicast options are in use. + */ + +struct ipstat { + u_long ips_total; /* total packets received */ + u_long ips_badsum; /* checksum bad */ + u_long ips_tooshort; /* packet too short */ + u_long ips_toosmall; /* not enough data */ + u_long ips_badhlen; /* ip header length < data size */ + u_long ips_badlen; /* ip length < ip header length */ + u_long ips_fragments; /* fragments received */ + u_long ips_fragdropped; /* frags dropped (dups, out of space) */ + u_long ips_fragtimeout; /* fragments timed out */ + u_long ips_forward; /* packets forwarded */ + u_long ips_cantforward; /* packets rcvd for unreachable dest */ + u_long ips_redirectsent; /* packets forwarded on same net */ + u_long ips_noproto; /* unknown or unsupported protocol */ + u_long ips_delivered; /* datagrams delivered to upper level*/ + u_long ips_localout; /* total ip packets generated here */ + u_long ips_odropped; /* lost packets due to nobufs, etc. */ + u_long ips_reassembled; /* total packets reassembled ok */ + u_long ips_fragmented; /* datagrams successfully fragmented */ + u_long ips_ofragments; /* output fragments created */ + u_long ips_cantfrag; /* don't fragment flag was set, etc. */ + u_long ips_badoptions; /* error in option processing */ + u_long ips_noroute; /* packets discarded due to no route */ + u_long ips_badvers; /* ip version != 4 */ + u_long ips_rawout; /* total raw ip packets generated */ + u_long ips_unaligned; /* times the ip packet was not aligned */ +}; + +extern struct ipstat ipstat; +extern struct ipq ipq; /* ip reass. queue */ +extern u_int16_t ip_id; /* ip packet ctr, for ids */ +extern int ip_defttl; /* default IP ttl */ + +#endif diff --git a/src/slirp/ip_icmp.c b/src/slirp/ip_icmp.c new file mode 100644 index 0000000..396293d --- /dev/null +++ b/src/slirp/ip_icmp.c @@ -0,0 +1,371 @@ +/* + * Copyright (c) 1982, 1986, 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)ip_icmp.c 8.2 (Berkeley) 1/4/94 + * ip_icmp.c,v 1.7 1995/05/30 08:09:42 rgrimes Exp + */ + +#include "slirp.h" +#include "ip_icmp.h" + +struct icmpstat icmpstat; + +/* The message sent when emulating PING */ +/* Be nice and tell them it's just a psuedo-ping packet */ +char icmp_ping_msg[] = "This is a psuedo-PING packet used by Slirp to emulate ICMP ECHO-REQUEST packets.\n"; + +/* list of actions for icmp_error() on RX of an icmp message */ +static int icmp_flush[19] = { +/* ECHO REPLY (0) */ 0, + 1, + 1, +/* DEST UNREACH (3) */ 1, +/* SOURCE QUENCH (4)*/ 1, +/* REDIRECT (5) */ 1, + 1, + 1, +/* ECHO (8) */ 0, +/* ROUTERADVERT (9) */ 1, +/* ROUTERSOLICIT (10) */ 1, +/* TIME EXCEEDED (11) */ 1, +/* PARAMETER PROBLEM (12) */ 1, +/* TIMESTAMP (13) */ 0, +/* TIMESTAMP REPLY (14) */ 0, +/* INFO (15) */ 0, +/* INFO REPLY (16) */ 0, +/* ADDR MASK (17) */ 0, +/* ADDR MASK REPLY (18) */ 0 +}; + +/* + * Process a received ICMP message. + */ +void +icmp_input(m, hlen) + struct SLIRPmbuf *m; + int hlen; +{ + register struct icmp *icp; + register struct ip *ip=mtod(m, struct ip *); + int icmplen=ip->ip_len; + /* int code; */ + + DEBUG_CALL("icmp_input"); + DEBUG_ARG("m = %lx", (long )m); + DEBUG_ARG("m_len = %d", m->m_len); + + icmpstat.icps_received++; + + /* + * Locate icmp structure in SLIRPmbuf, and check + * that its not corrupted and of at least minimum length. + */ + if (icmplen < ICMP_MINLEN) { /* min 8 bytes payload */ + icmpstat.icps_tooshort++; + freeit: + m_freem(m); + goto end_error; + } + + m->m_len -= hlen; + m->m_data += hlen; + icp = mtod(m, struct icmp *); + if (cksum(m, icmplen)) { + icmpstat.icps_checksum++; + goto freeit; + } + m->m_len += hlen; + m->m_data -= hlen; + + /* icmpstat.icps_inhist[icp->icmp_type]++; */ + /* code = icp->icmp_code; */ + + DEBUG_ARG("icmp_type = %d", icp->icmp_type); + switch (icp->icmp_type) { + case ICMP_ECHO: + icp->icmp_type = ICMP_ECHOREPLY; + ip->ip_len += hlen; /* since ip_input subtracts this */ + if (ip->ip_dst.s_addr == alias_addr.s_addr) { + icmp_reflect(m); + } else { + struct SLIRPsocket *so; + struct sockaddr_in addr; + if ((so = socreate()) == NULL) goto freeit; + if(udp_attach(so) == -1) { + DEBUG_MISC((dfd,"icmp_input udp_attach errno = %d-%s\n", + errno,strerror(errno))); + sofree(so); + m_free(m); + goto end_error; + } + so->so_m = m; + so->so_faddr = ip->ip_dst; + so->so_fport = htons(7); + so->so_laddr = ip->ip_src; + so->so_lport = htons(9); + so->so_iptos = ip->ip_tos; + so->so_type = IPPROTO_ICMP; + so->so_state = SS_ISFCONNECTED; + + /* Send the packet */ + addr.sin_family = AF_INET; + if ((so->so_faddr.s_addr & htonl(0xffffff00)) == special_addr.s_addr) { + /* It's an alias */ + switch(ntohl(so->so_faddr.s_addr) & 0xff) { + case CTL_DNS: + addr.sin_addr = dns_addr; + break; + case CTL_ALIAS: + default: + addr.sin_addr = loopback_addr; + break; + } + } else { + addr.sin_addr = so->so_faddr; + } + addr.sin_port = so->so_fport; + if(sendto(so->s, icmp_ping_msg, strlen(icmp_ping_msg), 0, + (struct sockaddr *)&addr, sizeof(addr)) == -1) { + DEBUG_MISC((dfd,"icmp_input udp sendto tx errno = %d-%s\n", + errno,strerror(errno))); + icmp_error(m, ICMP_UNREACH,ICMP_UNREACH_NET, 0,strerror(errno)); + udp_detach(so); + } + } /* if ip->ip_dst.s_addr == alias_addr.s_addr */ + break; + case ICMP_UNREACH: + /* XXX? report error? close socket? */ + case ICMP_TIMXCEED: + case ICMP_PARAMPROB: + case ICMP_SOURCEQUENCH: + case ICMP_TSTAMP: + case ICMP_MASKREQ: + case ICMP_REDIRECT: + icmpstat.icps_notsupp++; + m_freem(m); + break; + + default: + icmpstat.icps_badtype++; + m_freem(m); + } /* swith */ + +end_error: + /* m is m_free()'d xor put in a socket xor or given to ip_send */ + return; +} + + +/* + * Send an ICMP message in response to a situation + * + * RFC 1122: 3.2.2 MUST send at least the IP header and 8 bytes of header. MAY send more (we do). + * MUST NOT change this header information. + * MUST NOT reply to a multicast/broadcast IP address. + * MUST NOT reply to a multicast/broadcast MAC address. + * MUST reply to only the first fragment. + */ +/* + * Send ICMP_UNREACH back to the source regarding msrc. + * SLIRPmbuf *msrc is used as a template, but is NOT m_free()'d. + * It is reported as the bad ip packet. The header should + * be fully correct and in host byte order. + * ICMP fragmentation is illegal. All machines must accept 576 bytes in one + * packet. The maximum payload is 576-20(ip hdr)-8(icmp hdr)=548 + */ + +#define ICMP_MAXDATALEN (IP_MSS-28) +void +icmp_error(msrc, type, code, minsize, message) + struct SLIRPmbuf *msrc; + u_char type; + u_char code; + int minsize; + char *message; +{ + unsigned hlen, shlen, s_ip_len; + register struct ip *ip; + register struct icmp *icp; + register struct SLIRPmbuf *m; + + DEBUG_CALL("icmp_error"); + DEBUG_ARG("msrc = %lx", (long )msrc); + DEBUG_ARG("msrc_len = %d", msrc->m_len); + + if(type!=ICMP_UNREACH && type!=ICMP_TIMXCEED) goto end_error; + + /* check msrc */ + if(!msrc) goto end_error; + ip = mtod(msrc, struct ip *); +#if SLIRP_DEBUG + { char bufa[20], bufb[20]; + strcpy(bufa, inet_ntoa(ip->ip_src)); + strcpy(bufb, inet_ntoa(ip->ip_dst)); + DEBUG_MISC((dfd, " %.16s to %.16s\n", bufa, bufb)); + } +#endif + if(ip->ip_off & IP_OFFMASK) goto end_error; /* Only reply to fragment 0 */ + + shlen=ip->ip_hl << 2; + s_ip_len=ip->ip_len; + if(ip->ip_p == IPPROTO_ICMP) { + icp = (struct icmp *)((char *)ip + shlen); + /* + * Assume any unknown ICMP type is an error. This isn't + * specified by the RFC, but think about it.. + */ + if(icp->icmp_type>18 || icmp_flush[icp->icmp_type]) goto end_error; + } + + /* make a copy */ + if(!(m=m_get())) goto end_error; /* get SLIRPmbuf */ + { u_int new_m_size; + new_m_size=sizeof(struct ip )+ICMP_MINLEN+msrc->m_len+ICMP_MAXDATALEN; + if(new_m_size>m->m_size) m_inc(m, new_m_size); + } + memcpy(m->m_data, msrc->m_data, msrc->m_len); + m->m_len = msrc->m_len; /* copy msrc to m */ + + /* make the header of the reply packet */ + ip = mtod(m, struct ip *); + hlen= sizeof(struct ip ); /* no options in reply */ + + /* fill in icmp */ + m->m_data += hlen; + m->m_len -= hlen; + + icp = mtod(m, struct icmp *); + + if(minsize) s_ip_len=shlen+ICMP_MINLEN; /* return header+8b only */ + else if(s_ip_len>ICMP_MAXDATALEN) /* maximum size */ + s_ip_len=ICMP_MAXDATALEN; + + m->m_len=ICMP_MINLEN+s_ip_len; /* 8 bytes ICMP header */ + + /* min. size = 8+sizeof(struct ip)+8 */ + + icp->icmp_type = type; + icp->icmp_code = code; + icp->icmp_id = 0; + icp->icmp_seq = 0; + + memcpy(&icp->icmp_ip, msrc->m_data, s_ip_len); /* report the ip packet */ + HTONS(icp->icmp_ip.ip_len); + HTONS(icp->icmp_ip.ip_id); + HTONS(icp->icmp_ip.ip_off); + +#if SLIRP_DEBUG + if(message) { /* DEBUG : append message to ICMP packet */ + int message_len; + char *cpnt; + message_len=strlen(message); + if(message_len>ICMP_MAXDATALEN) message_len=ICMP_MAXDATALEN; + cpnt=(char *)m->m_data+m->m_len; + memcpy(cpnt, message, message_len); + m->m_len+=message_len; + } +#endif + + icp->icmp_cksum = 0; + icp->icmp_cksum = cksum(m, m->m_len); + + m->m_data -= hlen; + m->m_len += hlen; + + /* fill in ip */ + ip->ip_hl = hlen >> 2; + ip->ip_len = (u_int16_t)m->m_len; + + ip->ip_tos=((ip->ip_tos & 0x1E) | 0xC0); /* high priority for errors */ + + ip->ip_ttl = MAXTTL; + ip->ip_p = IPPROTO_ICMP; + ip->ip_dst = ip->ip_src; /* ip adresses */ + ip->ip_src = alias_addr; + + (void ) ip_output((struct SLIRPsocket *)NULL, m); + + icmpstat.icps_reflect++; + +end_error: + return; +} +#undef ICMP_MAXDATALEN + +/* + * Reflect the ip packet back to the source + */ +void +icmp_reflect(m) + struct SLIRPmbuf *m; +{ + register struct ip *ip = mtod(m, struct ip *); + int hlen = ip->ip_hl << 2; + int optlen = hlen - sizeof(struct ip ); + register struct icmp *icp; + + /* + * Send an icmp packet back to the ip level, + * after supplying a checksum. + */ + m->m_data += hlen; + m->m_len -= hlen; + icp = mtod(m, struct icmp *); + + icp->icmp_cksum = 0; + icp->icmp_cksum = cksum(m, ip->ip_len - hlen); + + m->m_data -= hlen; + m->m_len += hlen; + + /* fill in ip */ + if (optlen > 0) { + /* + * Strip out original options by copying rest of first + * SLIRPmbuf's data back, and adjust the IP length. + */ + memmove((SLIRPcaddr_t)(ip + 1), (SLIRPcaddr_t)ip + hlen, + (unsigned )(m->m_len - hlen)); + hlen -= optlen; + ip->ip_hl = hlen >> 2; + ip->ip_len -= optlen; + m->m_len -= optlen; + } + + ip->ip_ttl = MAXTTL; + { /* swap */ + struct in_addr icmp_dst; + icmp_dst = ip->ip_dst; + ip->ip_dst = ip->ip_src; + ip->ip_src = icmp_dst; + } + + (void ) ip_output((struct SLIRPsocket *)NULL, m); + + icmpstat.icps_reflect++; +} diff --git a/src/slirp/ip_icmp.h b/src/slirp/ip_icmp.h new file mode 100644 index 0000000..2de52a1 --- /dev/null +++ b/src/slirp/ip_icmp.h @@ -0,0 +1,168 @@ +/* + * Copyright (c) 1982, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)ip_icmp.h 8.1 (Berkeley) 6/10/93 + * ip_icmp.h,v 1.4 1995/05/30 08:09:43 rgrimes Exp + */ + +#ifndef _NETINET_IP_ICMP_H_ +#define _NETINET_IP_ICMP_H_ + +/* + * Interface Control Message Protocol Definitions. + * Per RFC 792, September 1981. + */ + +typedef u_int32_t n_time; + +/* + * Structure of an icmp header. + */ +#ifdef PRAGMA_PACK_SUPPORTED +#pragma pack(1) +#endif + +struct icmp { + u_char icmp_type; /* type of message, see below */ + u_char icmp_code; /* type sub code */ + u_short icmp_cksum; /* ones complement cksum of struct */ + union { + u_char ih_pptr; /* ICMP_PARAMPROB */ + struct in_addr ih_gwaddr; /* ICMP_REDIRECT */ + struct ih_idseq { + u_short icd_id; + u_short icd_seq; + } ih_idseq; + int ih_void; + + /* ICMP_UNREACH_NEEDFRAG -- Path MTU Discovery (RFC1191) */ + struct ih_pmtu { + u_short ipm_void; + u_short ipm_nextmtu; + } ih_pmtu; + } icmp_hun; +#define icmp_pptr icmp_hun.ih_pptr +#define icmp_gwaddr icmp_hun.ih_gwaddr +#define icmp_id icmp_hun.ih_idseq.icd_id +#define icmp_seq icmp_hun.ih_idseq.icd_seq +#define icmp_void icmp_hun.ih_void +#define icmp_pmvoid icmp_hun.ih_pmtu.ipm_void +#define icmp_nextmtu icmp_hun.ih_pmtu.ipm_nextmtu + union { + struct id_ts { + n_time its_otime; + n_time its_rtime; + n_time its_ttime; + } id_ts; + struct id_ip { + struct ip idi_ip; + /* options and then 64 bits of data */ + } id_ip; + uint32_t id_mask; + char id_data[1]; + } icmp_dun; +#define icmp_otime icmp_dun.id_ts.its_otime +#define icmp_rtime icmp_dun.id_ts.its_rtime +#define icmp_ttime icmp_dun.id_ts.its_ttime +#define icmp_ip icmp_dun.id_ip.idi_ip +#define icmp_mask icmp_dun.id_mask +#define icmp_data icmp_dun.id_data +} PACKED__; + +#ifdef PRAGMA_PACK_SUPPORTED +#pragma pack(0) +#endif + +/* + * Lower bounds on packet lengths for various types. + * For the error advice packets must first insure that the + * packet is large enought to contain the returned ip header. + * Only then can we do the check to see if 64 bits of packet + * data have been returned, since we need to check the returned + * ip header length. + */ +#define ICMP_MINLEN 8 /* abs minimum */ +#define ICMP_TSLEN (8 + 3 * sizeof (n_time)) /* timestamp */ +#define ICMP_MASKLEN 12 /* address mask */ +#define ICMP_ADVLENMIN (8 + sizeof (struct ip) + 8) /* min */ +#define ICMP_ADVLEN(p) (8 + ((p)->icmp_ip.ip_hl << 2) + 8) + /* N.B.: must separately check that ip_hl >= 5 */ + +/* + * Definition of type and code field values. + */ +#define ICMP_ECHOREPLY 0 /* echo reply */ +#define ICMP_UNREACH 3 /* dest unreachable, codes: */ +#define ICMP_UNREACH_NET 0 /* bad net */ +#define ICMP_UNREACH_HOST 1 /* bad host */ +#define ICMP_UNREACH_PROTOCOL 2 /* bad protocol */ +#define ICMP_UNREACH_PORT 3 /* bad port */ +#define ICMP_UNREACH_NEEDFRAG 4 /* IP_DF caused drop */ +#define ICMP_UNREACH_SRCFAIL 5 /* src route failed */ +#define ICMP_UNREACH_NET_UNKNOWN 6 /* unknown net */ +#define ICMP_UNREACH_HOST_UNKNOWN 7 /* unknown host */ +#define ICMP_UNREACH_ISOLATED 8 /* src host isolated */ +#define ICMP_UNREACH_NET_PROHIB 9 /* prohibited access */ +#define ICMP_UNREACH_HOST_PROHIB 10 /* ditto */ +#define ICMP_UNREACH_TOSNET 11 /* bad tos for net */ +#define ICMP_UNREACH_TOSHOST 12 /* bad tos for host */ +#define ICMP_SOURCEQUENCH 4 /* packet lost, slow down */ +#define ICMP_REDIRECT 5 /* shorter route, codes: */ +#define ICMP_REDIRECT_NET 0 /* for network */ +#define ICMP_REDIRECT_HOST 1 /* for host */ +#define ICMP_REDIRECT_TOSNET 2 /* for tos and net */ +#define ICMP_REDIRECT_TOSHOST 3 /* for tos and host */ +#define ICMP_ECHO 8 /* echo service */ +#define ICMP_ROUTERADVERT 9 /* router advertisement */ +#define ICMP_ROUTERSOLICIT 10 /* router solicitation */ +#define ICMP_TIMXCEED 11 /* time exceeded, code: */ +#define ICMP_TIMXCEED_INTRANS 0 /* ttl==0 in transit */ +#define ICMP_TIMXCEED_REASS 1 /* ttl==0 in reass */ +#define ICMP_PARAMPROB 12 /* ip header bad */ +#define ICMP_PARAMPROB_OPTABSENT 1 /* req. opt. absent */ +#define ICMP_TSTAMP 13 /* timestamp request */ +#define ICMP_TSTAMPREPLY 14 /* timestamp reply */ +#define ICMP_IREQ 15 /* information request */ +#define ICMP_IREQREPLY 16 /* information reply */ +#define ICMP_MASKREQ 17 /* address mask request */ +#define ICMP_MASKREPLY 18 /* address mask reply */ + +#define ICMP_MAXTYPE 18 + +#define ICMP_INFOTYPE(type) \ + ((type) == ICMP_ECHOREPLY || (type) == ICMP_ECHO || \ + (type) == ICMP_ROUTERADVERT || (type) == ICMP_ROUTERSOLICIT || \ + (type) == ICMP_TSTAMP || (type) == ICMP_TSTAMPREPLY || \ + (type) == ICMP_IREQ || (type) == ICMP_IREQREPLY || \ + (type) == ICMP_MASKREQ || (type) == ICMP_MASKREPLY) + +void icmp_input _P((struct SLIRPmbuf *, int)); +void icmp_error _P((struct SLIRPmbuf *, u_char, u_char, int, char *)); +void icmp_reflect _P((struct SLIRPmbuf *)); + +#endif diff --git a/src/slirp/ip_input.c b/src/slirp/ip_input.c new file mode 100644 index 0000000..70d3fc8 --- /dev/null +++ b/src/slirp/ip_input.c @@ -0,0 +1,693 @@ +/* + * Copyright (c) 1982, 1986, 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)ip_input.c 8.2 (Berkeley) 1/4/94 + * ip_input.c,v 1.11 1994/11/16 10:17:08 jkh Exp + */ + +/* + * Changes and additions relating to SLiRP are + * Copyright (c) 1995 Danny Gasparovski. + * + * Please read the file COPYRIGHT for the + * terms and conditions of the copyright. + */ + +#include "slirp.h" +#include "ip_icmp.h" + +int ip_defttl; +struct ipstat ipstat; +struct ipq ipq; + +/* + * IP initialization: fill in IP protocol switch table. + * All protocols not implemented in kernel go to raw IP protocol handler. + */ +void +ip_init() +{ + ipq.next = ipq.prev = (ipqp_32)&ipq; + ip_id = tt.tv_sec & 0xffff; + udp_init(); + tcp_init(); + ip_defttl = IPDEFTTL; +} + +/* + * Ip input routine. Checksum and byte swap header. If fragmented + * try to reassemble. Process options. Pass to next level. + */ +void +ip_input(m) + struct SLIRPmbuf *m; +{ + register struct ip *ip; + u_int hlen; + + DEBUG_CALL("ip_input"); + DEBUG_ARG("m = %lx", (long)m); + DEBUG_ARG("m_len = %d", m->m_len); + + ipstat.ips_total++; + + if (m->m_len < sizeof (struct ip)) { + ipstat.ips_toosmall++; + return; + } + + ip = mtod(m, struct ip *); + + if (ip->ip_v != IPVERSION) { + ipstat.ips_badvers++; + goto bad; + } + + hlen = ip->ip_hl << 2; + if (hlenm->m_len) {/* min header length */ + ipstat.ips_badhlen++; /* or packet too short */ + goto bad; + } + + /* keep ip header intact for ICMP reply + * ip->ip_sum = cksum(m, hlen); + * if (ip->ip_sum) { + */ + if(cksum(m,hlen)) { + ipstat.ips_badsum++; + goto bad; + } + + /* + * Convert fields to host representation. + */ + NTOHS(ip->ip_len); + if (ip->ip_len < hlen) { + ipstat.ips_badlen++; + goto bad; + } + NTOHS(ip->ip_id); + NTOHS(ip->ip_off); + + /* + * Check that the amount of data in the buffers + * is as at least much as the IP header would have us expect. + * Trim SLIRPmbufs if longer than we expect. + * Drop packet if shorter than we expect. + */ + if (m->m_len < ip->ip_len) { + ipstat.ips_tooshort++; + goto bad; + } + /* Should drop packet if SLIRPmbuf too long? hmmm... */ + if (m->m_len > ip->ip_len) + m_adj(m, ip->ip_len - m->m_len); + + /* check ip_ttl for a correct ICMP reply */ + if(ip->ip_ttl==0 || ip->ip_ttl==1) { + icmp_error(m, ICMP_TIMXCEED,ICMP_TIMXCEED_INTRANS, 0,"ttl"); + goto bad; + } + + /* + * Process options and, if not destined for us, + * ship it on. ip_dooptions returns 1 when an + * error was detected (causing an icmp message + * to be sent and the original packet to be freed). + */ +/* We do no IP options */ +/* if (hlen > sizeof (struct ip) && ip_dooptions(m)) + * goto next; + */ + /* + * If offset or IP_MF are set, must reassemble. + * Otherwise, nothing need be done. + * (We could look in the reassembly queue to see + * if the packet was previously fragmented, + * but it's not worth the time; just let them time out.) + * + * XXX This should fail, don't fragment yet + */ + if (ip->ip_off &~ IP_DF) { + register struct ipq *fp; + /* + * Look for queue of fragments + * of this datagram. + */ + for (fp = (struct ipq *) ipq.next; fp != &ipq; + fp = (struct ipq *) fp->next) + if (ip->ip_id == fp->ipq_id && + ip->ip_src.s_addr == fp->ipq_src.s_addr && + ip->ip_dst.s_addr == fp->ipq_dst.s_addr && + ip->ip_p == fp->ipq_p) + goto found; + fp = 0; + found: + + /* + * Adjust ip_len to not reflect header, + * set ip_mff if more fragments are expected, + * convert offset of this to bytes. + */ + ip->ip_len -= hlen; + if (ip->ip_off & IP_MF) + ((struct ipasfrag *)ip)->ipf_mff |= 1; + else + ((struct ipasfrag *)ip)->ipf_mff &= ~1; + + ip->ip_off <<= 3; + + /* + * If datagram marked as having more fragments + * or if this is not the first fragment, + * attempt reassembly; if it succeeds, proceed. + */ + if (((struct ipasfrag *)ip)->ipf_mff & 1 || ip->ip_off) { + ipstat.ips_fragments++; + ip = ip_reass((struct ipasfrag *)ip, fp); + if (ip == 0) + return; + ipstat.ips_reassembled++; + m = dtom(ip); + } else + if (fp) + ip_freef(fp); + + } else + ip->ip_len -= hlen; + + /* + * Switch out to protocol's input routine. + */ + ipstat.ips_delivered++; + switch (ip->ip_p) { + case IPPROTO_TCP: + tcp_input(m, hlen, (struct SLIRPsocket *)NULL); + break; + case IPPROTO_UDP: + udp_input(m, hlen); + break; + case IPPROTO_ICMP: + icmp_input(m, hlen); + break; + default: + ipstat.ips_noproto++; + m_free(m); + } + return; +bad: + m_freem(m); + return; +} + +/* + * Take incoming datagram fragment and try to + * reassemble it into whole datagram. If a chain for + * reassembly of this datagram already exists, then it + * is given as fp; otherwise have to make a chain. + */ +struct ip * +ip_reass(ip, fp) + register struct ipasfrag *ip; + register struct ipq *fp; +{ + register struct SLIRPmbuf *m = dtom(ip); + register struct ipasfrag *q; + int hlen = ip->ip_hl << 2; + int i, next; + + DEBUG_CALL("ip_reass"); + DEBUG_ARG("ip = %lx", (long)ip); + DEBUG_ARG("fp = %lx", (long)fp); + DEBUG_ARG("m = %lx", (long)m); + + /* + * Presence of header sizes in SLIRPmbufs + * would confuse code below. + * Fragment m_data is concatenated. + */ + m->m_data += hlen; + m->m_len -= hlen; + + /* + * If first fragment to arrive, create a reassembly queue. + */ + if (fp == 0) { + struct SLIRPmbuf *t; + if ((t = m_get()) == NULL) goto dropfrag; + fp = mtod(t, struct ipq *); + insque_32(fp, &ipq); + fp->ipq_ttl = IPFRAGTTL; + fp->ipq_p = ip->ip_p; + fp->ipq_id = ip->ip_id; + fp->ipq_next = fp->ipq_prev = (ipasfragp_32)fp; + fp->ipq_src = ((struct ip *)ip)->ip_src; + fp->ipq_dst = ((struct ip *)ip)->ip_dst; + q = (struct ipasfrag *)fp; + goto insert; + } + + /* + * Find a segment which begins after this one does. + */ + for (q = (struct ipasfrag *)fp->ipq_next; q != (struct ipasfrag *)fp; + q = (struct ipasfrag *)q->ipf_next) + if (q->ip_off > ip->ip_off) + break; + + /* + * If there is a preceding segment, it may provide some of + * our data already. If so, drop the data from the incoming + * segment. If it provides all of our data, drop us. + */ + if (q->ipf_prev != (ipasfragp_32)fp) { + i = ((struct ipasfrag *)(q->ipf_prev))->ip_off + + ((struct ipasfrag *)(q->ipf_prev))->ip_len - ip->ip_off; + if (i > 0) { + if (i >= ip->ip_len) + goto dropfrag; + m_adj(dtom(ip), i); + ip->ip_off += i; + ip->ip_len -= i; + } + } + + /* + * While we overlap succeeding segments trim them or, + * if they are completely covered, dequeue them. + */ + while (q != (struct ipasfrag *)fp && ip->ip_off + ip->ip_len > q->ip_off) { + i = (ip->ip_off + ip->ip_len) - q->ip_off; + if (i < q->ip_len) { + q->ip_len -= i; + q->ip_off += i; + m_adj(dtom(q), i); + break; + } + q = (struct ipasfrag *) q->ipf_next; + m_freem(dtom((struct ipasfrag *) q->ipf_prev)); + ip_deq((struct ipasfrag *) q->ipf_prev); + } + +insert: + /* + * Stick new segment in its place; + * check for complete reassembly. + */ + ip_enq(ip, (struct ipasfrag *) q->ipf_prev); + next = 0; + for (q = (struct ipasfrag *) fp->ipq_next; q != (struct ipasfrag *)fp; + q = (struct ipasfrag *) q->ipf_next) { + if (q->ip_off != next) + return (0); + next += q->ip_len; + } + if (((struct ipasfrag *)(q->ipf_prev))->ipf_mff & 1) + return (0); + + /* + * Reassembly is complete; concatenate fragments. + */ + q = (struct ipasfrag *) fp->ipq_next; + m = dtom(q); + + q = (struct ipasfrag *) q->ipf_next; + while (q != (struct ipasfrag *)fp) { + struct SLIRPmbuf *t; + t = dtom(q); + q = (struct ipasfrag *) q->ipf_next; + m_cat(m, t); + } + + /* + * Create header for new ip packet by + * modifying header of first packet; + * dequeue and discard fragment reassembly header. + * Make header visible. + */ + ip = (struct ipasfrag *) fp->ipq_next; + + /* + * If the fragments concatenated to an SLIRPmbuf that's + * bigger than the total size of the fragment, then and + * m_ext buffer was alloced. But fp->ipq_next points to + * the old buffer (in the SLIRPmbuf), so we must point ip + * into the new buffer. + */ + if (m->m_flags & M_EXT) { + int delta; + delta = (char *)ip - m->m_dat; + ip = (struct ipasfrag *)(m->m_ext + delta); + } + + /* DEBUG_ARG("ip = %lx", (long)ip); + * ip=(struct ipasfrag *)m->m_data; */ + + ip->ip_len = next; + ip->ipf_mff &= ~1; + ((struct ip *)ip)->ip_src = fp->ipq_src; + ((struct ip *)ip)->ip_dst = fp->ipq_dst; + remque_32(fp); + (void) m_free(dtom(fp)); + m = dtom(ip); + m->m_len += (ip->ip_hl << 2); + m->m_data -= (ip->ip_hl << 2); + + return ((struct ip *)ip); + +dropfrag: + ipstat.ips_fragdropped++; + m_freem(m); + return (0); +} + +/* + * Free a fragment reassembly header and all + * associated datagrams. + */ +void +ip_freef(fp) + struct ipq *fp; +{ + register struct ipasfrag *q, *p; + + for (q = (struct ipasfrag *) fp->ipq_next; q != (struct ipasfrag *)fp; + q = p) { + p = (struct ipasfrag *) q->ipf_next; + ip_deq(q); + m_freem(dtom(q)); + } + remque_32(fp); + (void) m_free(dtom(fp)); +} + +/* + * Put an ip fragment on a reassembly chain. + * Like insque, but pointers in middle of structure. + */ +void +ip_enq(p, prev) + register struct ipasfrag *p, *prev; +{ + DEBUG_CALL("ip_enq"); + DEBUG_ARG("prev = %lx", (long)prev); + p->ipf_prev = (ipasfragp_32) prev; + p->ipf_next = prev->ipf_next; + ((struct ipasfrag *)(prev->ipf_next))->ipf_prev = (ipasfragp_32) p; + prev->ipf_next = (ipasfragp_32) p; +} + +/* + * To ip_enq as remque is to insque. + */ +void +ip_deq(p) + register struct ipasfrag *p; +{ + ((struct ipasfrag *)(p->ipf_prev))->ipf_next = p->ipf_next; + ((struct ipasfrag *)(p->ipf_next))->ipf_prev = p->ipf_prev; +} + +/* + * IP timer processing; + * if a timer expires on a reassembly + * queue, discard it. + */ +void +ip_slowtimo() +{ + register struct ipq *fp; + + DEBUG_CALL("ip_slowtimo"); + + fp = (struct ipq *) ipq.next; + if (fp == 0) + return; + + while (fp != &ipq) { + --fp->ipq_ttl; + fp = (struct ipq *) fp->next; + if (((struct ipq *)(fp->prev))->ipq_ttl == 0) { + ipstat.ips_fragtimeout++; + ip_freef((struct ipq *) fp->prev); + } + } +} + +/* + * Do option processing on a datagram, + * possibly discarding it if bad options are encountered, + * or forwarding it if source-routed. + * Returns 1 if packet has been forwarded/freed, + * 0 if the packet should be processed further. + */ + +#ifdef notdef + +int +ip_dooptions(m) + struct SLIRPmbuf *m; +{ + register struct ip *ip = mtod(m, struct ip *); + register u_char *cp; + register struct ip_timestamp *ipt; + register struct in_ifaddr *ia; +/* int opt, optlen, cnt, off, code, type = ICMP_PARAMPROB, forward = 0; */ + int opt, optlen, cnt, off, code, type, forward = 0; + struct in_addr *sin, dst; +typedef u_int32_t n_time; + n_time ntime; + + dst = ip->ip_dst; + cp = (u_char *)(ip + 1); + cnt = (ip->ip_hl << 2) - sizeof (struct ip); + for (; cnt > 0; cnt -= optlen, cp += optlen) { + opt = cp[IPOPT_OPTVAL]; + if (opt == IPOPT_EOL) + break; + if (opt == IPOPT_NOP) + optlen = 1; + else { + optlen = cp[IPOPT_OLEN]; + if (optlen <= 0 || optlen > cnt) { + code = &cp[IPOPT_OLEN] - (u_char *)ip; + goto bad; + } + } + switch (opt) { + + default: + break; + + /* + * Source routing with record. + * Find interface with current destination address. + * If none on this machine then drop if strictly routed, + * or do nothing if loosely routed. + * Record interface address and bring up next address + * component. If strictly routed make sure next + * address is on directly accessible net. + */ + case IPOPT_LSRR: + case IPOPT_SSRR: + if ((off = cp[IPOPT_OFFSET]) < IPOPT_MINOFF) { + code = &cp[IPOPT_OFFSET] - (u_char *)ip; + goto bad; + } + ipaddr.sin_addr = ip->ip_dst; + ia = (struct in_ifaddr *) + ifa_ifwithaddr((struct sockaddr *)&ipaddr); + if (ia == 0) { + if (opt == IPOPT_SSRR) { + type = ICMP_UNREACH; + code = ICMP_UNREACH_SRCFAIL; + goto bad; + } + /* + * Loose routing, and not at next destination + * yet; nothing to do except forward. + */ + break; + } + off--; / * 0 origin * / + if (off > optlen - sizeof(struct in_addr)) { + /* + * End of source route. Should be for us. + */ + save_rte(cp, ip->ip_src); + break; + } + /* + * locate outgoing interface + */ + bcopy((SLIRPcaddr_t)(cp + off), (SLIRPcaddr_t)&ipaddr.sin_addr, + sizeof(ipaddr.sin_addr)); + if (opt == IPOPT_SSRR) { +#define INA struct in_ifaddr * +#define SA struct sockaddr * + if ((ia = (INA)ifa_ifwithdstaddr((SA)&ipaddr)) == 0) + ia = (INA)ifa_ifwithnet((SA)&ipaddr); + } else + ia = ip_rtaddr(ipaddr.sin_addr); + if (ia == 0) { + type = ICMP_UNREACH; + code = ICMP_UNREACH_SRCFAIL; + goto bad; + } + ip->ip_dst = ipaddr.sin_addr; + bcopy((SLIRPcaddr_t)&(IA_SIN(ia)->sin_addr), + (SLIRPcaddr_t)(cp + off), sizeof(struct in_addr)); + cp[IPOPT_OFFSET] += sizeof(struct in_addr); + /* + * Let ip_intr's mcast routing check handle mcast pkts + */ + forward = !IN_MULTICAST(ntohl(ip->ip_dst.s_addr)); + break; + + case IPOPT_RR: + if ((off = cp[IPOPT_OFFSET]) < IPOPT_MINOFF) { + code = &cp[IPOPT_OFFSET] - (u_char *)ip; + goto bad; + } + /* + * If no space remains, ignore. + */ + off--; * 0 origin * + if (off > optlen - sizeof(struct in_addr)) + break; + bcopy((SLIRPcaddr_t)(&ip->ip_dst), (SLIRPcaddr_t)&ipaddr.sin_addr, + sizeof(ipaddr.sin_addr)); + /* + * locate outgoing interface; if we're the destination, + * use the incoming interface (should be same). + */ + if ((ia = (INA)ifa_ifwithaddr((SA)&ipaddr)) == 0 && + (ia = ip_rtaddr(ipaddr.sin_addr)) == 0) { + type = ICMP_UNREACH; + code = ICMP_UNREACH_HOST; + goto bad; + } + bcopy((SLIRPcaddr_t)&(IA_SIN(ia)->sin_addr), + (SLIRPcaddr_t)(cp + off), sizeof(struct in_addr)); + cp[IPOPT_OFFSET] += sizeof(struct in_addr); + break; + + case IPOPT_TS: + code = cp - (u_char *)ip; + ipt = (struct ip_timestamp *)cp; + if (ipt->ipt_len < 5) + goto bad; + if (ipt->ipt_ptr > ipt->ipt_len - sizeof (int32_t)) { + if (++ipt->ipt_oflw == 0) + goto bad; + break; + } + sin = (struct in_addr *)(cp + ipt->ipt_ptr - 1); + switch (ipt->ipt_flg) { + + case IPOPT_TS_TSONLY: + break; + + case IPOPT_TS_TSANDADDR: + if (ipt->ipt_ptr + sizeof(n_time) + + sizeof(struct in_addr) > ipt->ipt_len) + goto bad; + ipaddr.sin_addr = dst; + ia = (INA)ifaof_ i f p foraddr((SA)&ipaddr, + m->m_pkthdr.rcvif); + if (ia == 0) + continue; + bcopy((SLIRPcaddr_t)&IA_SIN(ia)->sin_addr, + (SLIRPcaddr_t)sin, sizeof(struct in_addr)); + ipt->ipt_ptr += sizeof(struct in_addr); + break; + + case IPOPT_TS_PRESPEC: + if (ipt->ipt_ptr + sizeof(n_time) + + sizeof(struct in_addr) > ipt->ipt_len) + goto bad; + bcopy((SLIRPcaddr_t)sin, (SLIRPcaddr_t)&ipaddr.sin_addr, + sizeof(struct in_addr)); + if (ifa_ifwithaddr((SA)&ipaddr) == 0) + continue; + ipt->ipt_ptr += sizeof(struct in_addr); + break; + + default: + goto bad; + } + ntime = iptime(); + bcopy((SLIRPcaddr_t)&ntime, (SLIRPcaddr_t)cp + ipt->ipt_ptr - 1, + sizeof(n_time)); + ipt->ipt_ptr += sizeof(n_time); + } + } + if (forward) { + ip_forward(m, 1); + return (1); + } + } + } + return (0); +bad: + /* ip->ip_len -= ip->ip_hl << 2; XXX icmp_error adds in hdr length */ + +/* Not yet */ + icmp_error(m, type, code, 0, 0); + + ipstat.ips_badoptions++; + return (1); +} + +#endif /* notdef */ + +/* + * Strip out IP options, at higher + * level protocol in the kernel. + * Second argument is buffer to which options + * will be moved, and return value is their length. + * (XXX) should be deleted; last arg currently ignored. + */ +void +ip_stripoptions(m, mopt) + struct SLIRPmbuf *m; + struct SLIRPmbuf *mopt; +{ + register int i; + struct ip *ip = mtod(m, struct ip *); + register SLIRPcaddr_t opts; + int olen; + + olen = (ip->ip_hl<<2) - sizeof (struct ip); + opts = (SLIRPcaddr_t)(ip + 1); + i = m->m_len - (sizeof (struct ip) + olen); + memcpy(opts, opts + olen, (unsigned)i); + m->m_len -= olen; + + ip->ip_hl = sizeof(struct ip) >> 2; +} diff --git a/src/slirp/ip_output.c b/src/slirp/ip_output.c new file mode 100644 index 0000000..214c031 --- /dev/null +++ b/src/slirp/ip_output.c @@ -0,0 +1,202 @@ +/* + * Copyright (c) 1982, 1986, 1988, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)ip_output.c 8.3 (Berkeley) 1/21/94 + * ip_output.c,v 1.9 1994/11/16 10:17:10 jkh Exp + */ + +/* + * Changes and additions relating to SLiRP are + * Copyright (c) 1995 Danny Gasparovski. + * + * Please read the file COPYRIGHT for the + * terms and conditions of the copyright. + */ + +#include "slirp.h" + +u_int16_t ip_id; + +/* + * IP output. The packet in SLIRPmbuf chain m contains a skeletal IP + * header (with len, off, ttl, proto, tos, src, dst). + * The SLIRPmbuf chain containing the packet will be freed. + * The SLIRPmbuf opt, if present, will not be freed. + */ +int +ip_output(so, m0) + struct SLIRPsocket *so; + struct SLIRPmbuf *m0; +{ + struct ip *ip; + struct SLIRPmbuf *m = m0; + u_int hlen = sizeof(struct ip ); + u_int len, off; + int error = 0; + + DEBUG_CALL("ip_output"); + DEBUG_ARG("so = %lx", (long)so); + DEBUG_ARG("m0 = %lx", (long)m0); + + /* We do no options */ +/* if (opt) { + * m = ip_insertoptions(m, opt, &len); + * hlen = len; + * } + */ + ip = mtod(m, struct ip *); + /* + * Fill in IP header. + */ + ip->ip_v = IPVERSION; + ip->ip_off &= IP_DF; + ip->ip_id = htons(ip_id++); + ip->ip_hl = hlen >> 2; + ipstat.ips_localout++; + + /* + * Verify that we have any chance at all of being able to queue + * the packet or packet fragments + */ + /* XXX Hmmm... */ +/* if (if_queued > if_thresh && towrite <= 0) { + * error = ENOBUFS; + * goto bad; + * } + */ + + /* + * If small enough for interface, can just send directly. + */ + if ((u_int16_t)ip->ip_len <= if_mtu) { + ip->ip_len = htons((u_int16_t)ip->ip_len); + ip->ip_off = htons((u_int16_t)ip->ip_off); + ip->ip_sum = 0; + ip->ip_sum = cksum(m, hlen); + + if_output(so, m); + goto done; + } + + /* + * Too large for interface; fragment if possible. + * Must be able to put at least 8 bytes per fragment. + */ + if (ip->ip_off & IP_DF) { + error = -1; + ipstat.ips_cantfrag++; + goto bad; + } + + len = (if_mtu - hlen) &~ 7; /* ip databytes per packet */ + if (len < 8) { + error = -1; + goto bad; + } + + { + int mhlen, firstlen = len; + struct SLIRPmbuf **mnext = &m->m_nextpkt; + + /* + * Loop through length of segment after first fragment, + * make new header and copy data of each part and link onto chain. + */ + m0 = m; + mhlen = sizeof (struct ip); + for (off = hlen + len; off < ip->ip_len; off += len) { + struct ip *mhip; + m = m_get(); + if (m == 0) { + error = -1; + ipstat.ips_odropped++; + goto sendorfree; + } + m->m_data += if_maxlinkhdr; + mhip = mtod(m, struct ip *); + *mhip = *ip; + + /* No options */ +/* if (hlen > sizeof (struct ip)) { + * mhlen = ip_optcopy(ip, mhip) + sizeof (struct ip); + * mhip->ip_hl = mhlen >> 2; + * } + */ + m->m_len = mhlen; + mhip->ip_off = ((off - hlen) >> 3) + (ip->ip_off & ~IP_MF); + if (ip->ip_off & IP_MF) + mhip->ip_off |= IP_MF; + if (off + len >= (u_int16_t)ip->ip_len) + len = (u_int16_t)ip->ip_len - off; + else + mhip->ip_off |= IP_MF; + mhip->ip_len = htons((u_int16_t)(len + mhlen)); + + if (m_copy(m, m0, off, len) < 0) { + error = -1; + goto sendorfree; + } + + mhip->ip_off = htons((u_int16_t)mhip->ip_off); + mhip->ip_sum = 0; + mhip->ip_sum = cksum(m, mhlen); + *mnext = m; + mnext = &m->m_nextpkt; + ipstat.ips_ofragments++; + } + /* + * Update first fragment by trimming what's been copied out + * and updating header, then send each fragment (in order). + */ + m = m0; + m_adj(m, hlen + firstlen - ip->ip_len); + ip->ip_len = htons((u_int16_t)m->m_len); + ip->ip_off = htons((u_int16_t)(ip->ip_off | IP_MF)); + ip->ip_sum = 0; + ip->ip_sum = cksum(m, hlen); +sendorfree: + for (m = m0; m; m = m0) { + m0 = m->m_nextpkt; + m->m_nextpkt = 0; + if (error == 0) + if_output(so, m); + else + m_freem(m); + } + + if (error == 0) + ipstat.ips_fragmented++; + } + +done: + return (error); + +bad: + m_freem(m0); + goto done; +} diff --git a/src/slirp/libslirp.h b/src/slirp/libslirp.h new file mode 100644 index 0000000..57a68cf --- /dev/null +++ b/src/slirp/libslirp.h @@ -0,0 +1,41 @@ +#ifndef _LIBSLIRP_H +#define _LIBSLIRP_H + +#ifdef _WIN32 +#include +int inet_aton(const char *cp, struct in_addr *ia); +#else +#include +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +int slirp_init(void); + +int slirp_select_fill(int *pnfds, + fd_set *readfds, fd_set *writefds, fd_set *xfds); + +void slirp_select_poll(fd_set *readfds, fd_set *writefds, fd_set *xfds); + +void slirp_input(const uint8 *pkt, int pkt_len); + +/* you must provide the following functions: */ +int slirp_can_output(void); +void slirp_output(const uint8 *pkt, int pkt_len); + +int slirp_redir(int is_udp, int host_port, + struct in_addr guest_addr, int guest_port); +int slirp_add_exec(int do_pty, const char *args, int addr_low_byte, + int guest_port); + +extern const char *tftp_prefix; +extern char slirp_hostname[33]; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/slirp/main.h b/src/slirp/main.h new file mode 100644 index 0000000..45e51cd --- /dev/null +++ b/src/slirp/main.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 1995 Danny Gasparovski. + * + * Please read the file COPYRIGHT for the + * terms and conditions of the copyright. + */ + +#ifdef HAVE_SYS_SELECT_H +#include +#endif + +#define TOWRITEMAX 512 + +extern struct timeval tt; +extern int link_up; +extern int slirp_socket; +extern int slirp_socket_unit; +extern int slirp_socket_port; +extern u_int32_t slirp_socket_addr; +extern char *slirp_socket_passwd; +extern int ctty_closed; + +/* + * Get the difference in 2 times from updtim() + * Allow for wraparound times, "just in case" + * x is the greater of the 2 (current time) and y is + * what it's being compared against. + */ +#define TIME_DIFF(x,y) (x)-(y) < 0 ? ~0-(y)+(x) : (x)-(y) + +extern char *slirp_tty; +extern char *exec_shell; +extern u_int curtime; +extern fd_set *global_readfds, *global_writefds, *global_xfds; +extern struct in_addr ctl_addr; +extern struct in_addr special_addr; +extern struct in_addr alias_addr; +extern struct in_addr our_addr; +extern struct in_addr loopback_addr; +extern struct in_addr dns_addr; +extern char *username; +extern char *socket_path; +extern int towrite_max; +extern int ppp_exit; +extern int so_options; +extern int tcp_keepintvl; +extern uint8_t client_ethaddr[6]; + +#define PROTO_SLIP 0x1 +#ifdef USE_PPP +#define PROTO_PPP 0x2 +#endif + +void if_encap(const uint8_t *ip_data, int ip_data_len); diff --git a/src/slirp/mbuf.c b/src/slirp/mbuf.c new file mode 100644 index 0000000..bf636e1 --- /dev/null +++ b/src/slirp/mbuf.c @@ -0,0 +1,248 @@ +/* + * Copyright (c) 1995 Danny Gasparovski + * + * Please read the file COPYRIGHT for the + * terms and conditions of the copyright. + */ + +/* + * mbuf's in SLiRP are much simpler than the real mbufs in + * FreeBSD. They are fixed size, determined by the MTU, + * so that one whole packet can fit. Mbuf's cannot be + * chained together. If there's more data than the mbuf + * could hold, an external malloced buffer is pointed to + * by m_ext (and the data pointers) and M_EXT is set in + * the flags + */ + +#include +#include "slirp.h" + +struct mbuf *mbutl; +char *mclrefcnt; +int mbuf_alloced = 0; +struct SLIRPmbuf m_freelist, m_usedlist; +int mbuf_thresh = 30; +int mbuf_max = 0; +size_t msize; + +void +m_init() +{ + m_freelist.m_next = m_freelist.m_prev = &m_freelist; + m_usedlist.m_next = m_usedlist.m_prev = &m_usedlist; + msize_init(); +} + +void msize_init() +{ + /* + * Find a nice value for msize + * XXX if_maxlinkhdr already in mtu + */ + msize = (if_mtu>if_mru?if_mtu:if_mru) + + if_maxlinkhdr + sizeof(struct m_hdr ) + 6; +} + +/* + * Get an mbuf from the free list, if there are none + * malloc one + * + * Because fragmentation can occur if we alloc new mbufs and + * free old mbufs, we mark all mbufs above mbuf_thresh as M_DOFREE, + * which tells m_free to actually free() it + */ +struct SLIRPmbuf * m_get() +{ + struct SLIRPmbuf *m; + int flags = 0; + + DEBUG_CALL("m_get"); + + if (m_freelist.m_next == &m_freelist) { + m = (struct SLIRPmbuf *)malloc(msize); + if (m == NULL) goto end_error; + mbuf_alloced++; + if (mbuf_alloced > mbuf_thresh) + flags = M_DOFREE; + if (mbuf_alloced > mbuf_max) + mbuf_max = mbuf_alloced; + } else { + m = m_freelist.m_next; + remque(m); + } + + /* Insert it in the used list */ + insque(m,&m_usedlist); + m->m_flags = (flags | M_USEDLIST); + + /* Initialise it */ + m->m_size = msize - sizeof(struct m_hdr); + m->m_data = m->m_dat; + m->m_len = 0; + m->m_nextpkt = 0; + m->m_prevpkt = 0; +end_error: + DEBUG_ARG("m = %lx", (long )m); + return m; +} + +//For some reason this fails in GDB saying tehre is no m_flags member +void +m_free(m) + struct SLIRPmbuf *m; +{ + + DEBUG_CALL("m_free"); + DEBUG_ARG("m = %lx", (long )m); + + if(m) { + /* Remove from m_usedlist */ + if (m->m_flags & M_USEDLIST) + remque(m); + + + + /* If it's M_EXT, free() it */ + if (m->m_flags & M_EXT) + free(m->m_ext); + + /* + * Either free() it or put it on the free list + */ + if (m->m_flags & M_DOFREE) { + free(m); + mbuf_alloced--; + } else if ((m->m_flags & M_FREELIST) == 0) { + insque(m,&m_freelist); + m->m_flags = M_FREELIST; /* Clobber other flags */ + } + } /* if(m) */ +} + +/* + * Copy data from one mbuf to the end of + * the other.. if result is too big for one mbuf, malloc() + * an M_EXT data segment + */ +void +m_cat(m, n) + struct SLIRPmbuf *m, *n; +{ + /* + * If there's no room, realloc + */ + if (M_FREEROOM(m) < n->m_len) + m_inc(m,m->m_size+MINCSIZE); + + memcpy(m->m_data+m->m_len, n->m_data, n->m_len); + m->m_len += n->m_len; + + m_free(n); +} + + +/* make m size bytes large */ +void +m_inc(m, size) + struct SLIRPmbuf *m; + int size; +{ + int datasize; + + /* some compiles throw up on gotos. This one we can fake. */ + if(m->m_size>size) return; + + if (m->m_flags & M_EXT) { + datasize = m->m_data - m->m_ext; + m->m_ext = (char *)realloc(m->m_ext,size); +/* if (m->m_ext == NULL) + * return (struct SLIRPmbuf *)NULL; + */ + m->m_data = m->m_ext + datasize; + } else { + char *dat; + datasize = m->m_data - m->m_dat; + dat = (char *)malloc(size); +/* if (dat == NULL) + * return (struct SLIRPmbuf *)NULL; + */ + memcpy(dat, m->m_dat, m->m_size); + + m->m_ext = dat; + m->m_data = m->m_ext + datasize; + m->m_flags |= M_EXT; + } + + m->m_size = size; + +} + + + +void +m_adj(m, len) + struct SLIRPmbuf *m; + int len; +{ + if (m == NULL) + return; + if (len >= 0) { + /* Trim from head */ + m->m_data += len; + m->m_len -= len; + } else { + /* Trim from tail */ + len = -len; + m->m_len -= len; + } +} + + +/* + * Copy len bytes from m, starting off bytes into n + */ +int +m_copy(n, m, off, len) + struct SLIRPmbuf *n, *m; + int off, len; +{ + if (len > M_FREEROOM(n)) + return -1; + + memcpy((n->m_data + n->m_len), (m->m_data + off), len); + n->m_len += len; + return 0; +} + + +/* + * Given a pointer into an mbuf, return the mbuf + * XXX This is a kludge, I should eliminate the need for it + * Fortunately, it's not used often + */ +struct SLIRPmbuf * +dtom(dat) + void *dat; +{ + struct SLIRPmbuf *m; + + DEBUG_CALL("dtom"); + DEBUG_ARG("dat = %lx", (long )dat); + + /* bug corrected for M_EXT buffers */ + for (m = m_usedlist.m_next; m != &m_usedlist; m = m->m_next) { + if (m->m_flags & M_EXT) { + if( (char *)dat>=m->m_ext && (char *)dat<(m->m_ext + m->m_size) ) + return m; + } else { + if( (char *)dat >= m->m_dat && (char *)dat<(m->m_dat + m->m_size) ) + return m; + } + } + + DEBUG_ERROR((dfd, "dtom failed")); + + return (struct SLIRPmbuf *)0; +} + diff --git a/src/slirp/mbuf.h b/src/slirp/mbuf.h new file mode 100644 index 0000000..840fa3b --- /dev/null +++ b/src/slirp/mbuf.h @@ -0,0 +1,143 @@ +/* + * Copyright (c) 1982, 1986, 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)mbuf.h 8.3 (Berkeley) 1/21/94 + * mbuf.h,v 1.9 1994/11/14 13:54:20 bde Exp + */ + +#ifndef _MBUF_H_ +#define _MBUF_H_ + +#define m_freem m_free + + +#define MINCSIZE 4096 /* Amount to increase mbuf if too small */ + +/* + * Macros for type conversion + * mtod(m,t) - convert mbuf pointer to data pointer of correct type + * dtom(x) - convert data pointer within mbuf to mbuf pointer (XXX) + */ +#define mtod(m,t) ((t)(m)->m_data) +/* #define dtom(x) ((struct SLIRPmbuf *)((int)(x) & ~(M_SIZE-1))) */ + +/* XXX About mbufs for slirp: + * Only one mbuf is ever used in a chain, for each "cell" of data. + * m_nextpkt points to the next packet, if fragmented. + * If the data is too large, the M_EXT is used, and a larger block + * is alloced. Therefore, m_free[m] must check for M_EXT and if set + * free the m_ext. This is inefficient memory-wise, but who cares. + */ + +/* XXX should union some of these! */ +/* header at beginning of each mbuf: */ +struct m_hdr { + struct SLIRPmbuf *mh_next; /* Linked list of mbufs */ + struct SLIRPmbuf *mh_prev; + struct SLIRPmbuf *mh_nextpkt; /* Next packet in queue/record */ + struct SLIRPmbuf *mh_prevpkt; /* Flags aren't used in the output queue */ + int mh_flags; /* Misc flags */ + + size_t mh_size; /* Size of data */ + struct SLIRPsocket *mh_so; + + SLIRPcaddr_t mh_data; /* Location of data */ + size_t mh_len; /* Amount of data in this mbuf */ +}; + +/* + * How much room is in the mbuf, from m_data to the end of the mbuf + */ +#define M_ROOM(m) ((m->m_flags & M_EXT)? \ + (((m)->m_ext + (m)->m_size) - (m)->m_data) \ + : \ + (((m)->m_dat + (m)->m_size) - (m)->m_data)) + +/* + * How much free room there is + */ +#define M_FREEROOM(m) (M_ROOM(m) - (m)->m_len) +#define M_TRAILINGSPACE M_FREEROOM + +struct SLIRPmbuf { + struct m_hdr m_hdr; + union M_dat { + char m_dat_[1]; /* ANSI don't like 0 sized arrays */ + char *m_ext_; + } M_dat; +}; + +#define m_next m_hdr.mh_next +#define m_prev m_hdr.mh_prev +#define m_nextpkt m_hdr.mh_nextpkt +#define m_prevpkt m_hdr.mh_prevpkt +#define m_flags m_hdr.mh_flags +#define m_len m_hdr.mh_len +#define m_data m_hdr.mh_data +#define m_size m_hdr.mh_size +#define m_dat M_dat.m_dat_ +#define m_ext M_dat.m_ext_ +#define m_so m_hdr.mh_so + +#define ifq_prev m_prev +#define ifq_next m_next +#define ifs_prev m_prevpkt +#define ifs_next m_nextpkt +#define ifq_so m_so + +#define M_EXT 0x01 /* m_ext points to more (malloced) data */ +#define M_FREELIST 0x02 /* mbuf is on free list */ +#define M_USEDLIST 0x04 /* XXX mbuf is on used list (for dtom()) */ +#define M_DOFREE 0x08 /* when m_free is called on the mbuf, free() + * it rather than putting it on the free list */ + +/* + * Mbuf statistics. XXX + */ + +struct mbstat { + int mbs_alloced; /* Number of mbufs allocated */ + +}; + +extern struct mbstat mbstat; +extern int mbuf_alloced; +extern struct SLIRPmbuf m_freelist, m_usedlist; +extern int mbuf_max; + +void m_init _P((void)); +void msize_init _P((void)); +struct SLIRPmbuf * m_get _P((void)); +void m_free _P((struct SLIRPmbuf *)); +void m_cat _P((register struct SLIRPmbuf *, register struct SLIRPmbuf *)); +void m_inc _P((struct SLIRPmbuf *, int)); +void m_adj _P((struct SLIRPmbuf *, int)); +int m_copy _P((struct SLIRPmbuf *, struct SLIRPmbuf *, int, int)); +struct SLIRPmbuf * dtom _P((void *)); + +#endif diff --git a/src/slirp/misc.c b/src/slirp/misc.c new file mode 100644 index 0000000..b1e2df2 --- /dev/null +++ b/src/slirp/misc.c @@ -0,0 +1,970 @@ +/* + * Copyright (c) 1995 Danny Gasparovski. + * + * Please read the file COPYRIGHT for the + * terms and conditions of the copyright. + */ + +#define WANT_SYS_IOCTL_H +#include +#include "slirp.h" + +u_int curtime, time_fasttimo, last_slowtimo, detach_time; +u_int detach_wait = 600000; /* 10 minutes */ + +#if 0 +int x_port = -1; +int x_display = 0; +int x_screen = 0; + +int +show_x(buff, inso) + char *buff; + struct SLIRPsocket *inso; +{ + if (x_port < 0) { + lprint("X Redir: X not being redirected.\r\n"); + } else { + lprint("X Redir: In sh/bash/zsh/etc. type: DISPLAY=%s:%d.%d; export DISPLAY\r\n", + inet_ntoa(our_addr), x_port, x_screen); + lprint("X Redir: In csh/tcsh/etc. type: setenv DISPLAY %s:%d.%d\r\n", + inet_ntoa(our_addr), x_port, x_screen); + if (x_display) + lprint("X Redir: Redirecting to display %d\r\n", x_display); + } + + return CFG_OK; +} + + +/* + * XXX Allow more than one X redirection? + */ +void +redir_x(inaddr, start_port, display, screen) + u_int32_t inaddr; + int start_port; + int display; + int screen; +{ + int i; + + if (x_port >= 0) { + lprint("X Redir: X already being redirected.\r\n"); + show_x(0, 0); + } else { + for (i = 6001 + (start_port-1); i <= 6100; i++) { + if (solisten(htons(i), inaddr, htons(6000 + display), 0)) { + /* Success */ + x_port = i - 6000; + x_display = display; + x_screen = screen; + show_x(0, 0); + return; + } + } + lprint("X Redir: Error: Couldn't redirect a port for X. Weird.\r\n"); + } +} +#endif + +#ifndef HAVE_INET_ATON +int +inet_aton(cp, ia) + const char *cp; + struct in_addr *ia; +{ + u_int32_t addr = inet_addr(cp); + if (addr == 0xffffffff) + return 0; + ia->s_addr = addr; + return 1; +} +#endif + +/* + * Get our IP address and put it in our_addr + */ +void +getouraddr() +{ + char buff[512]; + struct hostent *he = NULL; +#define ANCIENT + #ifdef ANCIENT + if (gethostname(&buff,500) == 0) + he = gethostbyname(&buff); + if (he) + our_addr = *(struct in_addr *)he->h_addr; + if (our_addr.s_addr == 0) + our_addr.s_addr = loopback_addr.s_addr; + #else + if (gethostname(buff,256) == 0) + { + struct addrinfo hints = { 0 }; + hints.ai_flags = AI_NUMERICHOST; + hints.ai_family = AF_INET; + struct addrinfo* ai; + if (getaddrinfo(buff, NULL, &hints, &ai) == 0) + { + our_addr = *(struct in_addr *)ai->ai_addr->sa_data; + freeaddrinfo(ai); + } + } + if (our_addr.s_addr == 0) + our_addr.s_addr = loopback_addr.s_addr; + #endif + #undef ANCIENT +} + +//#if SIZEOF_CHAR_P == 8 +//what?! + +struct quehead_32 { + u_int32_t qh_link; + u_int32_t qh_rlink; +}; + +inline void +insque_32(a, b) + void *a; + void *b; +{ + register struct quehead_32 *element = (struct quehead_32 *) a; + register struct quehead_32 *head = (struct quehead_32 *) b; + element->qh_link = head->qh_link; + head->qh_link = (u_int32_t)element; + element->qh_rlink = (u_int32_t)head; + ((struct quehead_32 *)(element->qh_link))->qh_rlink + = (u_int32_t)element; +} + +inline void +remque_32(a) + void *a; +{ + register struct quehead_32 *element = (struct quehead_32 *) a; + ((struct quehead_32 *)(element->qh_link))->qh_rlink = element->qh_rlink; + ((struct quehead_32 *)(element->qh_rlink))->qh_link = element->qh_link; + element->qh_rlink = 0; +} + +//#endif /* SIZEOF_CHAR_P == 8 */ +//Should be for 64bit + +struct quehead { + struct quehead *qh_link; + struct quehead *qh_rlink; +}; + +void +insque(a, b) + void *a, *b; +{ + register struct quehead *element = (struct quehead *) a; + register struct quehead *head = (struct quehead *) b; + element->qh_link = head->qh_link; + head->qh_link = (struct quehead *)element; + element->qh_rlink = (struct quehead *)head; + ((struct quehead *)(element->qh_link))->qh_rlink + = (struct quehead *)element; +} + +void +remque(a) + void *a; +{ + register struct quehead *element = (struct quehead *) a; + ((struct quehead *)(element->qh_link))->qh_rlink = element->qh_rlink; + ((struct quehead *)(element->qh_rlink))->qh_link = element->qh_link; + element->qh_rlink = NULL; + /* element->qh_link = NULL; TCP FIN1 crashes if you do this. Why ? */ +} + +/* #endif */ + + +int +add_exec(ex_ptr, do_pty, exec, addr, port) + struct ex_list **ex_ptr; + int do_pty; + char *exec; + int addr; + int port; +{ + struct ex_list *tmp_ptr; + + /* First, check if the port is "bound" */ + for (tmp_ptr = *ex_ptr; tmp_ptr; tmp_ptr = tmp_ptr->ex_next) { + if (port == tmp_ptr->ex_fport && addr == tmp_ptr->ex_addr) + return -1; + } + + tmp_ptr = *ex_ptr; + *ex_ptr = (struct ex_list *)malloc(sizeof(struct ex_list)); + (*ex_ptr)->ex_fport = port; + (*ex_ptr)->ex_addr = addr; + (*ex_ptr)->ex_pty = do_pty; + (*ex_ptr)->ex_exec = strdup(exec); + (*ex_ptr)->ex_next = tmp_ptr; + return 0; +} + +#ifndef HAVE_STRERROR + +/* + * For systems with no strerror + */ + +#ifdef WIN32 +//extern int sys_nerr; +//extern char *sys_errlist[]; +#endif + +char * +SLIRPstrerror(error) + int error; +{ + if (error < sys_nerr) + return sys_errlist[error]; + else + return "Unknown error."; +} + +#endif + + +#ifdef _WIN32 + +int +fork_exec(so, ex, do_pty) + struct SLIRPsocket *so; + char *ex; + int do_pty; +{ + /* not implemented */ + return 0; +} + +#else + +int +slirp_openpty(amaster, aslave) + int *amaster, *aslave; +{ + register int master, slave; + +#ifdef HAVE_GRANTPT + char *ptr; + + if ((master = open("/dev/ptmx", O_RDWR)) < 0 || + grantpt(master) < 0 || + unlockpt(master) < 0 || + (ptr = ptsname(master)) == NULL) { + close(master); + return -1; + } + + if ((slave = open(ptr, O_RDWR)) < 0 || + ioctl(slave, I_PUSH, "ptem") < 0 || + ioctl(slave, I_PUSH, "ldterm") < 0 || + ioctl(slave, I_PUSH, "ttcompat") < 0) { + close(master); + close(slave); + return -1; + } + + *amaster = master; + *aslave = slave; + return 0; + +#else + + static char line[] = "/dev/ptyXX"; + register const char *cp1, *cp2; + + for (cp1 = "pqrsPQRS"; *cp1; cp1++) { + line[8] = *cp1; + for (cp2 = "0123456789abcdefghijklmnopqrstuv"; *cp2; cp2++) { + line[9] = *cp2; + if ((master = open(line, O_RDWR, 0)) == -1) { + if (errno == ENOENT) + return (-1); /* out of ptys */ + } else { + line[5] = 't'; + /* These will fail */ + (void) chown(line, getuid(), 0); + (void) chmod(line, S_IRUSR|S_IWUSR|S_IWGRP); +#ifdef HAVE_REVOKE + (void) revoke(line); +#endif + if ((slave = open(line, O_RDWR, 0)) != -1) { + *amaster = master; + *aslave = slave; + return 0; + } + (void) close(master); + line[5] = 'p'; + } + } + } + errno = ENOENT; /* out of ptys */ + return (-1); +#endif +} + +/* + * XXX This is ugly + * We create and bind a socket, then fork off to another + * process, which connects to this socket, after which we + * exec the wanted program. If something (strange) happens, + * the accept() call could block us forever. + * + * do_pty = 0 Fork/exec inetd style + * do_pty = 1 Fork/exec using slirp.telnetd + * do_ptr = 2 Fork/exec using pty + */ +int +fork_exec(so, ex, do_pty) + struct SLIRPsocket *so; + char *ex; + int do_pty; +{ + int s; + struct sockaddr_in addr; + socklen_t addrlen = sizeof(addr); + int opt; + int master; + char *argv[256]; +#if 0 + char buff[256]; +#endif + /* don't want to clobber the original */ + char *bptr; + char *curarg; + int c, i, ret; + + DEBUG_CALL("fork_exec"); + DEBUG_ARG("so = %lx", (long)so); + DEBUG_ARG("ex = %lx", (long)ex); + DEBUG_ARG("do_pty = %lx", (long)do_pty); + + if (do_pty == 2) { + if (slirp_openpty(&master, &s) == -1) { + lprint("Error: openpty failed: %s\n", strerror(errno)); + return 0; + } + } else { + memset(&addr, 0, sizeof(struct sockaddr_in)); + addr.sin_family = AF_INET; + addr.sin_port = 0; + addr.sin_addr.s_addr = INADDR_ANY; + + if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0 || + bind(s, (struct sockaddr *)&addr, addrlen) < 0 || + listen(s, 1) < 0) { + lprint("Error: inet socket: %s\n", strerror(errno)); + closesocket(s); + + return 0; + } + } + + switch(fork()) { + case -1: + lprint("Error: fork failed: %s\n", strerror(errno)); + close(s); + if (do_pty == 2) + close(master); + return 0; + + case 0: + /* Set the DISPLAY */ + if (do_pty == 2) { + (void) close(master); +#ifdef TIOCSCTTY /* XXXXX */ + (void) setsid(); + ioctl(s, TIOCSCTTY, (char *)NULL); +#endif + } else { + getsockname(s, (struct sockaddr *)&addr, &addrlen); + close(s); + /* + * Connect to the socket + * XXX If any of these fail, we're in trouble! + */ + s = socket(AF_INET, SOCK_STREAM, 0); + addr.sin_addr = loopback_addr; + do { + ret = connect(s, (struct sockaddr *)&addr, addrlen); + } while (ret < 0 && errno == EINTR); + } + +#if 0 + if (x_port >= 0) { +#ifdef HAVE_SETENV + sprintf(buff, "%s:%d.%d", inet_ntoa(our_addr), x_port, x_screen); + setenv("DISPLAY", buff, 1); +#else + sprintf(buff, "DISPLAY=%s:%d.%d", inet_ntoa(our_addr), x_port, x_screen); + putenv(buff); +#endif + } +#endif + dup2(s, 0); + dup2(s, 1); + dup2(s, 2); + for (s = 3; s <= 255; s++) + close(s); + + i = 0; + bptr = strdup(ex); /* No need to free() this */ + if (do_pty == 1) { + /* Setup "slirp.telnetd -x" */ + argv[i++] = "slirp.telnetd"; + argv[i++] = "-x"; + argv[i++] = bptr; + } else + do { + /* Change the string into argv[] */ + curarg = bptr; + while (*bptr != ' ' && *bptr != (char)0) + bptr++; + c = *bptr; + *bptr++ = (char)0; + argv[i++] = strdup(curarg); + } while (c); + + argv[i] = 0; + execvp(argv[0], argv); + + /* Ooops, failed, let's tell the user why */ + { + char buff[256]; + + sprintf(buff, "Error: execvp of %s failed: %s\n", + argv[0], strerror(errno)); + write(2, buff, strlen(buff)+1); + } + close(0); close(1); close(2); /* XXX */ + exit(1); + + default: + if (do_pty == 2) { + close(s); + so->s = master; + } else { + /* + * XXX this could block us... + * XXX Should set a timer here, and if accept() doesn't + * return after X seconds, declare it a failure + * The only reason this will block forever is if socket() + * of connect() fail in the child process + */ + do { + so->s = accept(s, (struct sockaddr *)&addr, &addrlen); + } while (so->s < 0 && errno == EINTR); + closesocket(s); + opt = 1; + setsockopt(so->s,SOL_SOCKET,SO_REUSEADDR,(char *)&opt,sizeof(int)); + opt = 1; + setsockopt(so->s,SOL_SOCKET,SO_OOBINLINE,(char *)&opt,sizeof(int)); + } + fd_nonblock(so->s); + + /* Append the telnet options now */ + if (so->so_m != 0 && do_pty == 1) { + sbappend(so, so->so_m); + so->so_m = 0; + } + + return 1; + } +} +#endif + +#ifndef HAVE_STRDUP +char * strdup(char *str) +{ + char *bptr; + + bptr = (char *)malloc(strlen(str)+1); + strcpy(bptr, str); + + return bptr; +} +#endif + +#if 0 +void +snooze_hup(num) + int num; +{ + int s, ret; +#ifndef NO_UNIX_SOCKETS + struct sockaddr_un sock_un; +#endif + struct sockaddr_in sock_in; + char buff[256]; + + ret = -1; + if (slirp_socket_passwd) { + s = socket(AF_INET, SOCK_STREAM, 0); + if (s < 0) + slirp_exit(1); + sock_in.sin_family = AF_INET; + sock_in.sin_addr.s_addr = slirp_socket_addr; + sock_in.sin_port = htons(slirp_socket_port); + if (connect(s, (struct sockaddr *)&sock_in, sizeof(sock_in)) != 0) + slirp_exit(1); /* just exit...*/ + sprintf(buff, "kill %s:%d", slirp_socket_passwd, slirp_socket_unit); + write(s, buff, strlen(buff)+1); + } +#ifndef NO_UNIX_SOCKETS + else { + s = socket(AF_UNIX, SOCK_STREAM, 0); + if (s < 0) + slirp_exit(1); + sock_un.sun_family = AF_UNIX; + strcpy(sock_un.sun_path, socket_path); + if (connect(s, (struct sockaddr *)&sock_un, + sizeof(sock_un.sun_family) + sizeof(sock_un.sun_path)) != 0) + slirp_exit(1); + sprintf(buff, "kill none:%d", slirp_socket_unit); + write(s, buff, strlen(buff)+1); + } +#endif + slirp_exit(0); +} + + +void +snooze() +{ + sigset_t s; + int i; + + /* Don't need our data anymore */ + /* XXX This makes SunOS barf */ +/* brk(0); */ + + /* Close all fd's */ + for (i = 255; i >= 0; i--) + close(i); + + signal(SIGQUIT, slirp_exit); + signal(SIGHUP, snooze_hup); + sigemptyset(&s); + + /* Wait for any signal */ + sigsuspend(&s); + + /* Just in case ... */ + exit(255); +} + +void +relay(s) + int s; +{ + char buf[8192]; + int n; + fd_set readfds; + struct ttys *ttyp; + + /* Don't need our data anymore */ + /* XXX This makes SunOS barf */ +/* brk(0); */ + + signal(SIGQUIT, slirp_exit); + signal(SIGHUP, slirp_exit); + signal(SIGINT, slirp_exit); + signal(SIGTERM, slirp_exit); + + /* Fudge to get term_raw and term_restore to work */ + if (NULL == (ttyp = tty_attach (0, slirp_tty))) { + lprint ("Error: tty_attach failed in misc.c:relay()\r\n"); + slirp_exit (1); + } + ttyp->fd = 0; + ttyp->flags |= TTY_CTTY; + term_raw(ttyp); + + while (1) { + FD_ZERO(&readfds); + + FD_SET(0, &readfds); + FD_SET(s, &readfds); + + n = select(s+1, &readfds, (fd_set *)0, (fd_set *)0, (struct timeval *)0); + + if (n <= 0) + slirp_exit(0); + + if (FD_ISSET(0, &readfds)) { + n = read(0, buf, 8192); + if (n <= 0) + slirp_exit(0); + n = writen(s, buf, n); + if (n <= 0) + slirp_exit(0); + } + + if (FD_ISSET(s, &readfds)) { + n = read(s, buf, 8192); + if (n <= 0) + slirp_exit(0); + n = writen(0, buf, n); + if (n <= 0) + slirp_exit(0); + } + } + + /* Just in case.... */ + exit(1); +} +#endif + +int (*lprint_print) _P((void *, const char *, va_list)); +char *lprint_ptr, *lprint_ptr2, **lprint_arg; + +#ifdef _MSC_VER //aren't we +#define __STDC__ +#endif + +void +#ifdef __STDC__ +lprint(const char *format, ...) +#else +lprint(va_alist) va_dcl +#endif +{ + va_list args; + +#ifdef __STDC__ + va_start(args, format); +#else + char *format; + va_start(args); + format = va_arg(args, char *); +#endif +#if 0 + /* If we're printing to an sbuf, make sure there's enough room */ + /* XXX +100? */ + if (lprint_sb) { + if ((lprint_ptr - lprint_sb->sb_wptr) >= + (lprint_sb->sb_datalen - (strlen(format) + 100))) { + int deltaw = lprint_sb->sb_wptr - lprint_sb->sb_data; + int deltar = lprint_sb->sb_rptr - lprint_sb->sb_data; + int deltap = lprint_ptr - lprint_sb->sb_data; + + lprint_sb->sb_data = (char *)realloc(lprint_sb->sb_data, + lprint_sb->sb_datalen + TCP_SNDSPACE); + + /* Adjust all values */ + lprint_sb->sb_wptr = lprint_sb->sb_data + deltaw; + lprint_sb->sb_rptr = lprint_sb->sb_data + deltar; + lprint_ptr = lprint_sb->sb_data + deltap; + + lprint_sb->sb_datalen += TCP_SNDSPACE; + } + } +#endif + if (lprint_print) + lprint_ptr += (*lprint_print)(*lprint_arg, format, args); + + /* Check if they want output to be logged to file as well */ + if (lfd) { + /* + * Remove \r's + * otherwise you'll get ^M all over the file + */ + int len = strlen(format); + char *bptr1, *bptr2; + + bptr1 = bptr2 = strdup(format); + + while (len--) { + if (*bptr1 == '\r') + memcpy(bptr1, bptr1+1, len+1); + else + bptr1++; + } + vfprintf(lfd, bptr2, args); + free(bptr2); + } + va_end(args); +} + +void +add_emu(buff) + char *buff; +{ + u_int lport, fport; + u_int8_t tos = 0, emu = 0; + char buff1[256], buff2[256], buff4[128]; + char *buff3 = buff4; + struct emu_t *emup; + struct SLIRPsocket *so; + + if (sscanf(buff, "%256s %256s", buff2, buff1) != 2) { + lprint("Error: Bad arguments\r\n"); + return; + } + + if (sscanf(buff1, "%d:%d", &lport, &fport) != 2) { + lport = 0; + if (sscanf(buff1, "%d", &fport) != 1) { + lprint("Error: Bad first argument\r\n"); + return; + } + } + + if (sscanf(buff2, "%128[^:]:%128s", buff1, buff3) != 2) { + buff3 = 0; + if (sscanf(buff2, "%256s", buff1) != 1) { + lprint("Error: Bad second argument\r\n"); + return; + } + } + + if (buff3) { + if (strcmp(buff3, "lowdelay") == 0) + tos = IPTOS_LOWDELAY; + else if (strcmp(buff3, "throughput") == 0) + tos = IPTOS_THROUGHPUT; + else { + lprint("Error: Expecting \"lowdelay\"/\"throughput\"\r\n"); + return; + } + } + + if (strcmp(buff1, "ftp") == 0) + emu = EMU_FTP; + else if (strcmp(buff1, "irc") == 0) + emu = EMU_IRC; + else if (strcmp(buff1, "none") == 0) + emu = EMU_NONE; /* ie: no emulation */ + else { + lprint("Error: Unknown service\r\n"); + return; + } + + /* First, check that it isn't already emulated */ + for (emup = tcpemu; emup; emup = emup->next) { + if (emup->lport == lport && emup->fport == fport) { + lprint("Error: port already emulated\r\n"); + return; + } + } + + /* link it */ + emup = (struct emu_t *)malloc(sizeof (struct emu_t)); + emup->lport = (u_int16_t)lport; + emup->fport = (u_int16_t)fport; + emup->tos = tos; + emup->emu = emu; + emup->next = tcpemu; + tcpemu = emup; + + /* And finally, mark all current sessions, if any, as being emulated */ + for (so = tcb.so_next; so != &tcb; so = so->so_next) { + if ((lport && lport == ntohs(so->so_lport)) || + (fport && fport == ntohs(so->so_fport))) { + if (emu) + so->so_emu = emu; + if (tos) + so->so_iptos = tos; + } + } + + lprint("Adding emulation for %s to port %d/%d\r\n", buff1, emup->lport, emup->fport); +} + +#ifdef BAD_SPRINTF + +#undef vsprintf +#undef sprintf + +/* + * Some BSD-derived systems have a sprintf which returns char * + */ + +int +vsprintf_len(string, format, args) + char *string; + const char *format; + va_list args; +{ + vsprintf(string, format, args); + return strlen(string); +} + +int +#ifdef __STDC__ +sprintf_len(char *string, const char *format, ...) +#else +sprintf_len(va_alist) va_dcl +#endif +{ + va_list args; +#ifdef __STDC__ + va_start(args, format); +#else + char *string; + char *format; + va_start(args); + string = va_arg(args, char *); + format = va_arg(args, char *); +#endif + vsprintf(string, format, args); + return strlen(string); +} + +#endif + +void +u_sleep(usec) + int usec; +{ + struct timeval t; + fd_set fdset; + + FD_ZERO(&fdset); + + t.tv_sec = 0; + t.tv_usec = usec * 1000; + + select(0, &fdset, &fdset, &fdset, &t); +} + +/* + * Set fd blocking and non-blocking + */ + +void +fd_nonblock(fd) + int fd; +{ +#if defined USE_FIONBIO && defined FIONBIO + ioctlsockopt_t opt = 1; + + ioctlsocket(fd, FIONBIO, &opt); +#else + int opt; + + opt = fcntl(fd, F_GETFL, 0); + opt |= O_NONBLOCK; + fcntl(fd, F_SETFL, opt); +#endif +} + +void +fd_block(fd) + int fd; +{ +#if defined USE_FIONBIO && defined FIONBIO + ioctlsockopt_t opt = 0; + + ioctlsocket(fd, FIONBIO, &opt); +#else + int opt; + + opt = fcntl(fd, F_GETFL, 0); + opt &= ~O_NONBLOCK; + fcntl(fd, F_SETFL, opt); +#endif +} + + +#if 0 +/* + * invoke RSH + */ +int +rsh_exec(so,ns, user, host, args) + struct SLIRPsocket *so; + struct SLIRPsocket *ns; + char *user; + char *host; + char *args; +{ + int fd[2]; + int fd0[2]; + int s; + char buff[256]; + + DEBUG_CALL("rsh_exec"); + DEBUG_ARG("so = %lx", (long)so); + + if (pipe(fd)<0) { + lprint("Error: pipe failed: %s\n", strerror(errno)); + return 0; + } +/* #ifdef HAVE_SOCKETPAIR */ +#if 1 + if (socketpair(PF_UNIX,SOCK_STREAM,0, fd0) == -1) { + close(fd[0]); + close(fd[1]); + lprint("Error: openpty failed: %s\n", strerror(errno)); + return 0; + } +#else + if (slirp_openpty(&fd0[0], &fd0[1]) == -1) { + close(fd[0]); + close(fd[1]); + lprint("Error: openpty failed: %s\n", strerror(errno)); + return 0; + } +#endif + + switch(fork()) { + case -1: + lprint("Error: fork failed: %s\n", strerror(errno)); + close(fd[0]); + close(fd[1]); + close(fd0[0]); + close(fd0[1]); + return 0; + + case 0: + close(fd[0]); + close(fd0[0]); + + /* Set the DISPLAY */ + if (x_port >= 0) { +#ifdef HAVE_SETENV + sprintf(buff, "%s:%d.%d", inet_ntoa(our_addr), x_port, x_screen); + setenv("DISPLAY", buff, 1); +#else + sprintf(buff, "DISPLAY=%s:%d.%d", inet_ntoa(our_addr), x_port, x_screen); + putenv(buff); +#endif + } + + dup2(fd0[1], 0); + dup2(fd0[1], 1); + dup2(fd[1], 2); + for (s = 3; s <= 255; s++) + close(s); + + execlp("rsh","rsh","-l", user, host, args, NULL); + + /* Ooops, failed, let's tell the user why */ + + sprintf(buff, "Error: execlp of %s failed: %s\n", + "rsh", strerror(errno)); + write(2, buff, strlen(buff)+1); + close(0); close(1); close(2); /* XXX */ + exit(1); + + default: + close(fd[1]); + close(fd0[1]); + ns->s=fd[0]; + so->s=fd0[0]; + + return 1; + } +} +#endif diff --git a/src/slirp/misc.h b/src/slirp/misc.h new file mode 100644 index 0000000..4ab838d --- /dev/null +++ b/src/slirp/misc.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) 1995 Danny Gasparovski. + * + * Please read the file COPYRIGHT for the + * terms and conditions of the copyright. + */ + +#ifndef _MISC_H_ +#define _MISC_H_ + +struct ex_list { + int ex_pty; /* Do we want a pty? */ + int ex_addr; /* The last byte of the address */ + int ex_fport; /* Port to telnet to */ + char *ex_exec; /* Command line of what to exec */ + struct ex_list *ex_next; +}; + +extern struct ex_list *exec_list; +extern u_int curtime, time_fasttimo, last_slowtimo, detach_time, detach_wait; + +extern int (*lprint_print) _P((void *, const char *, va_list)); +extern char *lprint_ptr, *lprint_ptr2, **lprint_arg; +extern struct sbuf *lprint_sb; + +#ifndef HAVE_STRDUP +char *strdup _P((const char *)); +#endif + +void do_wait _P((int)); + +#define EMU_NONE 0x0 + +/* TCP emulations */ +#define EMU_CTL 0x1 +#define EMU_FTP 0x2 +#define EMU_KSH 0x3 +#define EMU_IRC 0x4 +#define EMU_REALAUDIO 0x5 +#define EMU_RLOGIN 0x6 +#define EMU_IDENT 0x7 +#define EMU_RSH 0x8 + +#define EMU_NOCONNECT 0x10 /* Don't connect */ + +/* UDP emulations */ +#define EMU_TALK 0x1 +#define EMU_NTALK 0x2 +#define EMU_CUSEEME 0x3 + +struct tos_t { + u_int16_t lport; + u_int16_t fport; + u_int8_t tos; + u_int8_t emu; +}; + +struct emu_t { + u_int16_t lport; + u_int16_t fport; + u_int8_t tos; + u_int8_t emu; + struct emu_t *next; +}; + +extern struct emu_t *tcpemu; + +extern int x_port, x_server, x_display; + +int show_x _P((char *, struct SLIRPsocket *)); +void redir_x _P((u_int32_t, int, int, int)); +void getouraddr _P((void)); +void slirp_insque _P((void *, void *)); +void slirp_remque _P((void *)); +int add_exec _P((struct ex_list **, int, char *, int, int)); +int slirp_openpty _P((int *, int *)); +int fork_exec _P((struct SLIRPsocket *, char *, int)); +void snooze_hup _P((int)); +void snooze _P((void)); +void relay _P((int)); +void add_emu _P((char *)); +void u_sleep _P((int)); +void fd_nonblock _P((int)); +void fd_block _P((int)); +int rsh_exec _P((struct SLIRPsocket *, struct SLIRPsocket *, char *, char *, char *)); + +#endif diff --git a/src/slirp/queue.c b/src/slirp/queue.c new file mode 100644 index 0000000..39405dc --- /dev/null +++ b/src/slirp/queue.c @@ -0,0 +1,116 @@ +/* + * File: queue.c + * Author: Robert I. Pitts + * Last Modified: March 9, 2000 + * Topic: Queue - Array Implementation + * ---------------------------------------------------------------- + */ + +#include +#include +#include "queue.h" + +/* + * Constants + * --------- + * MAX_QUEUE_SIZE = Largest number of items queue can hold. + */ + +#define MAX_QUEUE_SIZE 100 + +/* + * struct queueCDT gives the implementation of a queue. + * It holds the information that we need for each queue. + */ +typedef struct queueCDT { + queueElementT contents[MAX_QUEUE_SIZE]; + int front; + int count; +} queueCDT; + +queueADT QueueCreate(void) +{ + queueADT queue; + + queue = (queueADT)malloc(sizeof(queueCDT)); + + if (queue == NULL) { + fprintf(stderr, "Insufficient Memory for new Queue.\n"); + exit(ERROR_MEMORY); /* Exit program, returning error code. */ + } + + queue->front = 0; + queue->count = 0; + + return queue; +} + +void QueueDestroy(queueADT queue) +{ + free(queue); +} + +void QueueEnter(queueADT queue, queueElementT element) +{ + int newElementIndex; + + if (queue->count >= MAX_QUEUE_SIZE) { +// fprintf(stderr, "QueueEnter on Full Queue.\n"); +// exit(ERROR_QUEUE); /* Exit program, returning error code. */ + return; + } + + /* + * Calculate index at which to put + * next element. + */ + newElementIndex = (queue->front + queue->count) + % MAX_QUEUE_SIZE; + queue->contents[newElementIndex] = element; +//printf("element %d, pointer to %d, [%s]\n",newElementIndex,element,element); + + queue->count++; +} + +int QueuePeek(queueADT queue) +{ +return queue->count; +} + +queueElementT QueueDelete(queueADT queue) +{ + queueElementT oldElement; + + if (queue->count <= 0) { + //fprintf(stderr, "QueueDelete on Empty Queue.\n"); + //exit(ERROR_QUEUE); /* Exit program, returning error code. */ + return NULL; + } + + /* Save the element so we can return it. */ + oldElement = queue->contents[queue->front]; + + /* + * Advance the index of the front, + * making sure it wraps around the + * array properly. + */ + queue->front++; + queue->front %= MAX_QUEUE_SIZE; + +//printf("dequing @%d [%s]\n",oldElement,oldElement); + + queue->count--; + + return oldElement; +} + +int QueueIsEmpty(queueADT queue) +{ + return queue->count <= 0; +} + +int QueueIsFull(queueADT queue) +{ + return queue->count >= MAX_QUEUE_SIZE; +} diff --git a/src/slirp/queue.h b/src/slirp/queue.h new file mode 100644 index 0000000..c75ec70 --- /dev/null +++ b/src/slirp/queue.h @@ -0,0 +1,101 @@ +/* + * File: queue.h + * Author: Robert I. Pitts + * Last Modified: March 9, 2000 + * Topic: Queue - Array Implementation + * ---------------------------------------------------------------- + */ + +#ifndef _QUEUE_H +#define _QUEUE_H + +/* + * Constants + * --------- + * ERROR_* These signal error conditions in queue functions + * and are used as exit codes for the program. + */ +#define ERROR_QUEUE 2 +#define ERROR_MEMORY 3 + +/* + * Type: queueElementT + * ------------------- + * This is the type of objects held in the queue. + */ + +/*typedef char queueElementT; +typedef unsigned char *queueElementT; +*/ + +struct queuepacket{ + int len; + unsigned char data[2000]; +}; +typedef struct queuepacket *queueElementT; + +/* + * Type: queueADT + * -------------- + * The actual implementation of a queue is completely + * hidden. Client will work with queueADT which is a + * pointer to underlying queueCDT. + */ + +/* + * NOTE: need word struct below so that the compiler + * knows at least that a queueCDT will be some sort + * of struct. + */ + +typedef struct queueCDT *queueADT; + +/* + * Function: QueueCreate + * Usage: queue = QueueCreate(); + * ------------------------- + * A new empty queue is created and returned. + */ + +queueADT QueueCreate(void); + +/* Function: QueueDestroy + * Usage: QueueDestroy(queue); + * ----------------------- + * This function frees all memory associated with + * the queue. "queue" may not be used again unless + * queue = QueueCreate() is called first. + */ + +void QueueDestroy(queueADT queue); + +/* + * Functions: QueueEnter, QueueDelete + * Usage: QueueEnter(queue, element); + * element = QueueDelete(queue); + * -------------------------------------------- + * These are the fundamental queue operations that enter + * elements in and delete elements from the queue. A call + * to QueueDelete() on an empty queue or to QueueEnter() + * on a full queue is an error. Make use of QueueIsFull() + * and QueueIsEmpty() (see below) to avoid these errors. + */ + +void QueueEnter(queueADT queue, queueElementT element); +queueElementT QueueDelete(queueADT queue); + + +/* + * Functions: QueueIsEmpty, QueueIsFull + * Usage: if (QueueIsEmpty(queue)) ... + * ----------------------------------- + * These return a true/false value based on whether + * the queue is empty or full, respectively. + */ + +int QueueIsEmpty(queueADT queue); +int QueueIsFull(queueADT queue); + +int QueuePeek(queueADT queue); + +#endif /* not defined _QUEUE_H */ diff --git a/src/slirp/sbuf.c b/src/slirp/sbuf.c new file mode 100644 index 0000000..259f627 --- /dev/null +++ b/src/slirp/sbuf.c @@ -0,0 +1,202 @@ +/* + * Copyright (c) 1995 Danny Gasparovski. + * + * Please read the file COPYRIGHT for the + * terms and conditions of the copyright. + */ + +#include +#include "slirp.h" + +/* Done as a macro in socket.h */ +/* int + * sbspace(struct sockbuff *sb) + * { + * return SB_DATALEN - sb->sb_cc; + * } + */ + +void +sbfree(sb) + struct sbuf *sb; +{ + free(sb->sb_data); +} + +void +sbdrop(sb, num) + struct sbuf *sb; + int num; +{ + /* + * We can only drop how much we have + * This should never succeed + */ + if(num > sb->sb_cc) + num = sb->sb_cc; + sb->sb_cc -= num; + sb->sb_rptr += num; + if(sb->sb_rptr >= sb->sb_data + sb->sb_datalen) + sb->sb_rptr -= sb->sb_datalen; + +} + +void +sbreserve(sb, size) + struct sbuf *sb; + int size; +{ + if (sb->sb_data) { + /* Already alloced, realloc if necessary */ + if (sb->sb_datalen != size) { + sb->sb_wptr = sb->sb_rptr = sb->sb_data = (char *)realloc(sb->sb_data, size); + sb->sb_cc = 0; + if (sb->sb_wptr) + sb->sb_datalen = size; + else + sb->sb_datalen = 0; + } + } else { + sb->sb_wptr = sb->sb_rptr = sb->sb_data = (char *)malloc(size); + sb->sb_cc = 0; + if (sb->sb_wptr) + sb->sb_datalen = size; + else + sb->sb_datalen = 0; + } +} + +/* + * Try and write() to the socket, whatever doesn't get written + * append to the buffer... for a host with a fast net connection, + * this prevents an unnecessary copy of the data + * (the socket is non-blocking, so we won't hang) + */ +void +sbappend(so, m) + struct SLIRPsocket *so; + struct SLIRPmbuf *m; +{ + int ret = 0; + + DEBUG_CALL("sbappend"); + DEBUG_ARG("so = %lx", (long)so); + DEBUG_ARG("m = %lx", (long)m); + DEBUG_ARG("m->m_len = %d", m->m_len); + + /* Shouldn't happen, but... e.g. foreign host closes connection */ + if (m->m_len <= 0) { + m_free(m); + return; + } + + /* + * If there is urgent data, call sosendoob + * if not all was sent, sowrite will take care of the rest + * (The rest of this function is just an optimisation) + */ + if (so->so_urgc) { + sbappendsb(&so->so_rcv, m); + m_free(m); + sosendoob(so); + return; + } + + /* + * We only write if there's nothing in the buffer, + * ottherwise it'll arrive out of order, and hence corrupt + */ + if (!so->so_rcv.sb_cc) + ret = send(so->s, m->m_data, m->m_len, 0); + + if (ret <= 0) { + /* + * Nothing was written + * It's possible that the socket has closed, but + * we don't need to check because if it has closed, + * it will be detected in the normal way by soread() + */ + sbappendsb(&so->so_rcv, m); + } else if (ret != m->m_len) { + /* + * Something was written, but not everything.. + * sbappendsb the rest + */ + m->m_len -= ret; + m->m_data += ret; + sbappendsb(&so->so_rcv, m); + } /* else */ + /* Whatever happened, we free the SLIRPmbuf */ + m_free(m); +} + +/* + * Copy the data from m into sb + * The caller is responsible to make sure there's enough room + */ +void +sbappendsb(sb, m) + struct sbuf *sb; + struct SLIRPmbuf *m; +{ + int len, n, nn; + + len = m->m_len; + + if (sb->sb_wptr < sb->sb_rptr) { + n = sb->sb_rptr - sb->sb_wptr; + if (n > len) n = len; + memcpy(sb->sb_wptr, m->m_data, n); + } else { + /* Do the right edge first */ + n = sb->sb_data + sb->sb_datalen - sb->sb_wptr; + if (n > len) n = len; + memcpy(sb->sb_wptr, m->m_data, n); + len -= n; + if (len) { + /* Now the left edge */ + nn = sb->sb_rptr - sb->sb_data; + if (nn > len) nn = len; + memcpy(sb->sb_data,m->m_data+n,nn); + n += nn; + } + } + + sb->sb_cc += n; + sb->sb_wptr += n; + if (sb->sb_wptr >= sb->sb_data + sb->sb_datalen) + sb->sb_wptr -= sb->sb_datalen; +} + +/* + * Copy data from sbuf to a normal, straight buffer + * Don't update the sbuf rptr, this will be + * done in sbdrop when the data is acked + */ +void +sbcopy(sb, off, len, to) + struct sbuf *sb; + int off; + int len; + char *to; +{ + char *from; + + from = sb->sb_rptr + off; + if (from >= sb->sb_data + sb->sb_datalen) + from -= sb->sb_datalen; + + if (from < sb->sb_wptr) { + if (len > sb->sb_cc) len = sb->sb_cc; + memcpy(to,from,len); + } else { + /* re-use off */ + off = (sb->sb_data + sb->sb_datalen) - from; + if (off > len) off = len; + memcpy(to,from,off); + len -= off; + if (len) + memcpy(to+off,sb->sb_data,len); + } +} + diff --git a/src/slirp/sbuf.h b/src/slirp/sbuf.h new file mode 100644 index 0000000..eff9481 --- /dev/null +++ b/src/slirp/sbuf.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 1995 Danny Gasparovski. + * + * Please read the file COPYRIGHT for the + * terms and conditions of the copyright. + */ + +#ifndef _SBUF_H_ +#define _SBUF_H_ + +#define sbflush(sb) sbdrop((sb),(sb)->sb_cc) +#define sbspace(sb) ((sb)->sb_datalen - (sb)->sb_cc) + +struct sbuf { + u_int sb_cc; /* actual chars in buffer */ + u_int sb_datalen; /* Length of data */ + char *sb_wptr; /* write pointer. points to where the next + * bytes should be written in the sbuf */ + char *sb_rptr; /* read pointer. points to where the next + * byte should be read from the sbuf */ + char *sb_data; /* Actual data */ +}; + +void sbfree _P((struct sbuf *)); +void sbdrop _P((struct sbuf *, int)); +void sbreserve _P((struct sbuf *, int)); +void sbappend _P((struct SLIRPsocket *, struct SLIRPmbuf *)); +void sbappendsb _P((struct sbuf *, struct SLIRPmbuf *)); +void sbcopy _P((struct sbuf *, int, int, char *)); + +#endif diff --git a/src/slirp/slirp.c b/src/slirp/slirp.c new file mode 100644 index 0000000..1ac66ae --- /dev/null +++ b/src/slirp/slirp.c @@ -0,0 +1,682 @@ +#include "slirp.h" + +/* host address */ +struct in_addr our_addr; +/* host dns address */ +struct in_addr dns_addr; +/* host loopback address */ +struct in_addr loopback_addr; + +/* address for slirp virtual addresses */ +struct in_addr special_addr; +/* virtual address alias for host */ +struct in_addr alias_addr; + +const uint8_t special_ethaddr[6] = { + 0x52, 0x54, 0x00, 0x12, 0x35, 0x00 +}; + +uint8_t client_ethaddr[6]; + +int do_slowtimo; +int link_up; +struct timeval tt; +FILE *lfd; +struct ex_list *exec_list; + +/* XXX: suppress those select globals */ +fd_set *global_readfds, *global_writefds, *global_xfds; + +char slirp_hostname[33]; + +#ifdef _WIN32 + +static int get_dns_addr(struct in_addr *pdns_addr) +{ + FIXED_INFO *FixedInfo=NULL; + ULONG BufLen; + DWORD ret; + IP_ADDR_STRING *pIPAddr; + struct in_addr tmp_addr; + + FixedInfo = (FIXED_INFO *)GlobalAlloc(GPTR, sizeof(FIXED_INFO)); + BufLen = sizeof(FIXED_INFO); + + if (ERROR_BUFFER_OVERFLOW == GetNetworkParams(FixedInfo, &BufLen)) { + if (FixedInfo) { + GlobalFree(FixedInfo); + FixedInfo = NULL; + } + FixedInfo = GlobalAlloc(GPTR, BufLen); + } + + if ((ret = GetNetworkParams(FixedInfo, &BufLen)) != ERROR_SUCCESS) { + printf("GetNetworkParams failed. ret = %08x\n", (u_int)ret ); + if (FixedInfo) { + GlobalFree(FixedInfo); + FixedInfo = NULL; + } + return -1; + } + + pIPAddr = &(FixedInfo->DnsServerList); + inet_aton(pIPAddr->IpAddress.String, &tmp_addr); + *pdns_addr = tmp_addr; +#if 0 + printf( "DNS Servers:\n" ); + printf( "DNS Addr:%s\n", pIPAddr->IpAddress.String ); + + pIPAddr = FixedInfo -> DnsServerList.Next; + while ( pIPAddr ) { + printf( "DNS Addr:%s\n", pIPAddr ->IpAddress.String ); + pIPAddr = pIPAddr ->Next; + } +#endif + if (FixedInfo) { + GlobalFree(FixedInfo); + FixedInfo = NULL; + } + return 0; +} + +#else + +static int get_dns_addr(struct in_addr *pdns_addr) +{ + char buff[512]; + char buff2[256]; + FILE *f; + int found = 0; + struct in_addr tmp_addr; + + f = fopen("/etc/resolv.conf", "r"); + if (!f) + return -1; + + lprint("IP address of your DNS(s): "); + while (fgets(buff, 512, f) != NULL) { + if (sscanf(buff, "nameserver%*[ \t]%256s", buff2) == 1) { + if (!inet_aton(buff2, &tmp_addr)) + continue; + if (tmp_addr.s_addr == loopback_addr.s_addr) + tmp_addr = our_addr; + /* If it's the first one, set it to dns_addr */ + if (!found) + *pdns_addr = tmp_addr; + else + lprint(", "); + if (++found > 3) { + lprint("(more)"); + break; + } else + lprint("%s", inet_ntoa(tmp_addr)); + } + } + fclose(f); + if (!found) + return -1; + return 0; +} + +#endif + +#ifdef _WIN32 +void slirp_cleanup(void) +{ + WSACleanup(); +} +#endif + +int slirp_init(void) +{ +#ifdef SLIRP_DEBUG + // debug_init("/tmp/slirp.log", DEBUG_DEFAULT); + // debug_init("slirplog.txt",DEBUG_DEFAULT); + //debug_init("slirplog.txt",DBG_CALL); +debug_init("slirplog.txt",DEBUG_DEFAULT); +#endif + +#ifdef _WIN32 + { + WSADATA Data; + WSAStartup(MAKEWORD(2,0), &Data); + atexit(slirp_cleanup); + } +#endif + + link_up = 1; + + if_init(); + ip_init(); + + /* Initialise mbufs *after* setting the MTU */ + m_init(); + + /* set default addresses */ + inet_aton("127.0.0.1", &loopback_addr); + + if (get_dns_addr(&dns_addr) < 0) + return -1; + + inet_aton(CTL_SPECIAL, &special_addr); + alias_addr.s_addr = special_addr.s_addr | htonl(CTL_ALIAS); + getouraddr(); + return 0; +} + +#define CONN_CANFSEND(so) (((so)->so_state & (SS_FCANTSENDMORE|SS_ISFCONNECTED)) == SS_ISFCONNECTED) +#define CONN_CANFRCV(so) (((so)->so_state & (SS_FCANTRCVMORE|SS_ISFCONNECTED)) == SS_ISFCONNECTED) +#define UPD_NFDS(x) if (nfds < (x)) nfds = (x) + +/* + * curtime kept to an accuracy of 1ms + */ +#ifdef _WIN32 +static void updtime(void) +{ + struct _timeb tb; + + _ftime(&tb); + curtime = (u_int)tb.time * (u_int)1000; + curtime += (u_int)tb.millitm; +} +#else +static void updtime(void) +{ + gettimeofday(&tt, 0); + + curtime = (u_int)tt.tv_sec * (u_int)1000; + curtime += (u_int)tt.tv_usec / (u_int)1000; + + if ((tt.tv_usec % 1000) >= 500) + curtime++; +} +#endif + +int slirp_select_fill(int *pnfds, + fd_set *readfds, fd_set *writefds, fd_set *xfds) +{ + struct SLIRPsocket *so, *so_next; + int nfds; + int timeout, tmp_time; + + /* fail safe */ + global_readfds = NULL; + global_writefds = NULL; + global_xfds = NULL; + + nfds = *pnfds; + /* + * First, TCP sockets + */ + do_slowtimo = 0; + if (link_up) { + /* + * *_slowtimo needs calling if there are IP fragments + * in the fragment queue, or there are TCP connections active + */ + do_slowtimo = ((tcb.so_next != &tcb) || + ((struct ipasfrag *)&ipq != (struct ipasfrag *)ipq.next)); + + for (so = tcb.so_next; (so != &tcb); so = so_next) { + so_next = so->so_next; + + + /* + * See if we need a tcp_fasttimo + */ + if(&so->so_tcpcb->t_flags!=0x0){ //This is to prevent a common lockup. + if (time_fasttimo == 0 && so->so_tcpcb->t_flags & TF_DELACK) + time_fasttimo = curtime; }/* Flag when we want a fasttimo */ + + + /* + * NOFDREF can include still connecting to local-host, + * newly socreated() sockets etc. Don't want to select these. + */ + if (so->so_state & SS_NOFDREF || so->s == -1) + continue; + + /* + * Set for reading sockets which are accepting + */ + if (so->so_state & SS_FACCEPTCONN) { + FD_SET(so->s, readfds); + UPD_NFDS(so->s); + continue; + } + + /* + * Set for writing sockets which are connecting + */ + if (so->so_state & SS_ISFCONNECTING) { + FD_SET(so->s, writefds); + UPD_NFDS(so->s); + continue; + } + + /* + * Set for writing if we are connected, can send more, and + * we have something to send + */ + if (CONN_CANFSEND(so) && so->so_rcv.sb_cc) { + FD_SET(so->s, writefds); + UPD_NFDS(so->s); + } + + /* + * Set for reading (and urgent data) if we are connected, can + * receive more, and we have room for it XXX /2 ? + */ + if (CONN_CANFRCV(so) && (so->so_snd.sb_cc < (so->so_snd.sb_datalen/2))) { + FD_SET(so->s, readfds); + FD_SET(so->s, xfds); + UPD_NFDS(so->s); + } + } + + /* + * UDP sockets + */ + for (so = udb.so_next; so != &udb; so = so_next) { + so_next = so->so_next; + + /* + * See if it's timed out + */ + if (so->so_expire) { + if (so->so_expire <= curtime) { + udp_detach(so); + continue; + } else + do_slowtimo = 1; /* Let socket expire */ + } + + /* + * When UDP packets are received from over the + * link, they're sendto()'d straight away, so + * no need for setting for writing + * Limit the number of packets queued by this session + * to 4. Note that even though we try and limit this + * to 4 packets, the session could have more queued + * if the packets needed to be fragmented + * (XXX <= 4 ?) + */ + if ((so->so_state & SS_ISFCONNECTED) && so->so_queued <= 4) { + FD_SET(so->s, readfds); + UPD_NFDS(so->s); + } + } + } + + /* + * Setup timeout to use minimum CPU usage, especially when idle + */ + + timeout = -1; + + /* + * If a slowtimo is needed, set timeout to 5ms from the last + * slow timeout. If a fast timeout is needed, set timeout within + * 2ms of when it was requested. + */ +# define SLOW_TIMO 5 +# define FAST_TIMO 2 + if (do_slowtimo) { + timeout = (SLOW_TIMO - (curtime - last_slowtimo)) * 1000; + if (timeout < 0) + timeout = 0; + else if (timeout > (SLOW_TIMO * 1000)) + timeout = SLOW_TIMO * 1000; + + /* Can only fasttimo if we also slowtimo */ + if (time_fasttimo) { + tmp_time = (FAST_TIMO - (curtime - time_fasttimo)) * 1000; + if (tmp_time < 0) + tmp_time = 0; + + /* Choose the smallest of the 2 */ + if (tmp_time < timeout) + timeout = tmp_time; + } + } + *pnfds = nfds; + + /* + * Adjust the timeout to make the minimum timeout + * 2ms (XXX?) to lessen the CPU load + */ + if (timeout < (FAST_TIMO * 1000)) + timeout = FAST_TIMO * 1000; + + return timeout; +} + +void slirp_select_poll(fd_set *readfds, fd_set *writefds, fd_set *xfds) +{ + struct SLIRPsocket *so, *so_next; + int ret; + + global_readfds = readfds; + global_writefds = writefds; + global_xfds = xfds; + + /* Update time */ + updtime(); + + /* + * See if anything has timed out + */ + if (link_up) { + if (time_fasttimo && ((curtime - time_fasttimo) >= FAST_TIMO)) { + tcp_fasttimo(); + time_fasttimo = 0; + } + if (do_slowtimo && ((curtime - last_slowtimo) >= SLOW_TIMO)) { + ip_slowtimo(); + tcp_slowtimo(); + last_slowtimo = curtime; + } + } + + /* + * Check sockets + */ + if (link_up) { + /* + * Check TCP sockets + */ + for (so = tcb.so_next; so != &tcb; so = so_next) { + so_next = so->so_next; + + /* + * FD_ISSET is meaningless on these sockets + * (and they can crash the program) + */ + if (so->so_state & SS_NOFDREF || so->s == -1) + continue; + + /* + * Check for URG data + * This will soread as well, so no need to + * test for readfds below if this succeeds + */ + if (FD_ISSET(so->s, xfds)) + sorecvoob(so); + /* + * Check sockets for reading + */ + else if (FD_ISSET(so->s, readfds)) { + /* + * Check for incoming connections + */ + if (so->so_state & SS_FACCEPTCONN) { + tcp_connect(so); + continue; + } /* else */ + ret = soread(so); + + /* Output it if we read something */ + if (ret > 0) + tcp_output(sototcpcb(so)); + } + + /* + * Check sockets for writing + */ + if (FD_ISSET(so->s, writefds)) { + /* + * Check for non-blocking, still-connecting sockets + */ + if (so->so_state & SS_ISFCONNECTING) { + /* Connected */ + so->so_state &= ~SS_ISFCONNECTING; + + //ret = send(so->s, &ret, 0, 0); + //winsock2.h:549:32: note: expected 'const char *' but argument is of type 'int *' + //WINSOCK_API_LINKAGE int PASCAL send(SOCKET,const char*,int,int); JASON + //ret = send(so->s, "a", 1, 0); WHY THE HELL WAS THIS HERE?! + ret = send(so->s, &ret, 0, 0); //This is what it should be. + if (ret < 0) { + /* XXXXX Must fix, zero bytes is a NOP */ + if (errno == EAGAIN || errno == EWOULDBLOCK || + errno == EINPROGRESS || errno == ENOTCONN) + continue; + + /* else failed */ + so->so_state = SS_NOFDREF; + } + /* else so->so_state &= ~SS_ISFCONNECTING; */ + + /* + * Continue tcp_input + */ + tcp_input((struct SLIRPmbuf *)NULL, sizeof(struct ip), so); + /* continue; */ + } else + ret = sowrite(so); + /* + * XXXXX If we wrote something (a lot), there + * could be a need for a window update. + * In the worst case, the remote will send + * a window probe to get things going again + */ + } + + /* + * Probe a still-connecting, non-blocking socket + * to check if it's still alive + */ +#ifdef PROBE_CONN + if (so->so_state & SS_ISFCONNECTING) { + ret = recv(so->s, (char *)&ret, 0,0); + + if (ret < 0) { + /* XXX */ + if (errno == EAGAIN || errno == EWOULDBLOCK || + errno == EINPROGRESS || errno == ENOTCONN) + continue; /* Still connecting, continue */ + + /* else failed */ + so->so_state = SS_NOFDREF; + + /* tcp_input will take care of it */ + } else { + ret = send(so->s, &ret, 0,0); + if (ret < 0) { + /* XXX */ + if (errno == EAGAIN || errno == EWOULDBLOCK || + errno == EINPROGRESS || errno == ENOTCONN) + continue; + /* else failed */ + so->so_state = SS_NOFDREF; + } else + so->so_state &= ~SS_ISFCONNECTING; + + } + tcp_input((struct SLIRPmbuf *)NULL, sizeof(struct ip),so); + } /* SS_ISFCONNECTING */ +#endif + } + + /* + * Now UDP sockets. + * Incoming packets are sent straight away, they're not buffered. + * Incoming UDP data isn't buffered either. + */ + for (so = udb.so_next; so != &udb; so = so_next) { + so_next = so->so_next; + + if (so->s != -1 && FD_ISSET(so->s, readfds)) { + sorecvfrom(so); + } + } + } + + /* + * See if we can start outputting + */ + if (if_queued && link_up) + if_start(); + + /* clear global file descriptor sets. + * these reside on the stack in vl.c + * so they're unusable if we're not in + * slirp_select_fill or slirp_select_poll. + */ + global_readfds = NULL; + global_writefds = NULL; + global_xfds = NULL; +} + +#define ETH_ALEN 6 +#define ETH_HLEN 14 + +#define ETH_P_IP 0x0800 /* Internet Protocol packet */ +#define ETH_P_ARP 0x0806 /* Address Resolution packet */ + +#define ARPOP_REQUEST 1 /* ARP request */ +#define ARPOP_REPLY 2 /* ARP reply */ + +struct ethhdr +{ + unsigned char h_dest[ETH_ALEN]; /* destination eth addr */ + unsigned char h_source[ETH_ALEN]; /* source ether addr */ + unsigned short h_proto; /* packet type ID field */ +}; + +struct arphdr +{ + unsigned short ar_hrd; /* format of hardware address */ + unsigned short ar_pro; /* format of protocol address */ + unsigned char ar_hln; /* length of hardware address */ + unsigned char ar_pln; /* length of protocol address */ + unsigned short ar_op; /* ARP opcode (command) */ + + /* + * Ethernet looks like this : This bit is variable sized however... + */ + unsigned char ar_sha[ETH_ALEN]; /* sender hardware address */ + unsigned char ar_sip[4]; /* sender IP address */ + unsigned char ar_tha[ETH_ALEN]; /* target hardware address */ + unsigned char ar_tip[4]; /* target IP address */ +}; + +void arp_input(const uint8_t *pkt, int pkt_len) +{ + struct ethhdr *eh = (struct ethhdr *)pkt; + struct arphdr *ah = (struct arphdr *)(pkt + ETH_HLEN); + uint8_t arp_reply[ETH_HLEN + sizeof(struct arphdr)]; + struct ethhdr *reh = (struct ethhdr *)arp_reply; + struct arphdr *rah = (struct arphdr *)(arp_reply + ETH_HLEN); + int ar_op; + struct ex_list *ex_ptr; + + ar_op = ntohs(ah->ar_op); + switch(ar_op) { + case ARPOP_REQUEST: + if (!memcmp(ah->ar_tip, &special_addr, 3)) { + if (ah->ar_tip[3] == CTL_DNS || ah->ar_tip[3] == CTL_ALIAS) + goto arp_ok; + for (ex_ptr = exec_list; ex_ptr; ex_ptr = ex_ptr->ex_next) { + if (ex_ptr->ex_addr == ah->ar_tip[3]) + goto arp_ok; + } + return; + arp_ok: + /* XXX: make an ARP request to have the client address */ + memcpy(client_ethaddr, eh->h_source, ETH_ALEN); + + /* ARP request for alias/dns mac address */ + memcpy(reh->h_dest, pkt + ETH_ALEN, ETH_ALEN); + memcpy(reh->h_source, special_ethaddr, ETH_ALEN - 1); + reh->h_source[5] = ah->ar_tip[3]; + reh->h_proto = htons(ETH_P_ARP); + + rah->ar_hrd = htons(1); + rah->ar_pro = htons(ETH_P_IP); + rah->ar_hln = ETH_ALEN; + rah->ar_pln = 4; + rah->ar_op = htons(ARPOP_REPLY); + memcpy(rah->ar_sha, reh->h_source, ETH_ALEN); + memcpy(rah->ar_sip, ah->ar_tip, 4); + memcpy(rah->ar_tha, ah->ar_sha, ETH_ALEN); + memcpy(rah->ar_tip, ah->ar_sip, 4); + slirp_output(arp_reply, sizeof(arp_reply)); + } + break; + default: + break; + } +} + +void slirp_input(const uint8_t *pkt, int pkt_len) +{ + struct SLIRPmbuf *m; + int proto; + + if (pkt_len < ETH_HLEN) + return; + + proto = (pkt[12] << 8) | pkt[13]; + switch(proto) { + case ETH_P_ARP: + arp_input(pkt, pkt_len); + break; + case ETH_P_IP: + m = m_get(); + if (!m) + return; + /* Note: we add to align the IP header */ + m->m_len = pkt_len + 2; + memcpy(m->m_data + 2, pkt, pkt_len); + + m->m_data += 2 + ETH_HLEN; + m->m_len -= 2 + ETH_HLEN; + + ip_input(m); + break; + default: + break; + } +} + +/* output the IP packet to the ethernet device */ +void if_encap(const uint8_t *ip_data, int ip_data_len) +{ + uint8_t buf[1600]; + struct ethhdr *eh = (struct ethhdr *)buf; + + if (ip_data_len + ETH_HLEN > sizeof(buf)) + return; + + memcpy(eh->h_dest, client_ethaddr, ETH_ALEN); + memcpy(eh->h_source, special_ethaddr, ETH_ALEN - 1); + /* XXX: not correct */ + eh->h_source[5] = CTL_ALIAS; + eh->h_proto = htons(ETH_P_IP); + memcpy(buf + sizeof(struct ethhdr), ip_data, ip_data_len); + slirp_output(buf, ip_data_len + ETH_HLEN); +} + +int slirp_redir(int is_udp, int host_port, + struct in_addr guest_addr, int guest_port) +{ + if (is_udp) { + if (!udp_listen(htons(host_port), guest_addr.s_addr, + htons(guest_port), 0)) + return -1; + } else { + if (!solisten(htons(host_port), guest_addr.s_addr, + htons(guest_port), 0)) + return -1; + } + return 0; +} + +int slirp_add_exec(int do_pty, const char *args, int addr_low_byte, + int guest_port) +{ + return add_exec(&exec_list, do_pty, (char *)args, + addr_low_byte, htons(guest_port)); +} diff --git a/src/slirp/slirp.h b/src/slirp/slirp.h new file mode 100644 index 0000000..83a1b6d --- /dev/null +++ b/src/slirp/slirp.h @@ -0,0 +1,415 @@ +#ifndef __COMMON_H__ +#define __COMMON_H__ + +#define SLIRP_VERSION "Cockatrice special" + +#define CONFIG_QEMU + +#ifndef CONFIG_QEMU +#include "version.h" +#endif +#include "config.h" +#include "slirp_config.h" + +#ifdef _WIN32 +#ifdef __GNUC__ //MINGW? +# include +typedef uint8_t u_int8_t; +typedef uint16_t u_int16_t; +typedef uint32_t u_int32_t; +typedef uint64_t u_int64_t; +typedef char *SLIRPcaddr_t; +typedef int socklen_t; +typedef unsigned long ioctlsockopt_t; +#else +typedef unsigned char u_int8_t; +typedef char int8_t; +typedef unsigned char uint8_t; +typedef unsigned short u_int16_t; +typedef unsigned short uint16_t; +typedef short int16_t; +typedef unsigned int u_int32_t; +typedef unsigned int uint32_t; +typedef int int32_t; + +typedef unsigned __int64 u_int64_t; +typedef char *SLIRPcaddr_t; +typedef int socklen_t; +typedef unsigned long ioctlsockopt_t; + +#endif + +# include //needs to be on top otherwise, it'll pull in winsock1 +# include + +# include +# include + +# define USE_FIONBIO 1 +# define EWOULDBLOCK WSAEWOULDBLOCK +# define EINPROGRESS WSAEINPROGRESS +# define ENOTCONN WSAENOTCONN +# define EHOSTUNREACH WSAEHOSTUNREACH +# define ENETUNREACH WSAENETUNREACH +# define ECONNREFUSED WSAECONNREFUSED + +/* Basilisk II Router defines those */ +# define udp_read_completion slirp_udp_read_completion +# define write_udp slirp_write_udp +# define init_udp slirp_init_udp +# define final_udp slirp_final_udp +#else +typedef int ioctlsockopt_t; +# define ioctlsocket ioctl +# define closesocket(s) close(s) +# define O_BINARY 0 +#endif + +#include +#ifdef HAVE_SYS_BITYPES_H +# include +#endif +#ifdef HAVE_STDINT_H +# include +#endif + +#ifndef _MSC_VER +#include +#else +#include +#endif + +#ifdef NEED_TYPEDEFS +typedef char int8_t; +typedef unsigned char u_int8_t; + +# if SIZEOF_SHORT == 2 + typedef short int16_t; + typedef unsigned short u_int16_t; +# else +# if SIZEOF_INT == 2 + typedef int int16_t; + typedef unsigned int u_int16_t; +# else + #error Cannot find a type with sizeof() == 2 +# endif +# endif + +# if SIZEOF_SHORT == 4 + typedef short int32_t; + typedef unsigned short u_int32_t; +# else +# if SIZEOF_INT == 4 + typedef int int32_t; + typedef unsigned int u_int32_t; +# else + #error Cannot find a type with sizeof() == 4 +# endif +# endif +#endif /* NEED_TYPEDEFS */ + +/* Basilisk II types glue */ +typedef u_int8_t uint8; +typedef u_int16_t uint16; +typedef u_int32_t uint32; + +#ifdef HAVE_UNISTD_H +# include +#endif + +#ifdef HAVE_STDLIB_H +# include +#endif + +#include +#include + +#ifndef HAVE_MEMMOVE +#define memmove(x, y, z) bcopy(y, x, z) +#endif + +#if TIME_WITH_SYS_TIME +# include +# include +#else +# if HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif + +#ifdef HAVE_STRING_H +# include +#else +#ifndef _MSC_VER +# include +#else +#include +#endif +#endif + +#ifndef _WIN32 +#include +#endif + +#ifndef _P +#ifndef NO_PROTOTYPES +# define _P(x) x +#else +# define _P(x) () +#endif +#endif + +#ifndef _WIN32 +#include +#include +#endif + +#ifdef GETTIMEOFDAY_ONE_ARG +#define gettimeofday(x, y) gettimeofday(x) +#endif + +/* Systems lacking strdup() definition in . */ +#if defined(ultrix) +char *strdup _P((const char *)); +#endif + +/* Systems lacking malloc() definition in . */ +#if defined(ultrix) || defined(hcx) +void *malloc _P((size_t arg)); +void free _P((void *ptr)); +#endif + +#ifndef HAVE_INET_ATON +int inet_aton _P((const char *cp, struct in_addr *ia)); +#endif + +#include +#ifndef NO_UNIX_SOCKETS +#include +#endif +#include +#ifdef HAVE_SYS_SIGNAL_H +# include +#endif +#ifndef _WIN32 +#include +#endif + +#if defined(HAVE_SYS_IOCTL_H) +# include +#endif + +#ifdef HAVE_SYS_SELECT_H +# include +#endif + +#ifdef HAVE_SYS_WAIT_H +# include +#endif + +#ifdef HAVE_SYS_FILIO_H +# include +#endif + +#ifdef USE_PPP +#include +#endif + +#ifdef __STDC__ +#include +#else +#include +#endif + +#include + +/* Avoid conflicting with the libc insque() and remque(), which + have different prototypes. */ +#define insque slirp_insque +#define remque slirp_remque + +#ifdef HAVE_SYS_STROPTS_H +#include +#endif + +#include "debug.h" + +#if defined __GNUC__ +#define PACKED__ __attribute__ ((packed)) +#elif defined __sgi +#define PRAGMA_PACK_SUPPORTED 1 +#define PACK_END 0 +#define PACKED__ +#elif _MSC_VER +//#define PRAGMA_PACK_SUPPORTED 1 +//#define PACK_END 4 +#define PACKED__ +#else +#error "Packed attribute or pragma shall be supported" +#endif + +#if defined(_MSC_VER) +#pragma pack(push, 1) +#endif + +#include "ip.h" +#include "tcp.h" +#include "tcp_timer.h" +#include "tcp_var.h" +#include "tcpip.h" +#include "udp.h" +#include "icmp_var.h" +#include "mbuf.h" +#include "sbuf.h" +#include "socket.h" +#include "if.h" +#include "main.h" +#include "misc.h" +#include "ctl.h" +#ifdef USE_PPP +#include "ppp/pppd.h" +#include "ppp/ppp.h" +#endif + +#include "bootp.h" +#include "tftp.h" +#include "libslirp.h" + +extern struct ttys *ttys_unit[MAX_INTERFACES]; + +#ifndef NULL +#define NULL (void *)0 +#endif + +#ifndef FULL_BOLT +void if_start _P((void)); +#else +void if_start _P((struct ttys *)); +#endif + +#ifdef BAD_SPRINTF +# define vsprintf vsprintf_len +# define sprintf sprintf_len + extern int vsprintf_len _P((char *, const char *, va_list)); + extern int sprintf_len _P((char *, const char *, ...)); +#endif + +#ifdef DECLARE_SPRINTF +# ifndef BAD_SPRINTF + extern int vsprintf _P((char *, const char *, va_list)); +# endif + extern int vfprintf _P((FILE *, const char *, va_list)); +#endif + +#ifndef HAVE_STRERROR +#ifndef _MSC_VER + extern char *strerror _P((int error)); + #define HAVE_STRERROR +#endif +#endif + +#ifndef HAVE_INDEX + char *index _P((const char *, int)); +#endif + +#ifndef HAVE_GETHOSTID + long gethostid _P((void)); +#endif + +void lprint _P((const char *, ...)); + +extern int do_echo; + +#ifdef _MSC_VER +#define inline +#endif + +#if SIZEOF_CHAR_P == 4 +# define insque_32 insque +# define remque_32 remque +#else + extern inline void insque_32 _P((void *, void *)); + extern inline void remque_32 _P((void *)); +#endif + +#ifndef _WIN32 +#include +#endif + +#define DEFAULT_BAUD 115200 + +/* cksum.c */ +int cksum(struct SLIRPmbuf *m, int len); + +/* if.c */ +void if_init _P((void)); +void if_output _P((struct SLIRPsocket *, struct SLIRPmbuf *)); + +/* ip_input.c */ +void ip_init _P((void)); +void ip_input _P((struct SLIRPmbuf *)); +struct ip * ip_reass _P((register struct ipasfrag *, register struct ipq *)); +void ip_freef _P((struct ipq *)); +void ip_enq _P((register struct ipasfrag *, register struct ipasfrag *)); +void ip_deq _P((register struct ipasfrag *)); +void ip_slowtimo _P((void)); +void ip_stripoptions _P((register struct SLIRPmbuf *, struct SLIRPmbuf *)); + +/* ip_output.c */ +int ip_output _P((struct SLIRPsocket *, struct SLIRPmbuf *)); + +/* tcp_input.c */ +int tcp_reass _P((register struct tcpcb *, register struct tcpiphdr *, struct SLIRPmbuf *)); +void tcp_input _P((register struct SLIRPmbuf *, int, struct SLIRPsocket *)); +void tcp_dooptions _P((struct tcpcb *, u_char *, int, struct tcpiphdr *)); +void tcp_xmit_timer _P((register struct tcpcb *, int)); +int tcp_mss _P((register struct tcpcb *, u_int)); + +/* tcp_output.c */ +int tcp_output _P((register struct tcpcb *)); +void tcp_setpersist _P((register struct tcpcb *)); + +/* tcp_subr.c */ +void tcp_init _P((void)); +void tcp_template _P((struct tcpcb *)); +void tcp_respond _P((struct tcpcb *, register struct tcpiphdr *, register struct SLIRPmbuf *, tcp_seq, tcp_seq, int)); +struct tcpcb * tcp_newtcpcb _P((struct SLIRPsocket *)); +struct tcpcb * tcp_close _P((register struct tcpcb *)); +void tcp_drain _P((void)); +void tcp_sockclosed _P((struct tcpcb *)); +int tcp_fconnect _P((struct SLIRPsocket *)); +void tcp_connect _P((struct SLIRPsocket *)); +int tcp_attach _P((struct SLIRPsocket *)); +u_int8_t tcp_tos _P((struct SLIRPsocket *)); +int tcp_emu _P((struct SLIRPsocket *, struct SLIRPmbuf *)); +int tcp_ctl _P((struct SLIRPsocket *)); +struct tcpcb *tcp_drop(struct tcpcb *tp, int err); + + +#if defined(_MSC_VER) +#pragma pack(pop) +#endif + +#ifdef USE_PPP +#define MIN_MRU MINMRU +#define MAX_MRU MAXMRU +#else +#define MIN_MRU 128 +#define MAX_MRU 16384 +#endif + +#ifndef _WIN32 +#define min(x,y) ((x) < (y) ? (x) : (y)) +#define max(x,y) ((x) > (y) ? (x) : (y)) +#endif + +#ifdef _WIN32 +#undef errno +#define errno (WSAGetLastError()) +#endif + +#define PROBE_CONN + +#endif diff --git a/src/slirp/slirp_config.h b/src/slirp/slirp_config.h new file mode 100644 index 0000000..850ddee --- /dev/null +++ b/src/slirp/slirp_config.h @@ -0,0 +1,135 @@ +/* + * User definable configuration options + */ + +/* Undefine if you don't want talk emulation */ +#undef EMULATE_TALK + +/* Define if you want the connection to be probed */ +/* XXX Not working yet, so ignore this for now */ +#undef PROBE_CONN + +/* Define to 1 if you want KEEPALIVE timers */ +#define DO_KEEPALIVE 0 + +/* Define to MAX interfaces you expect to use at once */ +/* MAX_INTERFACES determines the max. TOTAL number of interfaces (SLIP and PPP) */ +/* MAX_PPP_INTERFACES determines max. number of PPP interfaces */ +#define MAX_INTERFACES 1 +#define MAX_PPP_INTERFACES 1 + +/* Define if you want slirp's socket in /tmp */ +/* XXXXXX Do this in ./configure */ +#undef USE_TMPSOCKET + +/* Define if you want slirp to use cfsetXspeed() on the terminal */ +#undef DO_CFSETSPEED + +/* Define this if you want slirp to write to the tty as fast as it can */ +/* This should only be set if you are using load-balancing, slirp does a */ +/* pretty good job on single modems already, and seting this will make */ +/* interactive sessions less responsive */ +/* XXXXX Talk about having fast modem as unit 0 */ +#undef FULL_BOLT + +/* + * Define if you want slirp to use less CPU + * You will notice a small lag in interactive sessions, but it's not that bad + * Things like Netscape/ftp/etc. are completely unaffected + * This is mainly for sysadmins who have many slirp users + */ +#undef USE_LOWCPU + +/* Define this if your compiler doesn't like prototypes */ +#ifndef __STDC__ +#define NO_PROTOTYPES +#endif + +/*********************************************************/ +/* + * Autoconf defined configuration options + * You shouldn't need to touch any of these + */ + +/* Ignore this */ +#undef DUMMY_PPP + +/* XXX: Define according to how time.h should be included */ +#undef TIME_WITH_SYS_TIME +#define TIME_WITH_SYS_TIME 0 +#undef HAVE_SYS_TIME_H + +/* Define if your sprintf returns char * instead of int */ +#undef BAD_SPRINTF + +/* Define if you have readv */ +#undef HAVE_READV + +/* Define if iovec needs to be declared */ +#undef DECLARE_IOVEC +#ifdef _WIN32 +#define DECLARE_IOVEC +#endif + +/* Define if a declaration of sprintf/fprintf is needed */ +#undef DECLARE_SPRINTF + +/* Define if you have sys/stropts.h */ +#undef HAVE_SYS_STROPTS_H + +/* Define if your compiler doesn't like prototypes */ +#undef NO_PROTOTYPES + +/* Define if you don't have u_int32_t etc. typedef'd */ +#undef NEED_TYPEDEFS +#ifdef __sun__ +#define NEED_TYPEDEFS +#endif + +/* Define to sizeof(char *) */ +#define SIZEOF_CHAR_P SIZEOF_VOID_P + +/* Define if you have random() */ +#undef HAVE_RANDOM + +/* Define if you have srandom() */ +#undef HAVE_SRANDOM + +/* Define if you have setenv */ +#undef HAVE_SETENV + +/* Define if you have index() */ +#undef HAVE_INDEX + +/* Define if you have bcmp() */ +#undef HAVE_BCMP + +/* Define if you have drand48 */ +#undef HAVE_DRAND48 + +/* Define if you have memmove */ +#define HAVE_MEMMOVE + +/* Define if you have gethostid */ +#undef HAVE_GETHOSTID + +/* Define if you DON'T have unix-domain sockets */ +#undef NO_UNIX_SOCKETS +#ifdef _WIN32 +#define NO_UNIX_SOCKETS +#endif + +/* Define if gettimeofday only takes one argument */ +#undef GETTIMEOFDAY_ONE_ARG + +/* Define if you have revoke() */ +#undef HAVE_REVOKE + +/* Define if you have the sysv method of opening pty's (/dev/ptmx, etc.) */ +#undef HAVE_GRANTPT + +/* Define if you have fchmod */ +#undef HAVE_FCHMOD + +/* Define if you have */ +#undef HAVE_SYS_TYPES32_H diff --git a/src/slirp/socket.c b/src/slirp/socket.c new file mode 100644 index 0000000..604c2bf --- /dev/null +++ b/src/slirp/socket.c @@ -0,0 +1,731 @@ +/* + * Copyright (c) 1995 Danny Gasparovski. + * + * Please read the file COPYRIGHT for the + * terms and conditions of the copyright. + */ + +#define WANT_SYS_IOCTL_H +#include +#include "slirp.h" +#include "ip_icmp.h" +#include "main.h" +#ifdef __sun__ +#include +#endif + +#ifndef FIONREAD +#include +#endif + +void +so_init() +{ + /* Nothing yet */ +} + + +struct SLIRPsocket * +solookup(head, laddr, lport, faddr, fport) + struct SLIRPsocket *head; + struct in_addr laddr; + u_int lport; + struct in_addr faddr; + u_int fport; +{ + struct SLIRPsocket *so; + + for (so = head->so_next; so != head; so = so->so_next) { + if (so->so_lport == lport && + so->so_laddr.s_addr == laddr.s_addr && + so->so_faddr.s_addr == faddr.s_addr && + so->so_fport == fport) + break; + } + + if (so == head) + return (struct SLIRPsocket *)NULL; + return so; + +} + +/* + * Create a new socket, initialise the fields + * It is the responsibility of the caller to + * insque() it into the correct linked-list + */ +struct SLIRPsocket * +socreate() +{ + struct SLIRPsocket *so; + + so = (struct SLIRPsocket *)malloc(sizeof(struct SLIRPsocket)); + if(so) { + memset(so, 0, sizeof(struct SLIRPsocket)); + so->so_state = SS_NOFDREF; + so->s = -1; + } + return(so); +} + +/* + * remque and free a socket, clobber cache + */ +void +sofree(so) + struct SLIRPsocket *so; +{ + if (so->so_emu==EMU_RSH && so->extra) { + sofree(so->extra); + so->extra=NULL; + } + if (so == tcp_last_so) + tcp_last_so = &tcb; + else if (so == udp_last_so) + udp_last_so = &udb; + + if(so->so_m!=NULL) + m_free(so->so_m); + + if(so->so_next && so->so_prev) + remque(so); /* crashes if so is not in a queue */ + + free(so); +} + +/* + * Read from so's socket into sb_snd, updating all relevant sbuf fields + * NOTE: This will only be called if it is select()ed for reading, so + * a read() of 0 (or less) means it's disconnected + */ +int +soread(so) + struct SLIRPsocket *so; +{ + int n, nn, lss, total; + struct sbuf *sb = &so->so_snd; + int len = sb->sb_datalen - sb->sb_cc; + struct iovec iov[2]; + int mss; + if(!so->so_tcpcb) + return 0; + + mss = so->so_tcpcb->t_maxseg; + + DEBUG_CALL("soread"); + DEBUG_ARG("so = %lx", (long )so); + + /* + * No need to check if there's enough room to read. + * soread wouldn't have been called if there weren't + */ + + len = sb->sb_datalen - sb->sb_cc; + + iov[0].iov_base = sb->sb_wptr; + if (sb->sb_wptr < sb->sb_rptr) { + iov[0].iov_len = sb->sb_rptr - sb->sb_wptr; + /* Should never succeed, but... */ + if (iov[0].iov_len > len) + iov[0].iov_len = len; + if (iov[0].iov_len > mss) + iov[0].iov_len -= iov[0].iov_len%mss; + n = 1; + } else { + iov[0].iov_len = (sb->sb_data + sb->sb_datalen) - sb->sb_wptr; + /* Should never succeed, but... */ + if (iov[0].iov_len > len) iov[0].iov_len = len; + len -= iov[0].iov_len; + if (len) { + iov[1].iov_base = sb->sb_data; + iov[1].iov_len = sb->sb_rptr - sb->sb_data; + if(iov[1].iov_len > len) + iov[1].iov_len = len; + total = iov[0].iov_len + iov[1].iov_len; + if (total > mss) { + lss = total%mss; + if (iov[1].iov_len > lss) { + iov[1].iov_len -= lss; + n = 2; + } else { + lss -= iov[1].iov_len; + iov[0].iov_len -= lss; + n = 1; + } + } else + n = 2; + } else { + if (iov[0].iov_len > mss) + iov[0].iov_len -= iov[0].iov_len%mss; + n = 1; + } + } + +#ifdef HAVE_READV + nn = readv(so->s, (struct iovec *)iov, n); + DEBUG_MISC((dfd, " ... read nn = %d bytes\n", nn)); +#else + nn = recv(so->s, iov[0].iov_base, iov[0].iov_len,0); +#endif + if (nn <= 0) { + if (nn < 0 && (errno == EINTR || errno == EAGAIN)) + return 0; + else { + DEBUG_MISC((dfd, " --- soread() disconnected, nn = %d, errno = %d-%s\n", nn, errno,strerror(errno))); + sofcantrcvmore(so); + tcp_sockclosed(sototcpcb(so)); + return -1; + } + } + +#ifndef HAVE_READV + /* + * If there was no error, try and read the second time round + * We read again if n = 2 (ie, there's another part of the buffer) + * and we read as much as we could in the first read + * We don't test for <= 0 this time, because there legitimately + * might not be any more data (since the socket is non-blocking), + * a close will be detected on next iteration. + * A return of -1 wont (shouldn't) happen, since it didn't happen above + */ + if (n == 2 && nn == iov[0].iov_len) { + int ret; + ret = recv(so->s, iov[1].iov_base, iov[1].iov_len,0); + if (ret > 0) + nn += ret; + } + + DEBUG_MISC((dfd, " ... read nn = %d bytes\n", nn)); +#endif + + /* Update fields */ + sb->sb_cc += nn; + sb->sb_wptr += nn; + if (sb->sb_wptr >= (sb->sb_data + sb->sb_datalen)) + sb->sb_wptr -= sb->sb_datalen; + return nn; +} + +/* + * Get urgent data + * + * When the socket is created, we set it SO_OOBINLINE, + * so when OOB data arrives, we soread() it and everything + * in the send buffer is sent as urgent data + */ +void +sorecvoob(so) + struct SLIRPsocket *so; +{ + struct tcpcb *tp = sototcpcb(so); + + DEBUG_CALL("sorecvoob"); + DEBUG_ARG("so = %lx", (long)so); + + /* + * We take a guess at how much urgent data has arrived. + * In most situations, when urgent data arrives, the next + * read() should get all the urgent data. This guess will + * be wrong however if more data arrives just after the + * urgent data, or the read() doesn't return all the + * urgent data. + */ + soread(so); + tp->snd_up = tp->snd_una + so->so_snd.sb_cc; + tp->t_force = 1; + tcp_output(tp); + tp->t_force = 0; +} + +/* + * Send urgent data + * There's a lot duplicated code here, but... + */ +int +sosendoob(so) + struct SLIRPsocket *so; +{ + struct sbuf *sb = &so->so_rcv; + char buff[2048]; /* XXX Shouldn't be sending more oob data than this */ + + int n, len; + + DEBUG_CALL("sosendoob"); + DEBUG_ARG("so = %lx", (long)so); + DEBUG_ARG("sb->sb_cc = %d", sb->sb_cc); + + if (so->so_urgc > 2048) + so->so_urgc = 2048; /* XXXX */ + + if (sb->sb_rptr < sb->sb_wptr) { + /* We can send it directly */ + n = send(so->s, sb->sb_rptr, so->so_urgc, (MSG_OOB)); /* |MSG_DONTWAIT)); */ + so->so_urgc -= n; + + DEBUG_MISC((dfd, " --- sent %d bytes urgent data, %d urgent bytes left\n", n, so->so_urgc)); + } else { + /* + * Since there's no sendv or sendtov like writev, + * we must copy all data to a linear buffer then + * send it all + */ + len = (sb->sb_data + sb->sb_datalen) - sb->sb_rptr; + if (len > so->so_urgc) len = so->so_urgc; + memcpy(buff, sb->sb_rptr, len); + so->so_urgc -= len; + if (so->so_urgc) { + n = sb->sb_wptr - sb->sb_data; + if (n > so->so_urgc) n = so->so_urgc; + memcpy((buff + len), sb->sb_data, n); + so->so_urgc -= n; + len += n; + } + n = send(so->s, buff, len, (MSG_OOB)); /* |MSG_DONTWAIT)); */ +#ifdef SLIRP_DEBUG + if (n != len) + DEBUG_ERROR((dfd, "Didn't send all data urgently XXXXX\n")); +#endif + DEBUG_MISC((dfd, " ---2 sent %d bytes urgent data, %d urgent bytes left\n", n, so->so_urgc)); + } + + sb->sb_cc -= n; + sb->sb_rptr += n; + if (sb->sb_rptr >= (sb->sb_data + sb->sb_datalen)) + sb->sb_rptr -= sb->sb_datalen; + + return n; +} + +/* + * Write data from so_rcv to so's socket, + * updating all sbuf field as necessary + */ +int +sowrite(so) + struct SLIRPsocket *so; +{ + int n,nn; + struct sbuf *sb = &so->so_rcv; + int len = sb->sb_cc; + struct iovec iov[2]; + + DEBUG_CALL("sowrite"); + DEBUG_ARG("so = %lx", (long)so); + + if (so->so_urgc) { + sosendoob(so); + if (sb->sb_cc == 0) + return 0; + } + + /* + * No need to check if there's something to write, + * sowrite wouldn't have been called otherwise + */ + + len = sb->sb_cc; + + iov[0].iov_base = sb->sb_rptr; + if (sb->sb_rptr < sb->sb_wptr) { + iov[0].iov_len = sb->sb_wptr - sb->sb_rptr; + /* Should never succeed, but... */ + if (iov[0].iov_len > len) iov[0].iov_len = len; + n = 1; + } else { + iov[0].iov_len = (sb->sb_data + sb->sb_datalen) - sb->sb_rptr; + if (iov[0].iov_len > len) iov[0].iov_len = len; + len -= iov[0].iov_len; + if (len) { + iov[1].iov_base = sb->sb_data; + iov[1].iov_len = sb->sb_wptr - sb->sb_data; + if (iov[1].iov_len > len) iov[1].iov_len = len; + n = 2; + } else + n = 1; + } + /* Check if there's urgent data to send, and if so, send it */ + +#ifdef HAVE_READV + nn = writev(so->s, (const struct iovec *)iov, n); + + DEBUG_MISC((dfd, " ... wrote nn = %d bytes\n", nn)); +#else + nn = send(so->s, iov[0].iov_base, iov[0].iov_len,0); +#endif + /* This should never happen, but people tell me it does *shrug* */ + if (nn < 0 && (errno == EAGAIN || errno == EINTR)) + return 0; + + if (nn <= 0) { + DEBUG_MISC((dfd, " --- sowrite disconnected, so->so_state = %x, errno = %d\n", + so->so_state, errno)); + sofcantsendmore(so); + tcp_sockclosed(sototcpcb(so)); + return -1; + } + +#ifndef HAVE_READV + if (n == 2 && nn == iov[0].iov_len) { + int ret; + ret = send(so->s, iov[1].iov_base, iov[1].iov_len,0); + if (ret > 0) + nn += ret; + } + DEBUG_MISC((dfd, " ... wrote nn = %d bytes\n", nn)); +#endif + + /* Update sbuf */ + sb->sb_cc -= nn; + sb->sb_rptr += nn; + if (sb->sb_rptr >= (sb->sb_data + sb->sb_datalen)) + sb->sb_rptr -= sb->sb_datalen; + + /* + * If in DRAIN mode, and there's no more data, set + * it CANTSENDMORE + */ + if ((so->so_state & SS_FWDRAIN) && sb->sb_cc == 0) + sofcantsendmore(so); + + return nn; +} + +/* + * recvfrom() a UDP socket + */ +void +sorecvfrom(so) + struct SLIRPsocket *so; +{ + struct sockaddr_in addr; + socklen_t addrlen = sizeof(struct sockaddr_in); + + DEBUG_CALL("sorecvfrom"); + DEBUG_ARG("so = %lx", (long)so); + + if (so->so_type == IPPROTO_ICMP) { /* This is a "ping" reply */ + char buff[256]; + int len; + + len = recvfrom(so->s, buff, 256, 0, + (struct sockaddr *)&addr, &addrlen); + /* XXX Check if reply is "correct"? */ + + if(len == -1 || len == 0) { + u_char code=ICMP_UNREACH_PORT; + + if(errno == EHOSTUNREACH) code=ICMP_UNREACH_HOST; + else if(errno == ENETUNREACH) code=ICMP_UNREACH_NET; + + DEBUG_MISC((dfd," udp icmp rx errno = %d-%s\n", + errno,strerror(errno))); + icmp_error(so->so_m, ICMP_UNREACH,code, 0,strerror(errno)); + } else { + icmp_reflect(so->so_m); + so->so_m = 0; /* Don't m_free() it again! */ + } + /* No need for this socket anymore, udp_detach it */ + udp_detach(so); + } else { /* A "normal" UDP packet */ + struct SLIRPmbuf *m; + int len; + ioctlsockopt_t n; + + if (!(m = m_get())) return; + m->m_data += if_maxlinkhdr; + + /* + * XXX Shouldn't FIONREAD packets destined for port 53, + * but I don't know the max packet size for DNS lookups + */ + len = M_FREEROOM(m); + /* if (so->so_fport != htons(53)) { */ + ioctlsocket(so->s, FIONREAD, &n); + + if (n > len) { + n = (m->m_data - m->m_dat) + m->m_len + n + 1; + m_inc(m, n); + len = M_FREEROOM(m); + } + /* } */ + + m->m_len = recvfrom(so->s, m->m_data, len, 0, + (struct sockaddr *)&addr, &addrlen); + DEBUG_MISC((dfd, " did recvfrom %d, errno = %d-%s\n", + m->m_len, errno,strerror(errno))); + if(m->m_len<0) { + u_char code=ICMP_UNREACH_PORT; + + if(errno == EHOSTUNREACH) code=ICMP_UNREACH_HOST; + else if(errno == ENETUNREACH) code=ICMP_UNREACH_NET; + + DEBUG_MISC((dfd," rx error, tx icmp ICMP_UNREACH:%i\n", code)); + icmp_error(so->so_m, ICMP_UNREACH,code, 0,strerror(errno)); + m_free(m); + } else { + /* + * Hack: domain name lookup will be used the most for UDP, + * and since they'll only be used once there's no need + * for the 4 minute (or whatever) timeout... So we time them + * out much quicker (10 seconds for now...) + */ + if (so->so_expire) { + if (so->so_fport == htons(53)) + so->so_expire = curtime + SO_EXPIREFAST; + else + so->so_expire = curtime + SO_EXPIRE; + } + + /* if (m->m_len == len) { + * m_inc(m, MINCSIZE); + * m->m_len = 0; + * } + */ + + /* + * If this packet was destined for CTL_ADDR, + * make it look like that's where it came from, done by udp_output + */ + udp_output(so, m, &addr); + } /* rx error */ + } /* if ping packet */ +} + +/* + * sendto() a socket + */ +int +sosendto(so, m) + struct SLIRPsocket *so; + struct SLIRPmbuf *m; +{ + int ret; + struct sockaddr_in addr; + + DEBUG_CALL("sosendto"); + DEBUG_ARG("so = %lx", (long)so); + DEBUG_ARG("m = %lx", (long)m); + + addr.sin_family = AF_INET; + if ((so->so_faddr.s_addr & htonl(0xffffff00)) == special_addr.s_addr) { + /* It's an alias */ + switch(ntohl(so->so_faddr.s_addr) & 0xff) { + case CTL_DNS: + addr.sin_addr = dns_addr; + break; + case CTL_ALIAS: + default: + addr.sin_addr = loopback_addr; + break; + } + } else + addr.sin_addr = so->so_faddr; + addr.sin_port = so->so_fport; + + DEBUG_MISC((dfd, " sendto()ing, addr.sin_port=%d, addr.sin_addr.s_addr=%.16s\n", ntohs(addr.sin_port), inet_ntoa(addr.sin_addr))); + + /* Don't care what port we get */ + ret = sendto(so->s, m->m_data, m->m_len, 0, + (struct sockaddr *)&addr, sizeof (struct sockaddr)); + if (ret < 0) + return -1; + + /* + * Kill the socket if there's no reply in 4 minutes, + * but only if it's an expirable socket + */ + if (so->so_expire) + so->so_expire = curtime + SO_EXPIRE; + so->so_state = SS_ISFCONNECTED; /* So that it gets select()ed */ + return 0; +} + +/* + * XXX This should really be tcp_listen + */ +struct SLIRPsocket * +solisten(port, laddr, lport, flags) + u_int port; + u_int32_t laddr; + u_int lport; + int flags; +{ + struct sockaddr_in addr; + struct SLIRPsocket *so; + int s; + socklen_t addrlen = sizeof(addr); + int opt = 1; + + DEBUG_CALL("solisten"); + DEBUG_ARG("port = %d", port); + DEBUG_ARG("laddr = %x", laddr); + DEBUG_ARG("lport = %d", lport); + DEBUG_ARG("flags = %x", flags); + + if ((so = socreate()) == NULL) { + /* free(so); Not sofree() ??? free(NULL) == NOP */ + return NULL; + } + + /* Don't tcp_attach... we don't need so_snd nor so_rcv */ + if ((so->so_tcpcb = tcp_newtcpcb(so)) == NULL) { + free(so); + return NULL; + } + insque(so,&tcb); + + /* + * SS_FACCEPTONCE sockets must time out. + */ + if (flags & SS_FACCEPTONCE) + so->so_tcpcb->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT*2; + + so->so_state = (SS_FACCEPTCONN|flags); + so->so_lport = lport; /* Kept in network format */ + so->so_laddr.s_addr = laddr; /* Ditto */ + + memset(&addr, 0, sizeof(struct sockaddr_in)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = INADDR_ANY; + addr.sin_port = port; + + if (((s = socket(AF_INET,SOCK_STREAM,0)) < 0) || + (setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(char *)&opt,sizeof(int)) < 0) || + (bind(s,(struct sockaddr *)&addr, sizeof(addr)) < 0) || + (listen(s,1) < 0)) { + int tmperrno = errno; /* Don't clobber the real reason we failed */ + + close(s); + sofree(so); + /* Restore the real errno */ +#ifdef _WIN32 + WSASetLastError(tmperrno); +#else + errno = tmperrno; +#endif + return NULL; + } + setsockopt(s,SOL_SOCKET,SO_OOBINLINE,(char *)&opt,sizeof(int)); + + getsockname(s,(struct sockaddr *)&addr,&addrlen); + so->so_fport = addr.sin_port; + if (addr.sin_addr.s_addr == 0 || addr.sin_addr.s_addr == loopback_addr.s_addr) + so->so_faddr = alias_addr; + else + so->so_faddr = addr.sin_addr; + + so->s = s; + return so; +} + +/* + * Data is available in so_rcv + * Just write() the data to the socket + * XXX not yet... + */ +void +sorwakeup(so) + struct SLIRPsocket *so; +{ +/* sowrite(so); */ +/* FD_CLR(so->s,&writefds); */ +} + +/* + * Data has been freed in so_snd + * We have room for a read() if we want to + * For now, don't read, it'll be done in the main loop + */ +void +sowwakeup(so) + struct SLIRPsocket *so; +{ + /* Nothing, yet */ +} + +/* + * Various session state calls + * XXX Should be #define's + * The socket state stuff needs work, these often get call 2 or 3 + * times each when only 1 was needed + */ +void +soisfconnecting(so) + register struct SLIRPsocket *so; +{ + so->so_state &= ~(SS_NOFDREF|SS_ISFCONNECTED|SS_FCANTRCVMORE| + SS_FCANTSENDMORE|SS_FWDRAIN); + so->so_state |= SS_ISFCONNECTING; /* Clobber other states */ +} + +void +soisfconnected(so) + register struct SLIRPsocket *so; +{ + so->so_state &= ~(SS_ISFCONNECTING|SS_FWDRAIN|SS_NOFDREF); + so->so_state |= SS_ISFCONNECTED; /* Clobber other states */ +} + +void +sofcantrcvmore(so) + struct SLIRPsocket *so; +{ + if ((so->so_state & SS_NOFDREF) == 0) { + shutdown(so->s,0); + if(global_writefds) { + FD_CLR(so->s,global_writefds); + } + } + so->so_state &= ~(SS_ISFCONNECTING); + if (so->so_state & SS_FCANTSENDMORE) + so->so_state = SS_NOFDREF; /* Don't select it */ /* XXX close() here as well? */ + else + so->so_state |= SS_FCANTRCVMORE; +} + +void +sofcantsendmore(so) + struct SLIRPsocket *so; +{ + if ((so->so_state & SS_NOFDREF) == 0) { + shutdown(so->s,1); /* send FIN to fhost */ + if (global_readfds) { + FD_CLR(so->s,global_readfds); + } + if (global_xfds) { + FD_CLR(so->s,global_xfds); + } + } + so->so_state &= ~(SS_ISFCONNECTING); + if (so->so_state & SS_FCANTRCVMORE) + so->so_state = SS_NOFDREF; /* as above */ + else + so->so_state |= SS_FCANTSENDMORE; +} + +void +soisfdisconnected(so) + struct SLIRPsocket *so; +{ +/* so->so_state &= ~(SS_ISFCONNECTING|SS_ISFCONNECTED); */ +/* close(so->s); */ +/* so->so_state = SS_ISFDISCONNECTED; */ + /* + * XXX Do nothing ... ? + */ +} + +/* + * Set write drain mode + * Set CANTSENDMORE once all data has been write()n + */ +void +sofwdrain(so) + struct SLIRPsocket *so; +{ + if (so->so_rcv.sb_cc) + so->so_state |= SS_FWDRAIN; + else + sofcantsendmore(so); +} + diff --git a/src/slirp/socket.h b/src/slirp/socket.h new file mode 100644 index 0000000..9e9f231 --- /dev/null +++ b/src/slirp/socket.h @@ -0,0 +1,104 @@ +/* + * Copyright (c) 1995 Danny Gasparovski. + * + * Please read the file COPYRIGHT for the + * terms and conditions of the copyright. + */ + +/* MINE */ + +#ifndef _SLIRP_SOCKET_H_ +#define _SLIRP_SOCKET_H_ + +#define SO_EXPIRE 240000 +#define SO_EXPIREFAST 10000 + +/* + * Our socket structure + */ + +struct SLIRPsocket { + struct SLIRPsocket *so_next,*so_prev; /* For a linked list of sockets */ + + int s; /* The actual socket */ + + /* XXX union these with not-yet-used sbuf params */ + struct SLIRPmbuf *so_m; /* Pointer to the original SYN packet, + * for non-blocking connect()'s, and + * PING reply's */ + struct tcpiphdr *so_ti; /* Pointer to the original ti within + * so_mconn, for non-blocking connections */ + int so_urgc; + struct in_addr so_faddr; /* foreign host table entry */ + struct in_addr so_laddr; /* local host table entry */ + u_int16_t so_fport; /* foreign port */ + u_int16_t so_lport; /* local port */ + + u_int8_t so_iptos; /* Type of service */ + u_int8_t so_emu; /* Is the socket emulated? */ + + u_char so_type; /* Type of socket, UDP or TCP */ + int so_state; /* internal state flags SS_*, below */ + + struct tcpcb *so_tcpcb; /* pointer to TCP protocol control block */ + u_int so_expire; /* When the socket will expire */ + + int so_queued; /* Number of packets queued from this socket */ + int so_nqueued; /* Number of packets queued in a row + * Used to determine when to "downgrade" a session + * from fastq to batchq */ + + struct sbuf so_rcv; /* Receive buffer */ + struct sbuf so_snd; /* Send buffer */ + void * extra; /* Extra pointer */ +}; + + +/* + * Socket state bits. (peer means the host on the Internet, + * local host means the host on the other end of the modem) + */ +#define SS_NOFDREF 0x001 /* No fd reference */ + +#define SS_ISFCONNECTING 0x002 /* Socket is connecting to peer (non-blocking connect()'s) */ +#define SS_ISFCONNECTED 0x004 /* Socket is connected to peer */ +#define SS_FCANTRCVMORE 0x008 /* Socket can't receive more from peer (for half-closes) */ +#define SS_FCANTSENDMORE 0x010 /* Socket can't send more to peer (for half-closes) */ +/* #define SS_ISFDISCONNECTED 0x020*/ /* Socket has disconnected from peer, in 2MSL state */ +#define SS_FWDRAIN 0x040 /* We received a FIN, drain data and set SS_FCANTSENDMORE */ + +#define SS_CTL 0x080 +#define SS_FACCEPTCONN 0x100 /* Socket is accepting connections from a host on the internet */ +#define SS_FACCEPTONCE 0x200 /* If set, the SS_FACCEPTCONN socket will die after one accept */ + +extern struct SLIRPsocket tcb; + + +#if defined(DECLARE_IOVEC) && !defined(HAVE_READV) +struct iovec { + char *iov_base; + size_t iov_len; +}; +#endif + +void so_init _P((void)); +struct SLIRPsocket * solookup _P((struct SLIRPsocket *, struct in_addr, u_int, struct in_addr, u_int)); +struct SLIRPsocket * socreate _P((void)); +void sofree _P((struct SLIRPsocket *)); +int soread _P((struct SLIRPsocket *)); +void sorecvoob _P((struct SLIRPsocket *)); +int sosendoob _P((struct SLIRPsocket *)); +int sowrite _P((struct SLIRPsocket *)); +void sorecvfrom _P((struct SLIRPsocket *)); +int sosendto _P((struct SLIRPsocket *, struct SLIRPmbuf *)); +struct SLIRPsocket * solisten _P((u_int, u_int32_t, u_int, int)); +void sorwakeup _P((struct SLIRPsocket *)); +void sowwakeup _P((struct SLIRPsocket *)); +void soisfconnecting _P((register struct SLIRPsocket *)); +void soisfconnected _P((register struct SLIRPsocket *)); +void sofcantrcvmore _P((struct SLIRPsocket *)); +void sofcantsendmore _P((struct SLIRPsocket *)); +void soisfdisconnected _P((struct SLIRPsocket *)); +void sofwdrain _P((struct SLIRPsocket *)); + +#endif /* _SOCKET_H_ */ diff --git a/src/slirp/tcp.h b/src/slirp/tcp.h new file mode 100644 index 0000000..36edc5c --- /dev/null +++ b/src/slirp/tcp.h @@ -0,0 +1,181 @@ +/* + * Copyright (c) 1982, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)tcp.h 8.1 (Berkeley) 6/10/93 + * tcp.h,v 1.3 1994/08/21 05:27:34 paul Exp + */ + +#ifndef _TCP_H_ +#define _TCP_H_ + +typedef u_int32_t tcp_seq; + +#define PR_SLOWHZ 2 /* 2 slow timeouts per second (approx) */ +#define PR_FASTHZ 5 /* 5 fast timeouts per second (not important) */ + +extern int tcp_rcvspace; +extern int tcp_sndspace; +extern struct SLIRPsocket *tcp_last_so; + +#define TCP_SNDSPACE 8192 +#define TCP_RCVSPACE 8192 + +/* + * TCP header. + * Per RFC 793, September, 1981. + */ +#ifdef PRAGMA_PACK_SUPPORTED +#pragma pack(1) +#endif + +struct tcphdr { + u_int16_t th_sport; /* source port */ + u_int16_t th_dport; /* destination port */ + tcp_seq th_seq; /* sequence number */ + tcp_seq th_ack; /* acknowledgement number */ +#ifdef WORDS_BIGENDIAN + u_char th_off:4, /* data offset */ + th_x2:4; /* (unused) */ +#else + u_char th_x2:4, /* (unused) */ + th_off:4; /* data offset */ +#endif + u_int8_t th_flags; +#define TH_FIN 0x01 +#define TH_SYN 0x02 +#define TH_RST 0x04 +#define TH_PUSH 0x08 +#define TH_ACK 0x10 +#define TH_URG 0x20 + u_int16_t th_win; /* window */ + u_int16_t th_sum; /* checksum */ + u_int16_t th_urp; /* urgent pointer */ +} PACKED__; + +#ifdef PRAGMA_PACK_SUPPORTED +#pragma pack(PACK_END) +#endif + +#include "tcp_var.h" + +#define TCPOPT_EOL 0 +#define TCPOPT_NOP 1 +#define TCPOPT_MAXSEG 2 +#define TCPOLEN_MAXSEG 4 +#define TCPOPT_WINDOW 3 +#define TCPOLEN_WINDOW 3 +#define TCPOPT_SACK_PERMITTED 4 /* Experimental */ +#define TCPOLEN_SACK_PERMITTED 2 +#define TCPOPT_SACK 5 /* Experimental */ +#define TCPOPT_TIMESTAMP 8 +#define TCPOLEN_TIMESTAMP 10 +#define TCPOLEN_TSTAMP_APPA (TCPOLEN_TIMESTAMP+2) /* appendix A */ + +#define TCPOPT_TSTAMP_HDR \ + (TCPOPT_NOP<<24|TCPOPT_NOP<<16|TCPOPT_TIMESTAMP<<8|TCPOLEN_TIMESTAMP) + +/* + * Default maximum segment size for TCP. + * With an IP MSS of 576, this is 536, + * but 512 is probably more convenient. + * This should be defined as MIN(512, IP_MSS - sizeof (struct tcpiphdr)). + * + * We make this 1460 because we only care about Ethernet in the qemu context. + */ +#define TCP_MSS 1460 + +#define TCP_MAXWIN 65535 /* largest value for (unscaled) window */ + +#define TCP_MAX_WINSHIFT 14 /* maximum window shift */ + +/* + * User-settable options (used with setsockopt). + * + * We don't use the system headers on unix because we have conflicting + * local structures. We can't avoid the system definitions on Windows, + * so we undefine them. + */ +#undef TCP_NODELAY +#define TCP_NODELAY 0x01 /* don't delay send to coalesce packets */ +#undef TCP_MAXSEG +/* #define TCP_MAXSEG 0x02 */ /* set maximum segment size */ + +/* + * TCP FSM state definitions. + * Per RFC793, September, 1981. + */ + +#define TCP_NSTATES 11 + +#define TCPS_CLOSED 0 /* closed */ +#define TCPS_LISTEN 1 /* listening for connection */ +#define TCPS_SYN_SENT 2 /* active, have sent syn */ +#define TCPS_SYN_RECEIVED 3 /* have send and received syn */ +/* states < TCPS_ESTABLISHED are those where connections not established */ +#define TCPS_ESTABLISHED 4 /* established */ +#define TCPS_CLOSE_WAIT 5 /* rcvd fin, waiting for close */ +/* states > TCPS_CLOSE_WAIT are those where user has closed */ +#define TCPS_FIN_WAIT_1 6 /* have closed, sent fin */ +#define TCPS_CLOSING 7 /* closed xchd FIN; await FIN ACK */ +#define TCPS_LAST_ACK 8 /* had fin and close; await FIN ACK */ +/* states > TCPS_CLOSE_WAIT && < TCPS_FIN_WAIT_2 await ACK of FIN */ +#define TCPS_FIN_WAIT_2 9 /* have closed, fin is acked */ +#define TCPS_TIME_WAIT 10 /* in 2*msl quiet wait after close */ + +#define TCPS_HAVERCVDSYN(s) ((s) >= TCPS_SYN_RECEIVED) +#define TCPS_HAVEESTABLISHED(s) ((s) >= TCPS_ESTABLISHED) +#define TCPS_HAVERCVDFIN(s) ((s) >= TCPS_TIME_WAIT) + +/* + * TCP sequence numbers are 32 bit integers operated + * on with modular arithmetic. These macros can be + * used to compare such integers. + */ +#define SEQ_LT(a,b) ((int)((a)-(b)) < 0) +#define SEQ_LEQ(a,b) ((int)((a)-(b)) <= 0) +#define SEQ_GT(a,b) ((int)((a)-(b)) > 0) +#define SEQ_GEQ(a,b) ((int)((a)-(b)) >= 0) + +/* + * Macros to initialize tcp sequence numbers for + * send and receive from initial send and receive + * sequence numbers. + */ +#define tcp_rcvseqinit(tp) \ + (tp)->rcv_adv = (tp)->rcv_nxt = (tp)->irs + 1 + +#define tcp_sendseqinit(tp) \ + (tp)->snd_una = (tp)->snd_nxt = (tp)->snd_max = (tp)->snd_up = (tp)->iss + +#define TCP_ISSINCR (125*1024) /* increment for tcp_iss each second */ + +extern tcp_seq tcp_iss; /* tcp initial send seq # */ + +extern char *tcpstates[]; + +#endif diff --git a/src/slirp/tcp_input.c b/src/slirp/tcp_input.c new file mode 100644 index 0000000..369ec00 --- /dev/null +++ b/src/slirp/tcp_input.c @@ -0,0 +1,1722 @@ +/* + * Copyright (c) 1982, 1986, 1988, 1990, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)tcp_input.c 8.5 (Berkeley) 4/10/94 + * tcp_input.c,v 1.10 1994/10/13 18:36:32 wollman Exp + */ + +/* + * Changes and additions relating to SLiRP + * Copyright (c) 1995 Danny Gasparovski. + * + * Please read the file COPYRIGHT for the + * terms and conditions of the copyright. + */ + +#include +#include "slirp.h" +#include "ip_icmp.h" + +struct SLIRPsocket tcb; + +int tcprexmtthresh = 3; +struct SLIRPsocket *tcp_last_so = &tcb; + +tcp_seq tcp_iss; /* tcp initial send seq # */ + +#define TCP_PAWS_IDLE (24 * 24 * 60 * 60 * PR_SLOWHZ) + +/* for modulo comparisons of timestamps */ +#define TSTMP_LT(a,b) ((int)((a)-(b)) < 0) +#define TSTMP_GEQ(a,b) ((int)((a)-(b)) >= 0) + +/* + * Insert segment ti into reassembly queue of tcp with + * control block tp. Return TH_FIN if reassembly now includes + * a segment with FIN. The macro form does the common case inline + * (segment is the next to be received on an established connection, + * and the queue is empty), avoiding linkage into and removal + * from the queue and repetition of various conversions. + * Set DELACK for segments received in order, but ack immediately + * when segments are out of order (so fast retransmit can work). + */ +#ifdef TCP_ACK_HACK +#define TCP_REASS(tp, ti, m, so, flags) {\ + if ((ti)->ti_seq == (tp)->rcv_nxt && \ + (tp)->seg_next == (tcpiphdrp_32)(tp) && \ + (tp)->t_state == TCPS_ESTABLISHED) {\ + if (ti->ti_flags & TH_PUSH) \ + tp->t_flags |= TF_ACKNOW; \ + else \ + tp->t_flags |= TF_DELACK; \ + (tp)->rcv_nxt += (ti)->ti_len; \ + flags = (ti)->ti_flags & TH_FIN; \ + tcpstat.tcps_rcvpack++;\ + tcpstat.tcps_rcvbyte += (ti)->ti_len;\ + if (so->so_emu) { \ + if (tcp_emu((so),(m))) sbappend((so), (m)); \ + } else \ + sbappend((so), (m)); \ +/* sorwakeup(so); */ \ + } else {\ + (flags) = tcp_reass((tp), (ti), (m)); \ + tp->t_flags |= TF_ACKNOW; \ + } \ +} +#else +#define TCP_REASS(tp, ti, m, so, flags) { \ + if ((ti)->ti_seq == (tp)->rcv_nxt && \ + (tp)->seg_next == (tcpiphdrp_32)(tp) && \ + (tp)->t_state == TCPS_ESTABLISHED) { \ + tp->t_flags |= TF_DELACK; \ + (tp)->rcv_nxt += (ti)->ti_len; \ + flags = (ti)->ti_flags & TH_FIN; \ + tcpstat.tcps_rcvpack++;\ + tcpstat.tcps_rcvbyte += (ti)->ti_len;\ + if (so->so_emu) { \ + if (tcp_emu((so),(m))) sbappend(so, (m)); \ + } else \ + sbappend((so), (m)); \ +/* sorwakeup(so); */ \ + } else { \ + (flags) = tcp_reass((tp), (ti), (m)); \ + tp->t_flags |= TF_ACKNOW; \ + } \ +} +#endif + +int +tcp_reass(tp, ti, m) + struct tcpcb *tp; + struct tcpiphdr *ti; + struct SLIRPmbuf *m; +{ + struct tcpiphdr *q; + struct SLIRPsocket *so = tp->t_socket; + int flags; + + /* + * Call with ti==0 after become established to + * force pre-ESTABLISHED data up to user socket. + */ + if (ti == 0) + goto present; + + /* + * Find a segment which begins after this one does. + */ + for (q = (struct tcpiphdr *)tp->seg_next; q != (struct tcpiphdr *)tp; + q = (struct tcpiphdr *)q->ti_next) + if (SEQ_GT(q->ti_seq, ti->ti_seq)) + break; + + /* + * If there is a preceding segment, it may provide some of + * our data already. If so, drop the data from the incoming + * segment. If it provides all of our data, drop us. + */ + if ((struct tcpiphdr *)q->ti_prev != (struct tcpiphdr *)tp) { + int i; + q = (struct tcpiphdr *)q->ti_prev; + /* conversion to int (in i) handles seq wraparound */ + i = q->ti_seq + q->ti_len - ti->ti_seq; + if (i > 0) { + if (i >= ti->ti_len) { + tcpstat.tcps_rcvduppack++; + tcpstat.tcps_rcvdupbyte += ti->ti_len; + m_freem(m); + /* + * Try to present any queued data + * at the left window edge to the user. + * This is needed after the 3-WHS + * completes. + */ + goto present; /* ??? */ + } + m_adj(m, i); + ti->ti_len -= i; + ti->ti_seq += i; + } + q = (struct tcpiphdr *)(q->ti_next); + } + tcpstat.tcps_rcvoopack++; + tcpstat.tcps_rcvoobyte += ti->ti_len; + REASS_MBUF(ti) = (mbufp_32) m; /* XXX */ + + /* + * While we overlap succeeding segments trim them or, + * if they are completely covered, dequeue them. + */ + while (q != (struct tcpiphdr *)tp) { + int i = (ti->ti_seq + ti->ti_len) - q->ti_seq; + if (i <= 0) + break; + if (i < q->ti_len) { + q->ti_seq += i; + q->ti_len -= i; + m_adj((struct SLIRPmbuf *) REASS_MBUF(q), i); + break; + } + q = (struct tcpiphdr *)q->ti_next; + m = (struct SLIRPmbuf *) REASS_MBUF((struct tcpiphdr *)q->ti_prev); + remque_32((void *)(q->ti_prev)); + m_freem(m); + } + + /* + * Stick new segment in its place. + */ + insque_32(ti, (void *)(q->ti_prev)); + +present: + /* + * Present data to user, advancing rcv_nxt through + * completed sequence space. + */ + if (!TCPS_HAVEESTABLISHED(tp->t_state)) + return (0); + ti = (struct tcpiphdr *) tp->seg_next; + if (ti == (struct tcpiphdr *)tp || ti->ti_seq != tp->rcv_nxt) + return (0); + if (tp->t_state == TCPS_SYN_RECEIVED && ti->ti_len) + return (0); + do { + tp->rcv_nxt += ti->ti_len; + flags = ti->ti_flags & TH_FIN; + remque_32(ti); + m = (struct SLIRPmbuf *) REASS_MBUF(ti); /* XXX */ + ti = (struct tcpiphdr *)ti->ti_next; +/* if (so->so_state & SS_FCANTRCVMORE) */ + if (so->so_state & SS_FCANTSENDMORE) + m_freem(m); + else { + if (so->so_emu) { + if (tcp_emu(so,m)) sbappend(so, m); + } else + sbappend(so, m); + } + } while (ti != (struct tcpiphdr *)tp && ti->ti_seq == tp->rcv_nxt); +/* sorwakeup(so); */ + return (flags); +} + +/* + * TCP input routine, follows pages 65-76 of the + * protocol specification dated September, 1981 very closely. + */ +void +tcp_input(m, iphlen, inso) + struct SLIRPmbuf *m; + int iphlen; + struct SLIRPsocket *inso; +{ + struct ip save_ip, *ip; + struct tcpiphdr *ti; + SLIRPcaddr_t optp = NULL; + int optlen = 0; + int len, tlen, off; + struct tcpcb *tp = 0; + int tiflags; + struct SLIRPsocket *so = 0; + int todrop, acked, ourfinisacked, needoutput = 0; +/* int dropsocket = 0; */ + int iss = 0; + u_long tiwin; + int ret; +/* int ts_present = 0; */ + + DEBUG_CALL("tcp_input"); + DEBUG_ARGS((dfd," m = %8lx iphlen = %2d inso = %lx\n", + (long )m, iphlen, (long )inso )); + + /* + * If called with m == 0, then we're continuing the connect + */ + if (m == NULL) { + so = inso; + + /* Re-set a few variables */ + tp = sototcpcb(so); + m = so->so_m; + so->so_m = 0; + ti = so->so_ti; + tiwin = ti->ti_win; + tiflags = ti->ti_flags; + + goto cont_conn; + } + + + tcpstat.tcps_rcvtotal++; + /* + * Get IP and TCP header together in first SLIRPmbuf. + * Note: IP leaves IP header in first SLIRPmbuf. + */ + ti = mtod(m, struct tcpiphdr *); + if (iphlen > sizeof(struct ip )) { + ip_stripoptions(m, (struct SLIRPmbuf *)0); + iphlen=sizeof(struct ip ); + } + /* XXX Check if too short */ + + + /* + * Save a copy of the IP header in case we want restore it + * for sending an ICMP error message in response. + */ + ip=mtod(m, struct ip *); + save_ip = *ip; + save_ip.ip_len+= iphlen; + + /* + * Checksum extended TCP header and data. + */ + tlen = ((struct ip *)ti)->ip_len; + ti->ti_next = ti->ti_prev = 0; + ti->ti_x1 = 0; + ti->ti_len = htons((u_int16_t)tlen); + len = sizeof(struct ip ) + tlen; + /* keep checksum for ICMP reply + * ti->ti_sum = cksum(m, len); + * if (ti->ti_sum) { */ + if(cksum(m, len)) { + tcpstat.tcps_rcvbadsum++; + goto drop; + } + + /* + * Check that TCP offset makes sense, + * pull out TCP options and adjust length. XXX + */ + off = ti->ti_off << 2; + if (off < sizeof (struct tcphdr) || off > tlen) { + tcpstat.tcps_rcvbadoff++; + goto drop; + } + tlen -= off; + ti->ti_len = tlen; + if (off > sizeof (struct tcphdr)) { + optlen = off - sizeof (struct tcphdr); + optp = mtod(m, SLIRPcaddr_t) + sizeof (struct tcpiphdr); + + /* + * Do quick retrieval of timestamp options ("options + * prediction?"). If timestamp is the only option and it's + * formatted as recommended in RFC 1323 appendix A, we + * quickly get the values now and not bother calling + * tcp_dooptions(), etc. + */ +/* if ((optlen == TCPOLEN_TSTAMP_APPA || + * (optlen > TCPOLEN_TSTAMP_APPA && + * optp[TCPOLEN_TSTAMP_APPA] == TCPOPT_EOL)) && + * *(u_int32_t *)optp == htonl(TCPOPT_TSTAMP_HDR) && + * (ti->ti_flags & TH_SYN) == 0) { + * ts_present = 1; + * ts_val = ntohl(*(u_int32_t *)(optp + 4)); + * ts_ecr = ntohl(*(u_int32_t *)(optp + 8)); + * optp = NULL; / * we've parsed the options * / + * } + */ + } + tiflags = ti->ti_flags; + + /* + * Convert TCP protocol specific fields to host format. + */ + NTOHL(ti->ti_seq); + NTOHL(ti->ti_ack); + NTOHS(ti->ti_win); + NTOHS(ti->ti_urp); + + /* + * Drop TCP, IP headers and TCP options. + */ + m->m_data += sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr); + m->m_len -= sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr); + + /* + * Locate pcb for segment. + */ +findso: + so = tcp_last_so; + if (so->so_fport != ti->ti_dport || + so->so_lport != ti->ti_sport || + so->so_laddr.s_addr != ti->ti_src.s_addr || + so->so_faddr.s_addr != ti->ti_dst.s_addr) { + so = solookup(&tcb, ti->ti_src, ti->ti_sport, + ti->ti_dst, ti->ti_dport); + if (so) + tcp_last_so = so; + ++tcpstat.tcps_socachemiss; + } + + /* + * If the state is CLOSED (i.e., TCB does not exist) then + * all data in the incoming segment is discarded. + * If the TCB exists but is in CLOSED state, it is embryonic, + * but should either do a listen or a connect soon. + * + * state == CLOSED means we've done socreate() but haven't + * attached it to a protocol yet... + * + * XXX If a TCB does not exist, and the TH_SYN flag is + * the only flag set, then create a session, mark it + * as if it was LISTENING, and continue... + */ + if (so == 0) { + if ((tiflags & (TH_SYN|TH_FIN|TH_RST|TH_URG|TH_ACK)) != TH_SYN) + goto dropwithreset; + + if ((so = socreate()) == NULL) + goto dropwithreset; + if (tcp_attach(so) < 0) { + free(so); /* Not sofree (if it failed, it's not insqued) */ + goto dropwithreset; + } + + sbreserve(&so->so_snd, tcp_sndspace); + sbreserve(&so->so_rcv, tcp_rcvspace); + + /* tcp_last_so = so; */ /* XXX ? */ + /* tp = sototcpcb(so); */ + + so->so_laddr = ti->ti_src; + so->so_lport = ti->ti_sport; + so->so_faddr = ti->ti_dst; + so->so_fport = ti->ti_dport; + + if ((so->so_iptos = tcp_tos(so)) == 0) + so->so_iptos = ((struct ip *)ti)->ip_tos; + + tp = sototcpcb(so); + tp->t_state = TCPS_LISTEN; + } + + /* + * If this is a still-connecting socket, this probably + * a retransmit of the SYN. Whether it's a retransmit SYN + * or something else, we nuke it. + */ + if (so->so_state & SS_ISFCONNECTING) + goto drop; + + tp = sototcpcb(so); + + /* XXX Should never fail */ + if (tp == 0) + goto dropwithreset; + if (tp->t_state == TCPS_CLOSED) + goto drop; + + /* Unscale the window into a 32-bit value. */ +/* if ((tiflags & TH_SYN) == 0) + * tiwin = ti->ti_win << tp->snd_scale; + * else + */ + tiwin = ti->ti_win; + + /* + * Segment received on connection. + * Reset idle time and keep-alive timer. + */ + tp->t_idle = 0; + if (so_options) + tp->t_timer[TCPT_KEEP] = tcp_keepintvl; + else + tp->t_timer[TCPT_KEEP] = tcp_keepidle; + + /* + * Process options if not in LISTEN state, + * else do it below (after getting remote address). + */ + if (optp && tp->t_state != TCPS_LISTEN) + tcp_dooptions(tp, (u_char *)optp, optlen, ti); +/* , */ +/* &ts_present, &ts_val, &ts_ecr); */ + + /* + * Header prediction: check for the two common cases + * of a uni-directional data xfer. If the packet has + * no control flags, is in-sequence, the window didn't + * change and we're not retransmitting, it's a + * candidate. If the length is zero and the ack moved + * forward, we're the sender side of the xfer. Just + * free the data acked & wake any higher level process + * that was blocked waiting for space. If the length + * is non-zero and the ack didn't move, we're the + * receiver side. If we're getting packets in-order + * (the reassembly queue is empty), add the data to + * the socket buffer and note that we need a delayed ack. + * + * XXX Some of these tests are not needed + * eg: the tiwin == tp->snd_wnd prevents many more + * predictions.. with no *real* advantage.. + */ + if (tp->t_state == TCPS_ESTABLISHED && + (tiflags & (TH_SYN|TH_FIN|TH_RST|TH_URG|TH_ACK)) == TH_ACK && +/* (!ts_present || TSTMP_GEQ(ts_val, tp->ts_recent)) && */ + ti->ti_seq == tp->rcv_nxt && + tiwin && tiwin == tp->snd_wnd && + tp->snd_nxt == tp->snd_max) { + /* + * If last ACK falls within this segment's sequence numbers, + * record the timestamp. + */ +/* if (ts_present && SEQ_LEQ(ti->ti_seq, tp->last_ack_sent) && + * SEQ_LT(tp->last_ack_sent, ti->ti_seq + ti->ti_len)) { + * tp->ts_recent_age = tcp_now; + * tp->ts_recent = ts_val; + * } + */ + if (ti->ti_len == 0) { + if (SEQ_GT(ti->ti_ack, tp->snd_una) && + SEQ_LEQ(ti->ti_ack, tp->snd_max) && + tp->snd_cwnd >= tp->snd_wnd) { + /* + * this is a pure ack for outstanding data. + */ + ++tcpstat.tcps_predack; +/* if (ts_present) + * tcp_xmit_timer(tp, tcp_now-ts_ecr+1); + * else + */ if (tp->t_rtt && + SEQ_GT(ti->ti_ack, tp->t_rtseq)) + tcp_xmit_timer(tp, tp->t_rtt); + acked = ti->ti_ack - tp->snd_una; + tcpstat.tcps_rcvackpack++; + tcpstat.tcps_rcvackbyte += acked; + sbdrop(&so->so_snd, acked); + tp->snd_una = ti->ti_ack; + m_freem(m); + + /* + * If all outstanding data are acked, stop + * retransmit timer, otherwise restart timer + * using current (possibly backed-off) value. + * If process is waiting for space, + * wakeup/selwakeup/signal. If data + * are ready to send, let tcp_output + * decide between more output or persist. + */ + if (tp->snd_una == tp->snd_max) + tp->t_timer[TCPT_REXMT] = 0; + else if (tp->t_timer[TCPT_PERSIST] == 0) + tp->t_timer[TCPT_REXMT] = tp->t_rxtcur; + + /* + * There's room in so_snd, sowwakup will read() + * from the socket if we can + */ +/* if (so->so_snd.sb_flags & SB_NOTIFY) + * sowwakeup(so); + */ + /* + * This is called because sowwakeup might have + * put data into so_snd. Since we don't so sowwakeup, + * we don't need this.. XXX??? + */ + if (so->so_snd.sb_cc) + (void) tcp_output(tp); + + return; + } + } else if (ti->ti_ack == tp->snd_una && + tp->seg_next == (tcpiphdrp_32)tp && + ti->ti_len <= sbspace(&so->so_rcv)) { + /* + * this is a pure, in-sequence data packet + * with nothing on the reassembly queue and + * we have enough buffer space to take it. + */ + ++tcpstat.tcps_preddat; + tp->rcv_nxt += ti->ti_len; + tcpstat.tcps_rcvpack++; + tcpstat.tcps_rcvbyte += ti->ti_len; + /* + * Add data to socket buffer. + */ + if (so->so_emu) { + if (tcp_emu(so,m)) sbappend(so, m); + } else + sbappend(so, m); + + /* + * XXX This is called when data arrives. Later, check + * if we can actually write() to the socket + * XXX Need to check? It's be NON_BLOCKING + */ +/* sorwakeup(so); */ + + /* + * If this is a short packet, then ACK now - with Nagel + * congestion avoidance sender won't send more until + * he gets an ACK. + * + * It is better to not delay acks at all to maximize + * TCP throughput. See RFC 2581. + */ + tp->t_flags |= TF_ACKNOW; + tcp_output(tp); + return; + } + } /* header prediction */ + /* + * Calculate amount of space in receive window, + * and then do TCP input processing. + * Receive window is amount of space in rcv queue, + * but not less than advertised window. + */ + { int win; + win = sbspace(&so->so_rcv); + if (win < 0) + win = 0; + tp->rcv_wnd = max(win, (int)(tp->rcv_adv - tp->rcv_nxt)); + } + + switch (tp->t_state) { + + /* + * If the state is LISTEN then ignore segment if it contains an RST. + * If the segment contains an ACK then it is bad and send a RST. + * If it does not contain a SYN then it is not interesting; drop it. + * Don't bother responding if the destination was a broadcast. + * Otherwise initialize tp->rcv_nxt, and tp->irs, select an initial + * tp->iss, and send a segment: + * + * Also initialize tp->snd_nxt to tp->iss+1 and tp->snd_una to tp->iss. + * Fill in remote peer address fields if not previously specified. + * Enter SYN_RECEIVED state, and process any other fields of this + * segment in this state. + */ + case TCPS_LISTEN: { + + if (tiflags & TH_RST) + goto drop; + if (tiflags & TH_ACK) + goto dropwithreset; + if ((tiflags & TH_SYN) == 0) + goto drop; + + /* + * This has way too many gotos... + * But a bit of spaghetti code never hurt anybody :) + */ + + /* + * If this is destined for the control address, then flag to + * tcp_ctl once connected, otherwise connect + */ + if ((so->so_faddr.s_addr&htonl(0xffffff00)) == special_addr.s_addr) { + int lastbyte=ntohl(so->so_faddr.s_addr) & 0xff; + if (lastbyte!=CTL_ALIAS && lastbyte!=CTL_DNS) { +#if 0 + if(lastbyte==CTL_CMD || lastbyte==CTL_EXEC) { + /* Command or exec adress */ + so->so_state |= SS_CTL; + } else +#endif + { + /* May be an add exec */ + struct ex_list *ex_ptr; + for(ex_ptr = exec_list; ex_ptr; ex_ptr = ex_ptr->ex_next) { + if(ex_ptr->ex_fport == so->so_fport && + lastbyte == ex_ptr->ex_addr) { + so->so_state |= SS_CTL; + break; + } + } + } + if(so->so_state & SS_CTL) goto cont_input; + } + /* CTL_ALIAS: Do nothing, tcp_fconnect will be called on it */ + } + + if (so->so_emu & EMU_NOCONNECT) { + so->so_emu &= ~EMU_NOCONNECT; + goto cont_input; + } + + if((tcp_fconnect(so) == -1) && (errno != EINPROGRESS) && (errno != EWOULDBLOCK)) { + u_char code=ICMP_UNREACH_NET; + DEBUG_MISC((dfd," tcp fconnect errno = %d-%s\n", + errno,strerror(errno))); + if(errno == ECONNREFUSED) { + /* ACK the SYN, send RST to refuse the connection */ + tcp_respond(tp, ti, m, ti->ti_seq+1, (tcp_seq)0, + TH_RST|TH_ACK); + } else { + if(errno == EHOSTUNREACH) code=ICMP_UNREACH_HOST; + HTONL(ti->ti_seq); /* restore tcp header */ + HTONL(ti->ti_ack); + HTONS(ti->ti_win); + HTONS(ti->ti_urp); + m->m_data -= sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr); + m->m_len += sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr); + *ip=save_ip; + icmp_error(m, ICMP_UNREACH,code, 0,strerror(errno)); + } + tp = tcp_close(tp); + m_free(m); + } else { + /* + * Haven't connected yet, save the current SLIRPmbuf + * and ti, and return + * XXX Some OS's don't tell us whether the connect() + * succeeded or not. So we must time it out. + */ + so->so_m = m; + so->so_ti = ti; + tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT; + tp->t_state = TCPS_SYN_RECEIVED; + } + return; + + cont_conn: + /* m==NULL + * Check if the connect succeeded + */ + if (so->so_state & SS_NOFDREF) { + tp = tcp_close(tp); + goto dropwithreset; + } + cont_input: + tcp_template(tp); + + if (optp) + tcp_dooptions(tp, (u_char *)optp, optlen, ti); + /* , */ + /* &ts_present, &ts_val, &ts_ecr); */ + + if (iss) + tp->iss = iss; + else + tp->iss = tcp_iss; + tcp_iss += TCP_ISSINCR/2; + tp->irs = ti->ti_seq; + tcp_sendseqinit(tp); + tcp_rcvseqinit(tp); + tp->t_flags |= TF_ACKNOW; + tp->t_state = TCPS_SYN_RECEIVED; + tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT; + tcpstat.tcps_accepts++; + goto trimthenstep6; + } /* case TCPS_LISTEN */ + + /* + * If the state is SYN_SENT: + * if seg contains an ACK, but not for our SYN, drop the input. + * if seg contains a RST, then drop the connection. + * if seg does not contain SYN, then drop it. + * Otherwise this is an acceptable SYN segment + * initialize tp->rcv_nxt and tp->irs + * if seg contains ack then advance tp->snd_una + * if SYN has been acked change to ESTABLISHED else SYN_RCVD state + * arrange for segment to be acked (eventually) + * continue processing rest of data/controls, beginning with URG + */ + case TCPS_SYN_SENT: + if ((tiflags & TH_ACK) && + (SEQ_LEQ(ti->ti_ack, tp->iss) || + SEQ_GT(ti->ti_ack, tp->snd_max))) + goto dropwithreset; + + if (tiflags & TH_RST) { + if (tiflags & TH_ACK) + tp = tcp_drop(tp,0); /* XXX Check t_softerror! */ + goto drop; + } + + if ((tiflags & TH_SYN) == 0) + goto drop; + if (tiflags & TH_ACK) { + tp->snd_una = ti->ti_ack; + if (SEQ_LT(tp->snd_nxt, tp->snd_una)) + tp->snd_nxt = tp->snd_una; + } + + tp->t_timer[TCPT_REXMT] = 0; + tp->irs = ti->ti_seq; + tcp_rcvseqinit(tp); + tp->t_flags |= TF_ACKNOW; + if (tiflags & TH_ACK && SEQ_GT(tp->snd_una, tp->iss)) { + tcpstat.tcps_connects++; + soisfconnected(so); + tp->t_state = TCPS_ESTABLISHED; + + /* Do window scaling on this connection? */ +/* if ((tp->t_flags & (TF_RCVD_SCALE|TF_REQ_SCALE)) == + * (TF_RCVD_SCALE|TF_REQ_SCALE)) { + * tp->snd_scale = tp->requested_s_scale; + * tp->rcv_scale = tp->request_r_scale; + * } + */ + (void) tcp_reass(tp, (struct tcpiphdr *)0, + (struct SLIRPmbuf *)0); + /* + * if we didn't have to retransmit the SYN, + * use its rtt as our initial srtt & rtt var. + */ + if (tp->t_rtt) + tcp_xmit_timer(tp, tp->t_rtt); + } else + tp->t_state = TCPS_SYN_RECEIVED; + +trimthenstep6: + /* + * Advance ti->ti_seq to correspond to first data byte. + * If data, trim to stay within window, + * dropping FIN if necessary. + */ + ti->ti_seq++; + if (ti->ti_len > tp->rcv_wnd) { + todrop = ti->ti_len - tp->rcv_wnd; + m_adj(m, -todrop); + ti->ti_len = tp->rcv_wnd; + tiflags &= ~TH_FIN; + tcpstat.tcps_rcvpackafterwin++; + tcpstat.tcps_rcvbyteafterwin += todrop; + } + tp->snd_wl1 = ti->ti_seq - 1; + tp->rcv_up = ti->ti_seq; + goto step6; + } /* switch tp->t_state */ + /* + * States other than LISTEN or SYN_SENT. + * First check timestamp, if present. + * Then check that at least some bytes of segment are within + * receive window. If segment begins before rcv_nxt, + * drop leading data (and SYN); if nothing left, just ack. + * + * RFC 1323 PAWS: If we have a timestamp reply on this segment + * and it's less than ts_recent, drop it. + */ +/* if (ts_present && (tiflags & TH_RST) == 0 && tp->ts_recent && + * TSTMP_LT(ts_val, tp->ts_recent)) { + * + */ /* Check to see if ts_recent is over 24 days old. */ +/* if ((int)(tcp_now - tp->ts_recent_age) > TCP_PAWS_IDLE) { + */ /* + * * Invalidate ts_recent. If this segment updates + * * ts_recent, the age will be reset later and ts_recent + * * will get a valid value. If it does not, setting + * * ts_recent to zero will at least satisfy the + * * requirement that zero be placed in the timestamp + * * echo reply when ts_recent isn't valid. The + * * age isn't reset until we get a valid ts_recent + * * because we don't want out-of-order segments to be + * * dropped when ts_recent is old. + * */ +/* tp->ts_recent = 0; + * } else { + * tcpstat.tcps_rcvduppack++; + * tcpstat.tcps_rcvdupbyte += ti->ti_len; + * tcpstat.tcps_pawsdrop++; + * goto dropafterack; + * } + * } + */ + + todrop = tp->rcv_nxt - ti->ti_seq; + if (todrop > 0) { + if (tiflags & TH_SYN) { + tiflags &= ~TH_SYN; + ti->ti_seq++; + if (ti->ti_urp > 1) + ti->ti_urp--; + else + tiflags &= ~TH_URG; + todrop--; + } + /* + * Following if statement from Stevens, vol. 2, p. 960. + */ + if (todrop > ti->ti_len + || (todrop == ti->ti_len && (tiflags & TH_FIN) == 0)) { + /* + * Any valid FIN must be to the left of the window. + * At this point the FIN must be a duplicate or out + * of sequence; drop it. + */ + tiflags &= ~TH_FIN; + + /* + * Send an ACK to resynchronize and drop any data. + * But keep on processing for RST or ACK. + */ + tp->t_flags |= TF_ACKNOW; + todrop = ti->ti_len; + tcpstat.tcps_rcvduppack++; + tcpstat.tcps_rcvdupbyte += todrop; + } else { + tcpstat.tcps_rcvpartduppack++; + tcpstat.tcps_rcvpartdupbyte += todrop; + } + m_adj(m, todrop); + ti->ti_seq += todrop; + ti->ti_len -= todrop; + if (ti->ti_urp > todrop) + ti->ti_urp -= todrop; + else { + tiflags &= ~TH_URG; + ti->ti_urp = 0; + } + } + /* + * If new data are received on a connection after the + * user processes are gone, then RST the other end. + */ + if ((so->so_state & SS_NOFDREF) && + tp->t_state > TCPS_CLOSE_WAIT && ti->ti_len) { + tp = tcp_close(tp); + tcpstat.tcps_rcvafterclose++; + goto dropwithreset; + } + + /* + * If segment ends after window, drop trailing data + * (and PUSH and FIN); if nothing left, just ACK. + */ + todrop = (ti->ti_seq+ti->ti_len) - (tp->rcv_nxt+tp->rcv_wnd); + if (todrop > 0) { + tcpstat.tcps_rcvpackafterwin++; + if (todrop >= ti->ti_len) { + tcpstat.tcps_rcvbyteafterwin += ti->ti_len; + /* + * If a new connection request is received + * while in TIME_WAIT, drop the old connection + * and start over if the sequence numbers + * are above the previous ones. + */ + if (tiflags & TH_SYN && + tp->t_state == TCPS_TIME_WAIT && + SEQ_GT(ti->ti_seq, tp->rcv_nxt)) { + iss = tp->rcv_nxt + TCP_ISSINCR; + tp = tcp_close(tp); + goto findso; + } + /* + * If window is closed can only take segments at + * window edge, and have to drop data and PUSH from + * incoming segments. Continue processing, but + * remember to ack. Otherwise, drop segment + * and ack. + */ + if (tp->rcv_wnd == 0 && ti->ti_seq == tp->rcv_nxt) { + tp->t_flags |= TF_ACKNOW; + tcpstat.tcps_rcvwinprobe++; + } else + goto dropafterack; + } else + tcpstat.tcps_rcvbyteafterwin += todrop; + m_adj(m, -todrop); + ti->ti_len -= todrop; + tiflags &= ~(TH_PUSH|TH_FIN); + } + + /* + * If last ACK falls within this segment's sequence numbers, + * record its timestamp. + */ +/* if (ts_present && SEQ_LEQ(ti->ti_seq, tp->last_ack_sent) && + * SEQ_LT(tp->last_ack_sent, ti->ti_seq + ti->ti_len + + * ((tiflags & (TH_SYN|TH_FIN)) != 0))) { + * tp->ts_recent_age = tcp_now; + * tp->ts_recent = ts_val; + * } + */ + + /* + * If the RST bit is set examine the state: + * SYN_RECEIVED STATE: + * If passive open, return to LISTEN state. + * If active open, inform user that connection was refused. + * ESTABLISHED, FIN_WAIT_1, FIN_WAIT2, CLOSE_WAIT STATES: + * Inform user that connection was reset, and close tcb. + * CLOSING, LAST_ACK, TIME_WAIT STATES + * Close the tcb. + */ + if (tiflags&TH_RST) switch (tp->t_state) { + + case TCPS_SYN_RECEIVED: +/* so->so_error = ECONNREFUSED; */ + goto close; + + case TCPS_ESTABLISHED: + case TCPS_FIN_WAIT_1: + case TCPS_FIN_WAIT_2: + case TCPS_CLOSE_WAIT: +/* so->so_error = ECONNRESET; */ + close: + tp->t_state = TCPS_CLOSED; + tcpstat.tcps_drops++; + tp = tcp_close(tp); + goto drop; + + case TCPS_CLOSING: + case TCPS_LAST_ACK: + case TCPS_TIME_WAIT: + tp = tcp_close(tp); + goto drop; + } + + /* + * If a SYN is in the window, then this is an + * error and we send an RST and drop the connection. + */ + if (tiflags & TH_SYN) { + tp = tcp_drop(tp,0); + goto dropwithreset; + } + + /* + * If the ACK bit is off we drop the segment and return. + */ + if ((tiflags & TH_ACK) == 0) goto drop; + + /* + * Ack processing. + */ + switch (tp->t_state) { + /* + * In SYN_RECEIVED state if the ack ACKs our SYN then enter + * ESTABLISHED state and continue processing, otherwise + * send an RST. una<=ack<=max + */ + case TCPS_SYN_RECEIVED: + + if (SEQ_GT(tp->snd_una, ti->ti_ack) || + SEQ_GT(ti->ti_ack, tp->snd_max)) + goto dropwithreset; + tcpstat.tcps_connects++; + tp->t_state = TCPS_ESTABLISHED; + /* + * The sent SYN is ack'ed with our sequence number +1 + * The first data byte already in the buffer will get + * lost if no correction is made. This is only needed for + * SS_CTL since the buffer is empty otherwise. + * tp->snd_una++; or: + */ + tp->snd_una=ti->ti_ack; + if (so->so_state & SS_CTL) { + /* So tcp_ctl reports the right state */ + ret = tcp_ctl(so); + if (ret == 1) { + soisfconnected(so); + so->so_state &= ~SS_CTL; /* success XXX */ + } else if (ret == 2) { + so->so_state = SS_NOFDREF; /* CTL_CMD */ + } else { + needoutput = 1; + tp->t_state = TCPS_FIN_WAIT_1; + } + } else { + soisfconnected(so); + } + + /* Do window scaling? */ +/* if ((tp->t_flags & (TF_RCVD_SCALE|TF_REQ_SCALE)) == + * (TF_RCVD_SCALE|TF_REQ_SCALE)) { + * tp->snd_scale = tp->requested_s_scale; + * tp->rcv_scale = tp->request_r_scale; + * } + */ + (void) tcp_reass(tp, (struct tcpiphdr *)0, (struct SLIRPmbuf *)0); + tp->snd_wl1 = ti->ti_seq - 1; + /* Avoid ack processing; snd_una==ti_ack => dup ack */ + goto synrx_to_est; + /* fall into ... */ + + /* + * In ESTABLISHED state: drop duplicate ACKs; ACK out of range + * ACKs. If the ack is in the range + * tp->snd_una < ti->ti_ack <= tp->snd_max + * then advance tp->snd_una to ti->ti_ack and drop + * data from the retransmission queue. If this ACK reflects + * more up to date window information we update our window information. + */ + case TCPS_ESTABLISHED: + case TCPS_FIN_WAIT_1: + case TCPS_FIN_WAIT_2: + case TCPS_CLOSE_WAIT: + case TCPS_CLOSING: + case TCPS_LAST_ACK: + case TCPS_TIME_WAIT: + + if (SEQ_LEQ(ti->ti_ack, tp->snd_una)) { + if (ti->ti_len == 0 && tiwin == tp->snd_wnd) { + tcpstat.tcps_rcvdupack++; + DEBUG_MISC((dfd," dup ack m = %lx so = %lx \n", + (long )m, (long )so)); + /* + * If we have outstanding data (other than + * a window probe), this is a completely + * duplicate ack (ie, window info didn't + * change), the ack is the biggest we've + * seen and we've seen exactly our rexmt + * threshold of them, assume a packet + * has been dropped and retransmit it. + * Kludge snd_nxt & the congestion + * window so we send only this one + * packet. + * + * We know we're losing at the current + * window size so do congestion avoidance + * (set ssthresh to half the current window + * and pull our congestion window back to + * the new ssthresh). + * + * Dup acks mean that packets have left the + * network (they're now cached at the receiver) + * so bump cwnd by the amount in the receiver + * to keep a constant cwnd packets in the + * network. + */ + if (tp->t_timer[TCPT_REXMT] == 0 || + ti->ti_ack != tp->snd_una) + tp->t_dupacks = 0; + else if (++tp->t_dupacks == tcprexmtthresh) { + tcp_seq onxt = tp->snd_nxt; + u_int win = + min(tp->snd_wnd, tp->snd_cwnd) / 2 / + tp->t_maxseg; + + if (win < 2) + win = 2; + tp->snd_ssthresh = win * tp->t_maxseg; + tp->t_timer[TCPT_REXMT] = 0; + tp->t_rtt = 0; + tp->snd_nxt = ti->ti_ack; + tp->snd_cwnd = tp->t_maxseg; + (void) tcp_output(tp); + tp->snd_cwnd = tp->snd_ssthresh + + tp->t_maxseg * tp->t_dupacks; + if (SEQ_GT(onxt, tp->snd_nxt)) + tp->snd_nxt = onxt; + goto drop; + } else if (tp->t_dupacks > tcprexmtthresh) { + tp->snd_cwnd += tp->t_maxseg; + (void) tcp_output(tp); + goto drop; + } + } else + tp->t_dupacks = 0; + break; + } + synrx_to_est: + /* + * If the congestion window was inflated to account + * for the other side's cached packets, retract it. + */ + if (tp->t_dupacks > tcprexmtthresh && + tp->snd_cwnd > tp->snd_ssthresh) + tp->snd_cwnd = tp->snd_ssthresh; + tp->t_dupacks = 0; + if (SEQ_GT(ti->ti_ack, tp->snd_max)) { + tcpstat.tcps_rcvacktoomuch++; + goto dropafterack; + } + acked = ti->ti_ack - tp->snd_una; + tcpstat.tcps_rcvackpack++; + tcpstat.tcps_rcvackbyte += acked; + + /* + * If we have a timestamp reply, update smoothed + * round trip time. If no timestamp is present but + * transmit timer is running and timed sequence + * number was acked, update smoothed round trip time. + * Since we now have an rtt measurement, cancel the + * timer backoff (cf., Phil Karn's retransmit alg.). + * Recompute the initial retransmit timer. + */ +/* if (ts_present) + * tcp_xmit_timer(tp, tcp_now-ts_ecr+1); + * else + */ + if (tp->t_rtt && SEQ_GT(ti->ti_ack, tp->t_rtseq)) + tcp_xmit_timer(tp,tp->t_rtt); + + /* + * If all outstanding data is acked, stop retransmit + * timer and remember to restart (more output or persist). + * If there is more data to be acked, restart retransmit + * timer, using current (possibly backed-off) value. + */ + if (ti->ti_ack == tp->snd_max) { + tp->t_timer[TCPT_REXMT] = 0; + needoutput = 1; + } else if (tp->t_timer[TCPT_PERSIST] == 0) + tp->t_timer[TCPT_REXMT] = tp->t_rxtcur; + /* + * When new data is acked, open the congestion window. + * If the window gives us less than ssthresh packets + * in flight, open exponentially (maxseg per packet). + * Otherwise open linearly: maxseg per window + * (maxseg^2 / cwnd per packet). + */ + { + u_int cw = tp->snd_cwnd; + u_int incr = tp->t_maxseg; + + if (cw > tp->snd_ssthresh) + incr = incr * incr / cw; + tp->snd_cwnd = min(cw + incr, TCP_MAXWIN<snd_scale); + } + if (acked > so->so_snd.sb_cc) { + tp->snd_wnd -= so->so_snd.sb_cc; + sbdrop(&so->so_snd, (int )so->so_snd.sb_cc); + ourfinisacked = 1; + } else { + sbdrop(&so->so_snd, acked); + tp->snd_wnd -= acked; + ourfinisacked = 0; + } + /* + * XXX sowwakup is called when data is acked and there's room for + * for more data... it should read() the socket + */ +/* if (so->so_snd.sb_flags & SB_NOTIFY) + * sowwakeup(so); + */ + tp->snd_una = ti->ti_ack; + if (SEQ_LT(tp->snd_nxt, tp->snd_una)) + tp->snd_nxt = tp->snd_una; + + switch (tp->t_state) { + + /* + * In FIN_WAIT_1 STATE in addition to the processing + * for the ESTABLISHED state if our FIN is now acknowledged + * then enter FIN_WAIT_2. + */ + case TCPS_FIN_WAIT_1: + if (ourfinisacked) { + /* + * If we can't receive any more + * data, then closing user can proceed. + * Starting the timer is contrary to the + * specification, but if we don't get a FIN + * we'll hang forever. + */ + if (so->so_state & SS_FCANTRCVMORE) { + soisfdisconnected(so); + tp->t_timer[TCPT_2MSL] = tcp_maxidle; + } + tp->t_state = TCPS_FIN_WAIT_2; + } + break; + + /* + * In CLOSING STATE in addition to the processing for + * the ESTABLISHED state if the ACK acknowledges our FIN + * then enter the TIME-WAIT state, otherwise ignore + * the segment. + */ + case TCPS_CLOSING: + if (ourfinisacked) { + tp->t_state = TCPS_TIME_WAIT; + tcp_canceltimers(tp); + tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL; + soisfdisconnected(so); + } + break; + + /* + * In LAST_ACK, we may still be waiting for data to drain + * and/or to be acked, as well as for the ack of our FIN. + * If our FIN is now acknowledged, delete the TCB, + * enter the closed state and return. + */ + case TCPS_LAST_ACK: + if (ourfinisacked) { + tp = tcp_close(tp); + goto drop; + } + break; + + /* + * In TIME_WAIT state the only thing that should arrive + * is a retransmission of the remote FIN. Acknowledge + * it and restart the finack timer. + */ + case TCPS_TIME_WAIT: + tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL; + goto dropafterack; + } + } /* switch(tp->t_state) */ + +step6: + /* + * Update window information. + * Don't look at window if no ACK: TAC's send garbage on first SYN. + */ + if ((tiflags & TH_ACK) && + (SEQ_LT(tp->snd_wl1, ti->ti_seq) || + (tp->snd_wl1 == ti->ti_seq && (SEQ_LT(tp->snd_wl2, ti->ti_ack) || + (tp->snd_wl2 == ti->ti_ack && tiwin > tp->snd_wnd))))) { + /* keep track of pure window updates */ + if (ti->ti_len == 0 && + tp->snd_wl2 == ti->ti_ack && tiwin > tp->snd_wnd) + tcpstat.tcps_rcvwinupd++; + tp->snd_wnd = tiwin; + tp->snd_wl1 = ti->ti_seq; + tp->snd_wl2 = ti->ti_ack; + if (tp->snd_wnd > tp->max_sndwnd) + tp->max_sndwnd = tp->snd_wnd; + needoutput = 1; + } + + /* + * Process segments with URG. + */ + if ((tiflags & TH_URG) && ti->ti_urp && + TCPS_HAVERCVDFIN(tp->t_state) == 0) { + /* + * This is a kludge, but if we receive and accept + * random urgent pointers, we'll crash in + * soreceive. It's hard to imagine someone + * actually wanting to send this much urgent data. + */ + if (ti->ti_urp + so->so_rcv.sb_cc > so->so_rcv.sb_datalen) { + ti->ti_urp = 0; + tiflags &= ~TH_URG; + goto dodata; + } + /* + * If this segment advances the known urgent pointer, + * then mark the data stream. This should not happen + * in CLOSE_WAIT, CLOSING, LAST_ACK or TIME_WAIT STATES since + * a FIN has been received from the remote side. + * In these states we ignore the URG. + * + * According to RFC961 (Assigned Protocols), + * the urgent pointer points to the last octet + * of urgent data. We continue, however, + * to consider it to indicate the first octet + * of data past the urgent section as the original + * spec states (in one of two places). + */ + if (SEQ_GT(ti->ti_seq+ti->ti_urp, tp->rcv_up)) { + tp->rcv_up = ti->ti_seq + ti->ti_urp; + so->so_urgc = so->so_rcv.sb_cc + + (tp->rcv_up - tp->rcv_nxt); /* -1; */ + tp->rcv_up = ti->ti_seq + ti->ti_urp; + + } + } else + /* + * If no out of band data is expected, + * pull receive urgent pointer along + * with the receive window. + */ + if (SEQ_GT(tp->rcv_nxt, tp->rcv_up)) + tp->rcv_up = tp->rcv_nxt; +dodata: + + /* + * Process the segment text, merging it into the TCP sequencing queue, + * and arranging for acknowledgment of receipt if necessary. + * This process logically involves adjusting tp->rcv_wnd as data + * is presented to the user (this happens in tcp_usrreq.c, + * case PRU_RCVD). If a FIN has already been received on this + * connection then we just ignore the text. + */ + if ((ti->ti_len || (tiflags&TH_FIN)) && + TCPS_HAVERCVDFIN(tp->t_state) == 0) { + TCP_REASS(tp, ti, m, so, tiflags); + /* + * Note the amount of data that peer has sent into + * our window, in order to estimate the sender's + * buffer size. + */ + len = so->so_rcv.sb_datalen - (tp->rcv_adv - tp->rcv_nxt); + } else { + m_free(m); + tiflags &= ~TH_FIN; + } + + /* + * If FIN is received ACK the FIN and let the user know + * that the connection is closing. + */ + if (tiflags & TH_FIN) { + if (TCPS_HAVERCVDFIN(tp->t_state) == 0) { + /* + * If we receive a FIN we can't send more data, + * set it SS_FDRAIN + * Shutdown the socket if there is no rx data in the + * buffer. + * soread() is called on completion of shutdown() and + * will got to TCPS_LAST_ACK, and use tcp_output() + * to send the FIN. + */ +/* sofcantrcvmore(so); */ + sofwdrain(so); + + tp->t_flags |= TF_ACKNOW; + tp->rcv_nxt++; + } + switch (tp->t_state) { + + /* + * In SYN_RECEIVED and ESTABLISHED STATES + * enter the CLOSE_WAIT state. + */ + case TCPS_SYN_RECEIVED: + case TCPS_ESTABLISHED: + if(so->so_emu == EMU_CTL) /* no shutdown on socket */ + tp->t_state = TCPS_LAST_ACK; + else + tp->t_state = TCPS_CLOSE_WAIT; + break; + + /* + * If still in FIN_WAIT_1 STATE FIN has not been acked so + * enter the CLOSING state. + */ + case TCPS_FIN_WAIT_1: + tp->t_state = TCPS_CLOSING; + break; + + /* + * In FIN_WAIT_2 state enter the TIME_WAIT state, + * starting the time-wait timer, turning off the other + * standard timers. + */ + case TCPS_FIN_WAIT_2: + tp->t_state = TCPS_TIME_WAIT; + tcp_canceltimers(tp); + tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL; + soisfdisconnected(so); + break; + + /* + * In TIME_WAIT state restart the 2 MSL time_wait timer. + */ + case TCPS_TIME_WAIT: + tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL; + break; + } + } + + /* + * If this is a small packet, then ACK now - with Nagel + * congestion avoidance sender won't send more until + * he gets an ACK. + * + * See above. + */ +/* if (ti->ti_len && (unsigned)ti->ti_len < tp->t_maxseg) { + */ +/* if ((ti->ti_len && (unsigned)ti->ti_len < tp->t_maxseg && + * (so->so_iptos & IPTOS_LOWDELAY) == 0) || + * ((so->so_iptos & IPTOS_LOWDELAY) && + * ((struct tcpiphdr_2 *)ti)->first_char == (char)27)) { + */ + if (ti->ti_len && (unsigned)ti->ti_len <= 5 && + ((struct tcpiphdr_2 *)ti)->first_char == (char)27) { + tp->t_flags |= TF_ACKNOW; + } + + /* + * Return any desired output. + */ + if (needoutput || (tp->t_flags & TF_ACKNOW)) { + (void) tcp_output(tp); + } + return; + +dropafterack: + /* + * Generate an ACK dropping incoming segment if it occupies + * sequence space, where the ACK reflects our state. + */ + if (tiflags & TH_RST) + goto drop; + m_freem(m); + tp->t_flags |= TF_ACKNOW; + (void) tcp_output(tp); + return; + +dropwithreset: + /* reuses m if m!=NULL, m_free() unnecessary */ + if (tiflags & TH_ACK) + tcp_respond(tp, ti, m, (tcp_seq)0, ti->ti_ack, TH_RST); + else { + if (tiflags & TH_SYN) ti->ti_len++; + tcp_respond(tp, ti, m, ti->ti_seq+ti->ti_len, (tcp_seq)0, + TH_RST|TH_ACK); + } + + return; + +drop: + /* + * Drop space held by incoming segment and return. + */ + m_free(m); + + return; +} + + /* , ts_present, ts_val, ts_ecr) */ +/* int *ts_present; + * u_int32_t *ts_val, *ts_ecr; + */ +void +tcp_dooptions(tp, cp, cnt, ti) + struct tcpcb *tp; + u_char *cp; + int cnt; + struct tcpiphdr *ti; +{ + u_int16_t mss; + int opt, optlen; + + DEBUG_CALL("tcp_dooptions"); + DEBUG_ARGS((dfd," tp = %lx cnt=%i \n", (long )tp, cnt)); + + for (; cnt > 0; cnt -= optlen, cp += optlen) { + opt = cp[0]; + if (opt == TCPOPT_EOL) + break; + if (opt == TCPOPT_NOP) + optlen = 1; + else { + optlen = cp[1]; + if (optlen <= 0) + break; + } + switch (opt) { + + default: + continue; + + case TCPOPT_MAXSEG: + if (optlen != TCPOLEN_MAXSEG) + continue; + if (!(ti->ti_flags & TH_SYN)) + continue; + memcpy((char *) &mss, (char *) cp + 2, sizeof(mss)); + NTOHS(mss); + (void) tcp_mss(tp, mss); /* sets t_maxseg */ + break; + +/* case TCPOPT_WINDOW: + * if (optlen != TCPOLEN_WINDOW) + * continue; + * if (!(ti->ti_flags & TH_SYN)) + * continue; + * tp->t_flags |= TF_RCVD_SCALE; + * tp->requested_s_scale = min(cp[2], TCP_MAX_WINSHIFT); + * break; + */ +/* case TCPOPT_TIMESTAMP: + * if (optlen != TCPOLEN_TIMESTAMP) + * continue; + * *ts_present = 1; + * memcpy((char *) ts_val, (char *)cp + 2, sizeof(*ts_val)); + * NTOHL(*ts_val); + * memcpy((char *) ts_ecr, (char *)cp + 6, sizeof(*ts_ecr)); + * NTOHL(*ts_ecr); + * + */ /* + * * A timestamp received in a SYN makes + * * it ok to send timestamp requests and replies. + * */ +/* if (ti->ti_flags & TH_SYN) { + * tp->t_flags |= TF_RCVD_TSTMP; + * tp->ts_recent = *ts_val; + * tp->ts_recent_age = tcp_now; + * } + */ break; + } + } +} + + +/* + * Pull out of band byte out of a segment so + * it doesn't appear in the user's data queue. + * It is still reflected in the segment length for + * sequencing purposes. + */ + +#ifdef notdef + +void +tcp_pulloutofband(so, ti, m) + struct SLIRPsocket *so; + struct tcpiphdr *ti; + struct SLIRPmbuf *m; +{ + int cnt = ti->ti_urp - 1; + + while (cnt >= 0) { + if (m->m_len > cnt) { + char *cp = mtod(m, SLIRPcaddr_t) + cnt; + struct tcpcb *tp = sototcpcb(so); + + tp->t_iobc = *cp; + tp->t_oobflags |= TCPOOB_HAVEDATA; + memcpy(sp, cp+1, (unsigned)(m->m_len - cnt - 1)); + m->m_len--; + return; + } + cnt -= m->m_len; + m = m->m_next; /* XXX WRONG! Fix it! */ + if (m == 0) + break; + } + panic("tcp_pulloutofband"); +} + +#endif /* notdef */ + +/* + * Collect new round-trip time estimate + * and update averages and current timeout. + */ + +void +tcp_xmit_timer(tp, rtt) + struct tcpcb *tp; + int rtt; +{ + short delta; + + DEBUG_CALL("tcp_xmit_timer"); + DEBUG_ARG("tp = %lx", (long)tp); + DEBUG_ARG("rtt = %d", rtt); + + tcpstat.tcps_rttupdated++; + if (tp->t_srtt != 0) { + /* + * srtt is stored as fixed point with 3 bits after the + * binary point (i.e., scaled by 8). The following magic + * is equivalent to the smoothing algorithm in rfc793 with + * an alpha of .875 (srtt = rtt/8 + srtt*7/8 in fixed + * point). Adjust rtt to origin 0. + */ + delta = rtt - 1 - (tp->t_srtt >> TCP_RTT_SHIFT); + if ((tp->t_srtt += delta) <= 0) + tp->t_srtt = 1; + /* + * We accumulate a smoothed rtt variance (actually, a + * smoothed mean difference), then set the retransmit + * timer to smoothed rtt + 4 times the smoothed variance. + * rttvar is stored as fixed point with 2 bits after the + * binary point (scaled by 4). The following is + * equivalent to rfc793 smoothing with an alpha of .75 + * (rttvar = rttvar*3/4 + |delta| / 4). This replaces + * rfc793's wired-in beta. + */ + if (delta < 0) + delta = -delta; + delta -= (tp->t_rttvar >> TCP_RTTVAR_SHIFT); + if ((tp->t_rttvar += delta) <= 0) + tp->t_rttvar = 1; + } else { + /* + * No rtt measurement yet - use the unsmoothed rtt. + * Set the variance to half the rtt (so our first + * retransmit happens at 3*rtt). + */ + tp->t_srtt = rtt << TCP_RTT_SHIFT; + tp->t_rttvar = rtt << (TCP_RTTVAR_SHIFT - 1); + } + tp->t_rtt = 0; + tp->t_rxtshift = 0; + + /* + * the retransmit should happen at rtt + 4 * rttvar. + * Because of the way we do the smoothing, srtt and rttvar + * will each average +1/2 tick of bias. When we compute + * the retransmit timer, we want 1/2 tick of rounding and + * 1 extra tick because of +-1/2 tick uncertainty in the + * firing of the timer. The bias will give us exactly the + * 1.5 tick we need. But, because the bias is + * statistical, we have to test that we don't drop below + * the minimum feasible timer (which is 2 ticks). + */ + TCPT_RANGESET(tp->t_rxtcur, TCP_REXMTVAL(tp), + (short)tp->t_rttmin, TCPTV_REXMTMAX); /* XXX */ + + /* + * We received an ack for a packet that wasn't retransmitted; + * it is probably safe to discard any error indications we've + * received recently. This isn't quite right, but close enough + * for now (a route might have failed after we sent a segment, + * and the return path might not be symmetrical). + */ + tp->t_softerror = 0; +} + +/* + * Determine a reasonable value for maxseg size. + * If the route is known, check route for mtu. + * If none, use an mss that can be handled on the outgoing + * interface without forcing IP to fragment; if bigger than + * an SLIRPmbuf cluster (MCLBYTES), round down to nearest multiple of MCLBYTES + * to utilize large SLIRPmbufs. If no route is found, route has no mtu, + * or the destination isn't local, use a default, hopefully conservative + * size (usually 512 or the default IP max size, but no more than the mtu + * of the interface), as we can't discover anything about intervening + * gateways or networks. We also initialize the congestion/slow start + * window to be a single segment if the destination isn't local. + * While looking at the routing entry, we also initialize other path-dependent + * parameters from pre-set or cached values in the routing entry. + */ + +int +tcp_mss(tp, offer) + struct tcpcb *tp; + u_int offer; +{ + struct SLIRPsocket *so = tp->t_socket; + int mss; + + DEBUG_CALL("tcp_mss"); + DEBUG_ARG("tp = %lx", (long)tp); + DEBUG_ARG("offer = %d", offer); + + mss = min(if_mtu, if_mru) - sizeof(struct tcpiphdr); + if (offer) + mss = min(mss, offer); + mss = max(mss, 32); + if (mss < tp->t_maxseg || offer != 0) + tp->t_maxseg = mss; + + tp->snd_cwnd = mss; + + sbreserve(&so->so_snd, tcp_sndspace+((tcp_sndspace%mss)?(mss-(tcp_sndspace%mss)):0)); + sbreserve(&so->so_rcv, tcp_rcvspace+((tcp_rcvspace%mss)?(mss-(tcp_rcvspace%mss)):0)); + + DEBUG_MISC((dfd, " returning mss = %d\n", mss)); + + return mss; +} diff --git a/src/slirp/tcp_output.c b/src/slirp/tcp_output.c new file mode 100644 index 0000000..8bafb82 --- /dev/null +++ b/src/slirp/tcp_output.c @@ -0,0 +1,601 @@ +/* + * Copyright (c) 1982, 1986, 1988, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)tcp_output.c 8.3 (Berkeley) 12/30/93 + * tcp_output.c,v 1.3 1994/09/15 10:36:55 davidg Exp + */ + +/* + * Changes and additions relating to SLiRP + * Copyright (c) 1995 Danny Gasparovski. + * + * Please read the file COPYRIGHT for the + * terms and conditions of the copyright. + */ + +#include "slirp.h" + +/* + * Since this is only used in "stats socket", we give meaning + * names instead of the REAL names + */ +char *tcpstates[] = { +/* "CLOSED", "LISTEN", "SYN_SENT", "SYN_RCVD", */ + "REDIRECT", "LISTEN", "SYN_SENT", "SYN_RCVD", + "ESTABLISHED", "CLOSE_WAIT", "FIN_WAIT_1", "CLOSING", + "LAST_ACK", "FIN_WAIT_2", "TIME_WAIT", +}; + +u_char tcp_outflags[TCP_NSTATES] = { + TH_RST|TH_ACK, 0, TH_SYN, TH_SYN|TH_ACK, + TH_ACK, TH_ACK, TH_FIN|TH_ACK, TH_FIN|TH_ACK, + TH_FIN|TH_ACK, TH_ACK, TH_ACK, +}; + + +#define MAX_TCPOPTLEN 32 /* max # bytes that go in options */ + +/* + * Tcp output routine: figure out what should be sent and send it. + */ +int +tcp_output(tp) + struct tcpcb *tp; +{ + struct SLIRPsocket *so = tp->t_socket; + register long len, win; + int off, flags, error; + struct SLIRPmbuf *m; + struct tcpiphdr *ti; + u_char opt[MAX_TCPOPTLEN]; + unsigned optlen, hdrlen; + int idle, sendalot; + + DEBUG_CALL("tcp_output"); + DEBUG_ARG("tp = %lx", (long )tp); + + /* + * Determine length of data that should be transmitted, + * and flags that will be used. + * If there is some data or critical controls (SYN, RST) + * to send, then transmit; otherwise, investigate further. + */ + idle = (tp->snd_max == tp->snd_una); + if (idle && tp->t_idle >= tp->t_rxtcur) + /* + * We have been idle for "a while" and no acks are + * expected to clock out any data we send -- + * slow start to get ack "clock" running again. + */ + tp->snd_cwnd = tp->t_maxseg; +again: + sendalot = 0; + off = tp->snd_nxt - tp->snd_una; + win = min(tp->snd_wnd, tp->snd_cwnd); + + flags = tcp_outflags[tp->t_state]; + + DEBUG_MISC((dfd, " --- tcp_output flags = 0x%x\n",flags)); + + /* + * If in persist timeout with window of 0, send 1 byte. + * Otherwise, if window is small but nonzero + * and timer expired, we will send what we can + * and go to transmit state. + */ + if (tp->t_force) { + if (win == 0) { + /* + * If we still have some data to send, then + * clear the FIN bit. Usually this would + * happen below when it realizes that we + * aren't sending all the data. However, + * if we have exactly 1 byte of unset data, + * then it won't clear the FIN bit below, + * and if we are in persist state, we wind + * up sending the packet without recording + * that we sent the FIN bit. + * + * We can't just blindly clear the FIN bit, + * because if we don't have any more data + * to send then the probe will be the FIN + * itself. + */ + if (off < so->so_snd.sb_cc) + flags &= ~TH_FIN; + win = 1; + } else { + tp->t_timer[TCPT_PERSIST] = 0; + tp->t_rxtshift = 0; + } + } + + len = min(so->so_snd.sb_cc, win) - off; + + if (len < 0) { + /* + * If FIN has been sent but not acked, + * but we haven't been called to retransmit, + * len will be -1. Otherwise, window shrank + * after we sent into it. If window shrank to 0, + * cancel pending retransmit and pull snd_nxt + * back to (closed) window. We will enter persist + * state below. If the window didn't close completely, + * just wait for an ACK. + */ + len = 0; + if (win == 0) { + tp->t_timer[TCPT_REXMT] = 0; + tp->snd_nxt = tp->snd_una; + } + } + + if (len > tp->t_maxseg) { + len = tp->t_maxseg; + sendalot = 1; + } + if (SEQ_LT(tp->snd_nxt + len, tp->snd_una + so->so_snd.sb_cc)) + flags &= ~TH_FIN; + + win = sbspace(&so->so_rcv); + + /* + * Sender silly window avoidance. If connection is idle + * and can send all data, a maximum segment, + * at least a maximum default-size segment do it, + * or are forced, do it; otherwise don't bother. + * If peer's buffer is tiny, then send + * when window is at least half open. + * If retransmitting (possibly after persist timer forced us + * to send into a small window), then must resend. + */ + if (len) { + if (len == tp->t_maxseg) + goto send; + if ((1 || idle || tp->t_flags & TF_NODELAY) && + len + off >= so->so_snd.sb_cc) + goto send; + if (tp->t_force) + goto send; + if (len >= tp->max_sndwnd / 2 && tp->max_sndwnd > 0) + goto send; + if (SEQ_LT(tp->snd_nxt, tp->snd_max)) + goto send; + } + + /* + * Compare available window to amount of window + * known to peer (as advertised window less + * next expected input). If the difference is at least two + * max size segments, or at least 50% of the maximum possible + * window, then want to send a window update to peer. + */ + if (win > 0) { + /* + * "adv" is the amount we can increase the window, + * taking into account that we are limited by + * TCP_MAXWIN << tp->rcv_scale. + */ + long adv = min(win, (long)TCP_MAXWIN << tp->rcv_scale) - + (tp->rcv_adv - tp->rcv_nxt); + + if (adv >= (long) (2 * tp->t_maxseg)) + goto send; + if (2 * adv >= (long) so->so_rcv.sb_datalen) + goto send; + } + + /* + * Send if we owe peer an ACK. + */ + if (tp->t_flags & TF_ACKNOW) + goto send; + if (flags & (TH_SYN|TH_RST)) + goto send; + if (SEQ_GT(tp->snd_up, tp->snd_una)) + goto send; + /* + * If our state indicates that FIN should be sent + * and we have not yet done so, or we're retransmitting the FIN, + * then we need to send. + */ + if (flags & TH_FIN && + ((tp->t_flags & TF_SENTFIN) == 0 || tp->snd_nxt == tp->snd_una)) + goto send; + + /* + * TCP window updates are not reliable, rather a polling protocol + * using ``persist'' packets is used to insure receipt of window + * updates. The three ``states'' for the output side are: + * idle not doing retransmits or persists + * persisting to move a small or zero window + * (re)transmitting and thereby not persisting + * + * tp->t_timer[TCPT_PERSIST] + * is set when we are in persist state. + * tp->t_force + * is set when we are called to send a persist packet. + * tp->t_timer[TCPT_REXMT] + * is set when we are retransmitting + * The output side is idle when both timers are zero. + * + * If send window is too small, there is data to transmit, and no + * retransmit or persist is pending, then go to persist state. + * If nothing happens soon, send when timer expires: + * if window is nonzero, transmit what we can, + * otherwise force out a byte. + */ + if (so->so_snd.sb_cc && tp->t_timer[TCPT_REXMT] == 0 && + tp->t_timer[TCPT_PERSIST] == 0) { + tp->t_rxtshift = 0; + tcp_setpersist(tp); + } + + /* + * No reason to send a segment, just return. + */ + tcpstat.tcps_didnuttin++; + + return (0); + +send: + /* + * Before ESTABLISHED, force sending of initial options + * unless TCP set not to do any options. + * NOTE: we assume that the IP/TCP header plus TCP options + * always fit in a single SLIRPmbuf, leaving room for a maximum + * link header, i.e. + * max_linkhdr + sizeof (struct tcpiphdr) + optlen <= MHLEN + */ + optlen = 0; + hdrlen = sizeof (struct tcpiphdr); + if (flags & TH_SYN) { + tp->snd_nxt = tp->iss; + if ((tp->t_flags & TF_NOOPT) == 0) { + u_int16_t mss; + + opt[0] = TCPOPT_MAXSEG; + opt[1] = 4; + mss = htons((u_int16_t) tcp_mss(tp, 0)); + memcpy((SLIRPcaddr_t)(opt + 2), (SLIRPcaddr_t)&mss, sizeof(mss)); + optlen = 4; + +/* if ((tp->t_flags & TF_REQ_SCALE) && + * ((flags & TH_ACK) == 0 || + * (tp->t_flags & TF_RCVD_SCALE))) { + * *((u_int32_t *) (opt + optlen)) = htonl( + * TCPOPT_NOP << 24 | + * TCPOPT_WINDOW << 16 | + * TCPOLEN_WINDOW << 8 | + * tp->request_r_scale); + * optlen += 4; + * } + */ + } + } + + /* + * Send a timestamp and echo-reply if this is a SYN and our side + * wants to use timestamps (TF_REQ_TSTMP is set) or both our side + * and our peer have sent timestamps in our SYN's. + */ +/* if ((tp->t_flags & (TF_REQ_TSTMP|TF_NOOPT)) == TF_REQ_TSTMP && + * (flags & TH_RST) == 0 && + * ((flags & (TH_SYN|TH_ACK)) == TH_SYN || + * (tp->t_flags & TF_RCVD_TSTMP))) { + * u_int32_t *lp = (u_int32_t *)(opt + optlen); + * + * / * Form timestamp option as shown in appendix A of RFC 1323. * / + * *lp++ = htonl(TCPOPT_TSTAMP_HDR); + * *lp++ = htonl(tcp_now); + * *lp = htonl(tp->ts_recent); + * optlen += TCPOLEN_TSTAMP_APPA; + * } + */ + hdrlen += optlen; + + /* + * Adjust data length if insertion of options will + * bump the packet length beyond the t_maxseg length. + */ + if (len > tp->t_maxseg - optlen) { + len = tp->t_maxseg - optlen; + sendalot = 1; + } + + /* + * Grab a header SLIRPmbuf, attaching a copy of data to + * be transmitted, and initialize the header from + * the template for sends on this connection. + */ + if (len) { + if (tp->t_force && len == 1) + tcpstat.tcps_sndprobe++; + else if (SEQ_LT(tp->snd_nxt, tp->snd_max)) { + tcpstat.tcps_sndrexmitpack++; + tcpstat.tcps_sndrexmitbyte += len; + } else { + tcpstat.tcps_sndpack++; + tcpstat.tcps_sndbyte += len; + } + + m = m_get(); + if (m == NULL) { +/* error = ENOBUFS; */ + error = 1; + goto out; + } + m->m_data += if_maxlinkhdr; + m->m_len = hdrlen; + + /* + * This will always succeed, since we make sure our SLIRPmbufs + * are big enough to hold one MSS packet + header + ... etc. + */ +/* if (len <= MHLEN - hdrlen - max_linkhdr) { */ + + sbcopy(&so->so_snd, off, (int) len, mtod(m, SLIRPcaddr_t) + hdrlen); + m->m_len += len; + +/* } else { + * m->m_next = m_copy(so->so_snd.sb_mb, off, (int) len); + * if (m->m_next == 0) + * len = 0; + * } + */ + /* + * If we're sending everything we've got, set PUSH. + * (This will keep happy those implementations which only + * give data to the user when a buffer fills or + * a PUSH comes in.) + */ + if (off + len == so->so_snd.sb_cc) + flags |= TH_PUSH; + } else { + if (tp->t_flags & TF_ACKNOW) + tcpstat.tcps_sndacks++; + else if (flags & (TH_SYN|TH_FIN|TH_RST)) + tcpstat.tcps_sndctrl++; + else if (SEQ_GT(tp->snd_up, tp->snd_una)) + tcpstat.tcps_sndurg++; + else + tcpstat.tcps_sndwinup++; + + m = m_get(); + if (m == NULL) { +/* error = ENOBUFS; */ + error = 1; + goto out; + } + m->m_data += if_maxlinkhdr; + m->m_len = hdrlen; + } + + ti = mtod(m, struct tcpiphdr *); + + memcpy((SLIRPcaddr_t)ti, &tp->t_template, sizeof (struct tcpiphdr)); + + /* + * Fill in fields, remembering maximum advertised + * window for use in delaying messages about window sizes. + * If resending a FIN, be sure not to use a new sequence number. + */ + if (flags & TH_FIN && tp->t_flags & TF_SENTFIN && + tp->snd_nxt == tp->snd_max) + tp->snd_nxt--; + /* + * If we are doing retransmissions, then snd_nxt will + * not reflect the first unsent octet. For ACK only + * packets, we do not want the sequence number of the + * retransmitted packet, we want the sequence number + * of the next unsent octet. So, if there is no data + * (and no SYN or FIN), use snd_max instead of snd_nxt + * when filling in ti_seq. But if we are in persist + * state, snd_max might reflect one byte beyond the + * right edge of the window, so use snd_nxt in that + * case, since we know we aren't doing a retransmission. + * (retransmit and persist are mutually exclusive...) + */ + if (len || (flags & (TH_SYN|TH_FIN)) || tp->t_timer[TCPT_PERSIST]) + ti->ti_seq = htonl(tp->snd_nxt); + else + ti->ti_seq = htonl(tp->snd_max); + ti->ti_ack = htonl(tp->rcv_nxt); + if (optlen) { + memcpy((SLIRPcaddr_t)(ti + 1), (SLIRPcaddr_t)opt, optlen); + ti->ti_off = (sizeof (struct tcphdr) + optlen) >> 2; + } + ti->ti_flags = flags; + /* + * Calculate receive window. Don't shrink window, + * but avoid silly window syndrome. + */ + if (win < (long)(so->so_rcv.sb_datalen / 4) && win < (long)tp->t_maxseg) + win = 0; + if (win > (long)TCP_MAXWIN << tp->rcv_scale) + win = (long)TCP_MAXWIN << tp->rcv_scale; + if (win < (long)(tp->rcv_adv - tp->rcv_nxt)) + win = (long)(tp->rcv_adv - tp->rcv_nxt); + ti->ti_win = htons((u_int16_t) (win>>tp->rcv_scale)); + + if (SEQ_GT(tp->snd_up, tp->snd_una)) { + ti->ti_urp = htons((u_int16_t)(tp->snd_up - ntohl(ti->ti_seq))); +#ifdef notdef + if (SEQ_GT(tp->snd_up, tp->snd_nxt)) { + ti->ti_urp = htons((u_int16_t)(tp->snd_up - tp->snd_nxt)); +#endif + ti->ti_flags |= TH_URG; + } else + /* + * If no urgent pointer to send, then we pull + * the urgent pointer to the left edge of the send window + * so that it doesn't drift into the send window on sequence + * number wraparound. + */ + tp->snd_up = tp->snd_una; /* drag it along */ + + /* + * Put TCP length in extended header, and then + * checksum extended header and data. + */ + if (len + optlen) + ti->ti_len = htons((u_int16_t)(sizeof (struct tcphdr) + + optlen + len)); + ti->ti_sum = cksum(m, (int)(hdrlen + len)); + + /* + * In transmit state, time the transmission and arrange for + * the retransmit. In persist state, just set snd_max. + */ + if (tp->t_force == 0 || tp->t_timer[TCPT_PERSIST] == 0) { + tcp_seq startseq = tp->snd_nxt; + + /* + * Advance snd_nxt over sequence space of this segment. + */ + if (flags & (TH_SYN|TH_FIN)) { + if (flags & TH_SYN) + tp->snd_nxt++; + if (flags & TH_FIN) { + tp->snd_nxt++; + tp->t_flags |= TF_SENTFIN; + } + } + tp->snd_nxt += len; + if (SEQ_GT(tp->snd_nxt, tp->snd_max)) { + tp->snd_max = tp->snd_nxt; + /* + * Time this transmission if not a retransmission and + * not currently timing anything. + */ + if (tp->t_rtt == 0) { + tp->t_rtt = 1; + tp->t_rtseq = startseq; + tcpstat.tcps_segstimed++; + } + } + + /* + * Set retransmit timer if not currently set, + * and not doing an ack or a keep-alive probe. + * Initial value for retransmit timer is smoothed + * round-trip time + 2 * round-trip time variance. + * Initialize shift counter which is used for backoff + * of retransmit time. + */ + if (tp->t_timer[TCPT_REXMT] == 0 && + tp->snd_nxt != tp->snd_una) { + tp->t_timer[TCPT_REXMT] = tp->t_rxtcur; + if (tp->t_timer[TCPT_PERSIST]) { + tp->t_timer[TCPT_PERSIST] = 0; + tp->t_rxtshift = 0; + } + } + } else + if (SEQ_GT(tp->snd_nxt + len, tp->snd_max)) + tp->snd_max = tp->snd_nxt + len; + + /* + * Fill in IP length and desired time to live and + * send to IP level. There should be a better way + * to handle ttl and tos; we could keep them in + * the template, but need a way to checksum without them. + */ + m->m_len = hdrlen + len; /* XXX Needed? m_len should be correct */ + + { + + ((struct ip *)ti)->ip_len = m->m_len; + + ((struct ip *)ti)->ip_ttl = ip_defttl; + ((struct ip *)ti)->ip_tos = so->so_iptos; + +/* #if BSD >= 43 */ + /* Don't do IP options... */ +/* error = ip_output(m, tp->t_inpcb->inp_options, &tp->t_inpcb->inp_route, + * so->so_options & SO_DONTROUTE, 0); + */ + error = ip_output(so, m); + +/* #else + * error = ip_output(m, (struct SLIRPmbuf *)0, &tp->t_inpcb->inp_route, + * so->so_options & SO_DONTROUTE); + * #endif + */ + } + if (error) { +out: +/* if (error == ENOBUFS) { + * tcp_quench(tp->t_inpcb, 0); + * return (0); + * } + */ +/* if ((error == EHOSTUNREACH || error == ENETDOWN) + * && TCPS_HAVERCVDSYN(tp->t_state)) { + * tp->t_softerror = error; + * return (0); + * } + */ + return (error); + } + tcpstat.tcps_sndtotal++; + + /* + * Data sent (as far as we can tell). + * If this advertises a larger window than any other segment, + * then remember the size of the advertised window. + * Any pending ACK has now been sent. + */ + if (win > 0 && SEQ_GT(tp->rcv_nxt+win, tp->rcv_adv)) + tp->rcv_adv = tp->rcv_nxt + win; + tp->last_ack_sent = tp->rcv_nxt; + tp->t_flags &= ~(TF_ACKNOW|TF_DELACK); + if (sendalot) + goto again; + + return (0); +} + +void +tcp_setpersist(tp) + struct tcpcb *tp; +{ + int t = ((tp->t_srtt >> 2) + tp->t_rttvar) >> 1; + +/* if (tp->t_timer[TCPT_REXMT]) + * panic("tcp_output REXMT"); + */ + /* + * Start/restart persistence timer. + */ + TCPT_RANGESET(tp->t_timer[TCPT_PERSIST], + t * tcp_backoff[tp->t_rxtshift], + TCPTV_PERSMIN, TCPTV_PERSMAX); + if (tp->t_rxtshift < TCP_MAXRXTSHIFT) + tp->t_rxtshift++; +} diff --git a/src/slirp/tcp_subr.c b/src/slirp/tcp_subr.c new file mode 100644 index 0000000..f275244 --- /dev/null +++ b/src/slirp/tcp_subr.c @@ -0,0 +1,1324 @@ +/* + * Copyright (c) 1982, 1986, 1988, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)tcp_subr.c 8.1 (Berkeley) 6/10/93 + * tcp_subr.c,v 1.5 1994/10/08 22:39:58 phk Exp + */ + +/* + * Changes and additions relating to SLiRP + * Copyright (c) 1995 Danny Gasparovski. + * + * Please read the file COPYRIGHT for the + * terms and conditions of the copyright. + */ + +#define WANT_SYS_IOCTL_H +#include +#include "slirp.h" + +/* patchable/settable parameters for tcp */ +int tcp_mssdflt = TCP_MSS; +int tcp_rttdflt = TCPTV_SRTTDFLT / PR_SLOWHZ; +int tcp_do_rfc1323 = 0; /* Don't do rfc1323 performance enhancements */ +int tcp_rcvspace; /* You may want to change this */ +int tcp_sndspace; /* Keep small if you have an error prone link */ + +/* + * Tcp initialization + */ +void +tcp_init() +{ + tcp_iss = 1; /* wrong */ + tcb.so_next = tcb.so_prev = &tcb; + + /* tcp_rcvspace = our Window we advertise to the remote */ + tcp_rcvspace = TCP_RCVSPACE; + tcp_sndspace = TCP_SNDSPACE; + + /* Make sure tcp_sndspace is at least 2*MSS */ + if (tcp_sndspace < 2*(min(if_mtu, if_mru) - sizeof(struct tcpiphdr))) + tcp_sndspace = 2*(min(if_mtu, if_mru) - sizeof(struct tcpiphdr)); +} + +/* + * Create template to be used to send tcp packets on a connection. + * Call after host entry created, fills + * in a skeletal tcp/ip header, minimizing the amount of work + * necessary when the connection is used. + */ +/* struct tcpiphdr * */ +void +tcp_template(tp) + struct tcpcb *tp; +{ + struct SLIRPsocket *so = tp->t_socket; + struct tcpiphdr *n = &tp->t_template; + + n->ti_next = n->ti_prev = 0; + n->ti_x1 = 0; + n->ti_pr = IPPROTO_TCP; + n->ti_len = htons(sizeof (struct tcpiphdr) - sizeof (struct ip)); + n->ti_src = so->so_faddr; + n->ti_dst = so->so_laddr; + n->ti_sport = so->so_fport; + n->ti_dport = so->so_lport; + + n->ti_seq = 0; + n->ti_ack = 0; + n->ti_x2 = 0; + n->ti_off = 5; + n->ti_flags = 0; + n->ti_win = 0; + n->ti_sum = 0; + n->ti_urp = 0; +} + +/* + * Send a single message to the TCP at address specified by + * the given TCP/IP header. If m == 0, then we make a copy + * of the tcpiphdr at ti and send directly to the addressed host. + * This is used to force keep alive messages out using the TCP + * template for a connection tp->t_template. If flags are given + * then we send a message back to the TCP which originated the + * segment ti, and discard the SLIRPmbuf containing it and any other + * attached SLIRPmbufs. + * + * In any case the ack and sequence number of the transmitted + * segment are as specified by the parameters. + */ +void +tcp_respond(tp, ti, m, ack, seq, flags) + struct tcpcb *tp; + struct tcpiphdr *ti; + struct SLIRPmbuf *m; + tcp_seq ack, seq; + int flags; +{ + register int tlen; + int win = 0; + + DEBUG_CALL("tcp_respond"); + DEBUG_ARG("tp = %lx", (long)tp); + DEBUG_ARG("ti = %lx", (long)ti); + DEBUG_ARG("m = %lx", (long)m); + DEBUG_ARG("ack = %u", ack); + DEBUG_ARG("seq = %u", seq); + DEBUG_ARG("flags = %x", flags); + + if (tp) + win = sbspace(&tp->t_socket->so_rcv); + if (m == 0) { + if ((m = m_get()) == NULL) + return; +#ifdef TCP_COMPAT_42 + tlen = 1; +#else + tlen = 0; +#endif + m->m_data += if_maxlinkhdr; + *mtod(m, struct tcpiphdr *) = *ti; + ti = mtod(m, struct tcpiphdr *); + flags = TH_ACK; + } else { + /* + * ti points into m so the next line is just making + * the SLIRPmbuf point to ti + */ + m->m_data = (SLIRPcaddr_t)ti; + + m->m_len = sizeof (struct tcpiphdr); + tlen = 0; +#define xchg(a,b,type) { type t; t=a; a=b; b=t; } + xchg(ti->ti_dst.s_addr, ti->ti_src.s_addr, u_int32_t); + xchg(ti->ti_dport, ti->ti_sport, u_int16_t); +#undef xchg + } + ti->ti_len = htons((u_short)(sizeof (struct tcphdr) + tlen)); + tlen += sizeof (struct tcpiphdr); + m->m_len = tlen; + + ti->ti_next = ti->ti_prev = 0; + ti->ti_x1 = 0; + ti->ti_seq = htonl(seq); + ti->ti_ack = htonl(ack); + ti->ti_x2 = 0; + ti->ti_off = sizeof (struct tcphdr) >> 2; + ti->ti_flags = flags; + if (tp) + ti->ti_win = htons((u_int16_t) (win >> tp->rcv_scale)); + else + ti->ti_win = htons((u_int16_t)win); + ti->ti_urp = 0; + ti->ti_sum = 0; + ti->ti_sum = cksum(m, tlen); + ((struct ip *)ti)->ip_len = tlen; + + if(flags & TH_RST) + ((struct ip *)ti)->ip_ttl = MAXTTL; + else + ((struct ip *)ti)->ip_ttl = ip_defttl; + + (void) ip_output((struct SLIRPsocket *)0, m); +} + +/* + * Create a new TCP control block, making an + * empty reassembly queue and hooking it to the argument + * protocol control block. + */ +struct tcpcb * +tcp_newtcpcb(so) + struct SLIRPsocket *so; +{ + struct tcpcb *tp; + + tp = (struct tcpcb *)malloc(sizeof(*tp)); + if (tp == NULL) + return ((struct tcpcb *)0); + + memset((char *) tp, 0, sizeof(struct tcpcb)); + tp->seg_next = tp->seg_prev = (tcpiphdrp_32)tp; + tp->t_maxseg = tcp_mssdflt; + + tp->t_flags = tcp_do_rfc1323 ? (TF_REQ_SCALE|TF_REQ_TSTMP) : 0; + tp->t_socket = so; + + /* + * Init srtt to TCPTV_SRTTBASE (0), so we can tell that we have no + * rtt estimate. Set rttvar so that srtt + 2 * rttvar gives + * reasonable initial retransmit time. + */ + tp->t_srtt = TCPTV_SRTTBASE; + tp->t_rttvar = tcp_rttdflt * PR_SLOWHZ << 2; + tp->t_rttmin = TCPTV_MIN; + + TCPT_RANGESET(tp->t_rxtcur, + ((TCPTV_SRTTBASE >> 2) + (TCPTV_SRTTDFLT << 2)) >> 1, + TCPTV_MIN, TCPTV_REXMTMAX); + + tp->snd_cwnd = TCP_MAXWIN << TCP_MAX_WINSHIFT; + tp->snd_ssthresh = TCP_MAXWIN << TCP_MAX_WINSHIFT; + tp->t_state = TCPS_CLOSED; + + so->so_tcpcb = tp; + + return (tp); +} + +/* + * Drop a TCP connection, reporting + * the specified error. If connection is synchronized, + * then send a RST to peer. + */ +struct tcpcb *tcp_drop(struct tcpcb *tp, int err) +{ +/* tcp_drop(tp, errno) + struct tcpcb *tp; + int errno; +{ +*/ + + DEBUG_CALL("tcp_drop"); + DEBUG_ARG("tp = %lx", (long)tp); + DEBUG_ARG("errno = %d", errno); + + if (TCPS_HAVERCVDSYN(tp->t_state)) { + tp->t_state = TCPS_CLOSED; + (void) tcp_output(tp); + tcpstat.tcps_drops++; + } else + tcpstat.tcps_conndrops++; +/* if (errno == ETIMEDOUT && tp->t_softerror) + * errno = tp->t_softerror; + */ +/* so->so_error = errno; */ + return (tcp_close(tp)); +} + +/* + * Close a TCP control block: + * discard all space held by the tcp + * discard internet protocol block + * wake up any sleepers + */ +struct tcpcb * +tcp_close(tp) + struct tcpcb *tp; +{ + struct tcpiphdr *t; + struct SLIRPsocket *so = tp->t_socket; + struct SLIRPmbuf *m; + + DEBUG_CALL("tcp_close"); + DEBUG_ARG("tp = %lx", (long )tp); + + /* free the reassembly queue, if any */ + t = (struct tcpiphdr *) tp->seg_next; + while (t != (struct tcpiphdr *)tp) { + t = (struct tcpiphdr *)t->ti_next; + m = (struct SLIRPmbuf *) REASS_MBUF((struct tcpiphdr *)t->ti_prev); + remque_32((struct tcpiphdr *) t->ti_prev); + m_freem(m); + } + /* It's static */ +/* if (tp->t_template) + * (void) m_free(dtom(tp->t_template)); + */ +/* free(tp, M_PCB); */ + free(tp); + so->so_tcpcb = 0; + soisfdisconnected(so); + /* clobber input socket cache if we're closing the cached connection */ + if (so == tcp_last_so) + tcp_last_so = &tcb; + closesocket(so->s); + sbfree(&so->so_rcv); + sbfree(&so->so_snd); + sofree(so); + tcpstat.tcps_closed++; + return ((struct tcpcb *)0); +} + +void +tcp_drain() +{ + /* XXX */ +} + +/* + * When a source quench is received, close congestion window + * to one segment. We will gradually open it again as we proceed. + */ + +#ifdef notdef + +void +tcp_quench(i, errno) + + int errno; +{ + struct tcpcb *tp = intotcpcb(inp); + + if (tp) + tp->snd_cwnd = tp->t_maxseg; +} + +#endif /* notdef */ + +/* + * TCP protocol interface to socket abstraction. + */ + +/* + * User issued close, and wish to trail through shutdown states: + * if never received SYN, just forget it. If got a SYN from peer, + * but haven't sent FIN, then go to FIN_WAIT_1 state to send peer a FIN. + * If already got a FIN from peer, then almost done; go to LAST_ACK + * state. In all other cases, have already sent FIN to peer (e.g. + * after PRU_SHUTDOWN), and just have to play tedious game waiting + * for peer to send FIN or not respond to keep-alives, etc. + * We can let the user exit from the close as soon as the FIN is acked. + */ +void +tcp_sockclosed(tp) + struct tcpcb *tp; +{ + + DEBUG_CALL("tcp_sockclosed"); + DEBUG_ARG("tp = %lx", (long)tp); + + switch (tp->t_state) { + + case TCPS_CLOSED: + case TCPS_LISTEN: + case TCPS_SYN_SENT: + tp->t_state = TCPS_CLOSED; + tp = tcp_close(tp); + break; + + case TCPS_SYN_RECEIVED: + case TCPS_ESTABLISHED: + tp->t_state = TCPS_FIN_WAIT_1; + break; + + case TCPS_CLOSE_WAIT: + tp->t_state = TCPS_LAST_ACK; + break; + } +/* soisfdisconnecting(tp->t_socket); */ + if (tp && tp->t_state >= TCPS_FIN_WAIT_2) + soisfdisconnected(tp->t_socket); + if (tp) + tcp_output(tp); +} + +/* + * Connect to a host on the Internet + * Called by tcp_input + * Only do a connect, the tcp fields will be set in tcp_input + * return 0 if there's a result of the connect, + * else return -1 means we're still connecting + * The return value is almost always -1 since the socket is + * nonblocking. Connect returns after the SYN is sent, and does + * not wait for ACK+SYN. + */ +int tcp_fconnect(so) + struct SLIRPsocket *so; +{ + int ret=0; + + DEBUG_CALL("tcp_fconnect"); + DEBUG_ARG("so = %lx", (long )so); + + if( (ret=so->s=socket(AF_INET,SOCK_STREAM,0)) >= 0) { + int opt, s=so->s; + struct sockaddr_in addr; + memset(&addr, 0, sizeof(struct sockaddr_in)); + + fd_nonblock(s); + opt = 1; + setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(char *)&opt,sizeof(opt )); + opt = 1; + setsockopt(s,SOL_SOCKET,SO_OOBINLINE,(char *)&opt,sizeof(opt )); + + addr.sin_family = AF_INET; + if ((so->so_faddr.s_addr & htonl(0xffffff00)) == special_addr.s_addr) { + /* It's an alias */ + switch(ntohl(so->so_faddr.s_addr) & 0xff) { + case CTL_DNS: + addr.sin_addr = dns_addr; + break; + case CTL_ALIAS: + default: + addr.sin_addr = loopback_addr; + break; + } + } else + addr.sin_addr = so->so_faddr; + addr.sin_port = so->so_fport; + + DEBUG_MISC((dfd, " connect()ing, addr.sin_port=%d, " + "addr.sin_addr.s_addr=%.16s\n", + ntohs(addr.sin_port), inet_ntoa(addr.sin_addr))); + /* We don't care what port we get */ + ret = connect(s,(struct sockaddr *)&addr,sizeof (addr)); + + /* + * If it's not in progress, it failed, so we just return 0, + * without clearing SS_NOFDREF + */ + soisfconnecting(so); + } + + return(ret); +} + +/* + * Accept the socket and connect to the local-host + * + * We have a problem. The correct thing to do would be + * to first connect to the local-host, and only if the + * connection is accepted, then do an accept() here. + * But, a) we need to know who's trying to connect + * to the socket to be able to SYN the local-host, and + * b) we are already connected to the foreign host by + * the time it gets to accept(), so... We simply accept + * here and SYN the local-host. + */ +void +tcp_connect(inso) + struct SLIRPsocket *inso; +{ + struct SLIRPsocket *so; + struct sockaddr_in addr; + socklen_t addrlen = sizeof(struct sockaddr_in); + struct tcpcb *tp; + int s, opt; + + DEBUG_CALL("tcp_connect"); + DEBUG_ARG("inso = %lx", (long)inso); + + /* + * If it's an SS_ACCEPTONCE socket, no need to socreate() + * another socket, just use the accept() socket. + */ + if (inso->so_state & SS_FACCEPTONCE) { + /* FACCEPTONCE already have a tcpcb */ + so = inso; + } else { + if ((so = socreate()) == NULL) { + /* If it failed, get rid of the pending connection */ + closesocket(accept(inso->s,(struct sockaddr *)&addr,&addrlen)); + return; + } + if (tcp_attach(so) < 0) { + free(so); /* NOT sofree */ + return; + } + so->so_laddr = inso->so_laddr; + so->so_lport = inso->so_lport; + } + + (void) tcp_mss(sototcpcb(so), 0); + + if ((s = accept(inso->s,(struct sockaddr *)&addr,&addrlen)) < 0) { + tcp_close(sototcpcb(so)); /* This will sofree() as well */ + return; + } + fd_nonblock(s); + opt = 1; + setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(char *)&opt,sizeof(int)); + opt = 1; + setsockopt(s,SOL_SOCKET,SO_OOBINLINE,(char *)&opt,sizeof(int)); + opt = 1; + setsockopt(s,IPPROTO_TCP,TCP_NODELAY,(char *)&opt,sizeof(int)); + + so->so_fport = addr.sin_port; + so->so_faddr = addr.sin_addr; + /* Translate connections from localhost to the real hostname */ + if (so->so_faddr.s_addr == 0 || so->so_faddr.s_addr == loopback_addr.s_addr) + so->so_faddr = alias_addr; + + /* Close the accept() socket, set right state */ + if (inso->so_state & SS_FACCEPTONCE) { + closesocket(so->s); /* If we only accept once, close the accept() socket */ + so->so_state = SS_NOFDREF; /* Don't select it yet, even though we have an FD */ + /* if it's not FACCEPTONCE, it's already NOFDREF */ + } + so->s = s; + + so->so_iptos = tcp_tos(so); + tp = sototcpcb(so); + + tcp_template(tp); + + /* Compute window scaling to request. */ +/* while (tp->request_r_scale < TCP_MAX_WINSHIFT && + * (TCP_MAXWIN << tp->request_r_scale) < so->so_rcv.sb_hiwat) + * tp->request_r_scale++; + */ + +/* soisconnecting(so); */ /* NOFDREF used instead */ + tcpstat.tcps_connattempt++; + + tp->t_state = TCPS_SYN_SENT; + tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT; + tp->iss = tcp_iss; + tcp_iss += TCP_ISSINCR/2; + tcp_sendseqinit(tp); + tcp_output(tp); +} + +/* + * Attach a TCPCB to a socket. + */ +int +tcp_attach(so) + struct SLIRPsocket *so; +{ + if ((so->so_tcpcb = tcp_newtcpcb(so)) == NULL) + return -1; + + insque(so, &tcb); + + return 0; +} + +/* + * Set the socket's type of service field + */ +struct tos_t tcptos[] = { + {0, 20, IPTOS_THROUGHPUT, 0}, /* ftp data */ + {21, 21, IPTOS_LOWDELAY, EMU_FTP}, /* ftp control */ + {0, 23, IPTOS_LOWDELAY, 0}, /* telnet */ + {0, 80, IPTOS_THROUGHPUT, 0}, /* WWW */ + {0, 513, IPTOS_LOWDELAY, EMU_RLOGIN|EMU_NOCONNECT}, /* rlogin */ + {0, 514, IPTOS_LOWDELAY, EMU_RSH|EMU_NOCONNECT}, /* shell */ + {0, 544, IPTOS_LOWDELAY, EMU_KSH}, /* kshell */ + {0, 543, IPTOS_LOWDELAY, 0}, /* klogin */ + {0, 6667, IPTOS_THROUGHPUT, EMU_IRC}, /* IRC */ + {0, 6668, IPTOS_THROUGHPUT, EMU_IRC}, /* IRC undernet */ + {0, 7070, IPTOS_LOWDELAY, EMU_REALAUDIO }, /* RealAudio control */ + {0, 113, IPTOS_LOWDELAY, EMU_IDENT }, /* identd protocol */ + {0, 0, 0, 0} +}; + +struct emu_t *tcpemu = 0; + +/* + * Return TOS according to the above table + */ +u_int8_t +tcp_tos(so) + struct SLIRPsocket *so; +{ + int i = 0; + struct emu_t *emup; + + while(tcptos[i].tos) { + if ((tcptos[i].fport && (ntohs(so->so_fport) == tcptos[i].fport)) || + (tcptos[i].lport && (ntohs(so->so_lport) == tcptos[i].lport))) { + so->so_emu = tcptos[i].emu; + return tcptos[i].tos; + } + i++; + } + + /* Nope, lets see if there's a user-added one */ + for (emup = tcpemu; emup; emup = emup->next) { + if ((emup->fport && (ntohs(so->so_fport) == emup->fport)) || + (emup->lport && (ntohs(so->so_lport) == emup->lport))) { + so->so_emu = emup->emu; + return emup->tos; + } + } + + return 0; +} + +int do_echo = -1; + +/* + * Emulate programs that try and connect to us + * This includes ftp (the data connection is + * initiated by the server) and IRC (DCC CHAT and + * DCC SEND) for now + * + * NOTE: It's possible to crash SLiRP by sending it + * unstandard strings to emulate... if this is a problem, + * more checks are needed here + * + * XXX Assumes the whole command came in one packet + * + * XXX Some ftp clients will have their TOS set to + * LOWDELAY and so Nagel will kick in. Because of this, + * we'll get the first letter, followed by the rest, so + * we simply scan for ORT instead of PORT... + * DCC doesn't have this problem because there's other stuff + * in the packet before the DCC command. + * + * Return 1 if the SLIRPmbuf m is still valid and should be + * sbappend()ed + * + * NOTE: if you return 0 you MUST m_free() the SLIRPmbuf! + */ +int +tcp_emu(so, m) + struct SLIRPsocket *so; + struct SLIRPmbuf *m; +{ + u_int n1, n2, n3, n4, n5, n6; + char buff[256]; + u_int32_t laddr; + u_int lport; + char *bptr; + + DEBUG_CALL("tcp_emu"); + DEBUG_ARG("so = %lx", (long)so); + DEBUG_ARG("m = %lx", (long)m); + + switch(so->so_emu) { + int x, i; + + case EMU_IDENT: + /* + * Identification protocol as per rfc-1413 + */ + + { + struct SLIRPsocket *tmpso; + struct sockaddr_in addr; + socklen_t addrlen = sizeof(struct sockaddr_in); + struct sbuf *so_rcv = &so->so_rcv; + + memcpy(so_rcv->sb_wptr, m->m_data, m->m_len); + so_rcv->sb_wptr += m->m_len; + so_rcv->sb_rptr += m->m_len; + m->m_data[m->m_len] = 0; /* NULL terminate */ + if (strchr(m->m_data, '\r') || strchr(m->m_data, '\n')) { + if (sscanf(so_rcv->sb_data, "%d%*[ ,]%d", &n1, &n2) == 2) { + HTONS(n1); + HTONS(n2); + /* n2 is the one on our host */ + for (tmpso = tcb.so_next; tmpso != &tcb; tmpso = tmpso->so_next) { + if (tmpso->so_laddr.s_addr == so->so_laddr.s_addr && + tmpso->so_lport == n2 && + tmpso->so_faddr.s_addr == so->so_faddr.s_addr && + tmpso->so_fport == n1) { + if (getsockname(tmpso->s, + (struct sockaddr *)&addr, &addrlen) == 0) + n2 = ntohs(addr.sin_port); + break; + } + } + } + so_rcv->sb_cc = sprintf(so_rcv->sb_data, "%d,%d\r\n", n1, n2); + so_rcv->sb_rptr = so_rcv->sb_data; + so_rcv->sb_wptr = so_rcv->sb_data + so_rcv->sb_cc; + } + m_free(m); + return 0; + } + +#if 0 + case EMU_RLOGIN: + /* + * Rlogin emulation + * First we accumulate all the initial option negotiation, + * then fork_exec() rlogin according to the options + */ + { + int i, i2, n; + char *ptr; + char args[100]; + char term[100]; + struct sbuf *so_snd = &so->so_snd; + struct sbuf *so_rcv = &so->so_rcv; + + /* First check if they have a priveladged port, or too much data has arrived */ + if (ntohs(so->so_lport) > 1023 || ntohs(so->so_lport) < 512 || + (m->m_len + so_rcv->sb_wptr) > (so_rcv->sb_data + so_rcv->sb_datalen)) { + memcpy(so_snd->sb_wptr, "Permission denied\n", 18); + so_snd->sb_wptr += 18; + so_snd->sb_cc += 18; + tcp_sockclosed(sototcpcb(so)); + m_free(m); + return 0; + } + + /* Append the current data */ + memcpy(so_rcv->sb_wptr, m->m_data, m->m_len); + so_rcv->sb_wptr += m->m_len; + so_rcv->sb_rptr += m->m_len; + m_free(m); + + /* + * Check if we have all the initial options, + * and build argument list to rlogin while we're here + */ + n = 0; + ptr = so_rcv->sb_data; + args[0] = 0; + term[0] = 0; + while (ptr < so_rcv->sb_wptr) { + if (*ptr++ == 0) { + n++; + if (n == 2) { + sprintf(args, "rlogin -l %s %s", + ptr, inet_ntoa(so->so_faddr)); + } else if (n == 3) { + i2 = so_rcv->sb_wptr - ptr; + for (i = 0; i < i2; i++) { + if (ptr[i] == '/') { + ptr[i] = 0; +#ifdef HAVE_SETENV + sprintf(term, "%s", ptr); +#else + sprintf(term, "TERM=%s", ptr); +#endif + ptr[i] = '/'; + break; + } + } + } + } + } + + if (n != 4) + return 0; + + /* We have it, set our term variable and fork_exec() */ +#ifdef HAVE_SETENV + setenv("TERM", term, 1); +#else + putenv(term); +#endif + fork_exec(so, args, 2); + term[0] = 0; + so->so_emu = 0; + + /* And finally, send the client a 0 character */ + so_snd->sb_wptr[0] = 0; + so_snd->sb_wptr++; + so_snd->sb_cc++; + + return 0; + } + + case EMU_RSH: + /* + * rsh emulation + * First we accumulate all the initial option negotiation, + * then rsh_exec() rsh according to the options + */ + { + int n; + char *ptr; + char *user; + char *args; + struct sbuf *so_snd = &so->so_snd; + struct sbuf *so_rcv = &so->so_rcv; + + /* First check if they have a priveladged port, or too much data has arrived */ + if (ntohs(so->so_lport) > 1023 || ntohs(so->so_lport) < 512 || + (m->m_len + so_rcv->sb_wptr) > (so_rcv->sb_data + so_rcv->sb_datalen)) { + memcpy(so_snd->sb_wptr, "Permission denied\n", 18); + so_snd->sb_wptr += 18; + so_snd->sb_cc += 18; + tcp_sockclosed(sototcpcb(so)); + m_free(m); + return 0; + } + + /* Append the current data */ + memcpy(so_rcv->sb_wptr, m->m_data, m->m_len); + so_rcv->sb_wptr += m->m_len; + so_rcv->sb_rptr += m->m_len; + m_free(m); + + /* + * Check if we have all the initial options, + * and build argument list to rlogin while we're here + */ + n = 0; + ptr = so_rcv->sb_data; + user=""; + args=""; + if (so->extra==NULL) { + struct SLIRPsocket *ns; + struct tcpcb* tp; + int port=atoi(ptr); + if (port <= 0) return 0; + if (port > 1023 || port < 512) { + memcpy(so_snd->sb_wptr, "Permission denied\n", 18); + so_snd->sb_wptr += 18; + so_snd->sb_cc += 18; + tcp_sockclosed(sototcpcb(so)); + return 0; + } + if ((ns=socreate()) == NULL) + return 0; + if (tcp_attach(ns)<0) { + free(ns); + return 0; + } + + ns->so_laddr=so->so_laddr; + ns->so_lport=htons(port); + + (void) tcp_mss(sototcpcb(ns), 0); + + ns->so_faddr=so->so_faddr; + ns->so_fport=htons(IPPORT_RESERVED-1); /* Use a fake port. */ + + if (ns->so_faddr.s_addr == 0 || + ns->so_faddr.s_addr == loopback_addr.s_addr) + ns->so_faddr = alias_addr; + + ns->so_iptos = tcp_tos(ns); + tp = sototcpcb(ns); + + tcp_template(tp); + + /* Compute window scaling to request. */ + /* while (tp->request_r_scale < TCP_MAX_WINSHIFT && + * (TCP_MAXWIN << tp->request_r_scale) < so->so_rcv.sb_hiwat) + * tp->request_r_scale++; + */ + + /*soisfconnecting(ns);*/ + + tcpstat.tcps_connattempt++; + + tp->t_state = TCPS_SYN_SENT; + tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT; + tp->iss = tcp_iss; + tcp_iss += TCP_ISSINCR/2; + tcp_sendseqinit(tp); + tcp_output(tp); + so->extra=ns; + } + while (ptr < so_rcv->sb_wptr) { + if (*ptr++ == 0) { + n++; + if (n == 2) { + user=ptr; + } else if (n == 3) { + args=ptr; + } + } + } + + if (n != 4) + return 0; + + rsh_exec(so,so->extra, user, inet_ntoa(so->so_faddr), args); + so->so_emu = 0; + so->extra=NULL; + + /* And finally, send the client a 0 character */ + so_snd->sb_wptr[0] = 0; + so_snd->sb_wptr++; + so_snd->sb_cc++; + + return 0; + } + + case EMU_CTL: + { + int num; + struct sbuf *so_snd = &so->so_snd; + struct sbuf *so_rcv = &so->so_rcv; + + /* + * If there is binary data here, we save it in so->so_m + */ + if (!so->so_m) { + int rxlen; + char *rxdata; + rxdata=mtod(m, char *); + for (rxlen=m->m_len; rxlen; rxlen--) { + if (*rxdata++ & 0x80) { + so->so_m = m; + return 0; + } + } + } /* if(so->so_m==NULL) */ + + /* + * Append the line + */ + sbappendsb(so_rcv, m); + + /* To avoid going over the edge of the buffer, we reset it */ + if (so_snd->sb_cc == 0) + so_snd->sb_wptr = so_snd->sb_rptr = so_snd->sb_data; + + /* + * A bit of a hack: + * If the first packet we get here is 1 byte long, then it + * was done in telnet character mode, therefore we must echo + * the characters as they come. Otherwise, we echo nothing, + * because in linemode, the line is already echoed + * XXX two or more control connections won't work + */ + if (do_echo == -1) { + if (m->m_len == 1) do_echo = 1; + else do_echo = 0; + } + if (do_echo) { + sbappendsb(so_snd, m); + m_free(m); + tcp_output(sototcpcb(so)); /* XXX */ + } else + m_free(m); + + num = 0; + while (num < so->so_rcv.sb_cc) { + if (*(so->so_rcv.sb_rptr + num) == '\n' || + *(so->so_rcv.sb_rptr + num) == '\r') { + int n; + + *(so_rcv->sb_rptr + num) = 0; + if (ctl_password && !ctl_password_ok) { + /* Need a password */ + if (sscanf(so_rcv->sb_rptr, "pass %256s", buff) == 1) { + if (strcmp(buff, ctl_password) == 0) { + ctl_password_ok = 1; + n = sprintf(so_snd->sb_wptr, + "Password OK.\r\n"); + goto do_prompt; + } + } + n = sprintf(so_snd->sb_wptr, + "Error: Password required, log on with \"pass PASSWORD\"\r\n"); + goto do_prompt; + } + cfg_quitting = 0; + n = do_config(so_rcv->sb_rptr, so, PRN_SPRINTF); + if (!cfg_quitting) { + /* Register the printed data */ +do_prompt: + so_snd->sb_cc += n; + so_snd->sb_wptr += n; + /* Add prompt */ + n = sprintf(so_snd->sb_wptr, "Slirp> "); + so_snd->sb_cc += n; + so_snd->sb_wptr += n; + } + /* Drop so_rcv data */ + so_rcv->sb_cc = 0; + so_rcv->sb_wptr = so_rcv->sb_rptr = so_rcv->sb_data; + tcp_output(sototcpcb(so)); /* Send the reply */ + } + num++; + } + return 0; + } +#endif + case EMU_FTP: /* ftp */ + *(m->m_data+m->m_len) = 0; /* NULL terminate for strstr */ + if ((bptr = (char *)strstr(m->m_data, "ORT")) != NULL) { + /* + * Need to emulate the PORT command + */ + x = sscanf(bptr, "ORT %d,%d,%d,%d,%d,%d\r\n%256[^\177]", + &n1, &n2, &n3, &n4, &n5, &n6, buff); + if (x < 6) + return 1; + + laddr = htonl((n1 << 24) | (n2 << 16) | (n3 << 8) | (n4)); + lport = htons((n5 << 8) | (n6)); + + if ((so = solisten(0, laddr, lport, SS_FACCEPTONCE)) == NULL) + return 1; + + n6 = ntohs(so->so_fport); + + n5 = (n6 >> 8) & 0xff; + n6 &= 0xff; + + laddr = ntohl(so->so_faddr.s_addr); + + n1 = ((laddr >> 24) & 0xff); + n2 = ((laddr >> 16) & 0xff); + n3 = ((laddr >> 8) & 0xff); + n4 = (laddr & 0xff); + + m->m_len = bptr - m->m_data; /* Adjust length */ + m->m_len += sprintf(bptr,"ORT %d,%d,%d,%d,%d,%d\r\n%s", + n1, n2, n3, n4, n5, n6, x==7?buff:""); + return 1; + } else if ((bptr = (char *)strstr(m->m_data, "27 Entering")) != NULL) { + /* + * Need to emulate the PASV response + */ + x = sscanf(bptr, "27 Entering Passive Mode (%d,%d,%d,%d,%d,%d)\r\n%256[^\177]", + &n1, &n2, &n3, &n4, &n5, &n6, buff); + if (x < 6) + return 1; + + laddr = htonl((n1 << 24) | (n2 << 16) | (n3 << 8) | (n4)); + lport = htons((n5 << 8) | (n6)); + + if ((so = solisten(0, laddr, lport, SS_FACCEPTONCE)) == NULL) + return 1; + + n6 = ntohs(so->so_fport); + + n5 = (n6 >> 8) & 0xff; + n6 &= 0xff; + + laddr = ntohl(so->so_faddr.s_addr); + + n1 = ((laddr >> 24) & 0xff); + n2 = ((laddr >> 16) & 0xff); + n3 = ((laddr >> 8) & 0xff); + n4 = (laddr & 0xff); + + m->m_len = bptr - m->m_data; /* Adjust length */ + m->m_len += sprintf(bptr,"27 Entering Passive Mode (%d,%d,%d,%d,%d,%d)\r\n%s", + n1, n2, n3, n4, n5, n6, x==7?buff:""); + + return 1; + } + + return 1; + + case EMU_KSH: + /* + * The kshell (Kerberos rsh) and shell services both pass + * a local port port number to carry signals to the server + * and stderr to the client. It is passed at the beginning + * of the connection as a NUL-terminated decimal ASCII string. + */ + so->so_emu = 0; + for (lport = 0, i = 0; i < m->m_len-1; ++i) { + if (m->m_data[i] < '0' || m->m_data[i] > '9') + return 1; /* invalid number */ + lport *= 10; + lport += m->m_data[i] - '0'; + } + if (m->m_data[m->m_len-1] == '\0' && lport != 0 && + (so = solisten(0, so->so_laddr.s_addr, htons(lport), SS_FACCEPTONCE)) != NULL) + m->m_len = sprintf(m->m_data, "%d", ntohs(so->so_fport))+1; + return 1; + + case EMU_IRC: + /* + * Need to emulate DCC CHAT, DCC SEND and DCC MOVE + */ + *(m->m_data+m->m_len) = 0; /* NULL terminate the string for strstr */ + if ((bptr = (char *)strstr(m->m_data, "DCC")) == NULL) + return 1; + + /* The %256s is for the broken mIRC */ + if (sscanf(bptr, "DCC CHAT %256s %u %u", buff, &laddr, &lport) == 3) { + if ((so = solisten(0, htonl(laddr), htons(lport), SS_FACCEPTONCE)) == NULL) + return 1; + + m->m_len = bptr - m->m_data; /* Adjust length */ + m->m_len += sprintf(bptr, "DCC CHAT chat %lu %u%c\n", + (unsigned long)ntohl(so->so_faddr.s_addr), + ntohs(so->so_fport), 1); + } else if (sscanf(bptr, "DCC SEND %256s %u %u %u", buff, &laddr, &lport, &n1) == 4) { + if ((so = solisten(0, htonl(laddr), htons(lport), SS_FACCEPTONCE)) == NULL) + return 1; + + m->m_len = bptr - m->m_data; /* Adjust length */ + m->m_len += sprintf(bptr, "DCC SEND %s %lu %u %u%c\n", + buff, (unsigned long)ntohl(so->so_faddr.s_addr), + ntohs(so->so_fport), n1, 1); + } else if (sscanf(bptr, "DCC MOVE %256s %u %u %u", buff, &laddr, &lport, &n1) == 4) { + if ((so = solisten(0, htonl(laddr), htons(lport), SS_FACCEPTONCE)) == NULL) + return 1; + + m->m_len = bptr - m->m_data; /* Adjust length */ + m->m_len += sprintf(bptr, "DCC MOVE %s %lu %u %u%c\n", + buff, (unsigned long)ntohl(so->so_faddr.s_addr), + ntohs(so->so_fport), n1, 1); + } + return 1; + + case EMU_REALAUDIO: + /* + * RealAudio emulation - JP. We must try to parse the incoming + * data and try to find the two characters that contain the + * port number. Then we redirect an udp port and replace the + * number with the real port we got. + * + * The 1.0 beta versions of the player are not supported + * any more. + * + * A typical packet for player version 1.0 (release version): + * + * 0000:50 4E 41 00 05 + * 0000:00 01 00 02 1B D7 00 00 67 E6 6C DC 63 00 12 50 .....×..gælÜc..P + * 0010:4E 43 4C 49 45 4E 54 20 31 30 31 20 41 4C 50 48 NCLIENT 101 ALPH + * 0020:41 6C 00 00 52 00 17 72 61 66 69 6C 65 73 2F 76 Al..R..rafiles/v + * 0030:6F 61 2F 65 6E 67 6C 69 73 68 5F 2E 72 61 79 42 oa/english_.rayB + * + * Now the port number 0x1BD7 is found at offset 0x04 of the + * Now the port number 0x1BD7 is found at offset 0x04 of the + * second packet. This time we received five bytes first and + * then the rest. You never know how many bytes you get. + * + * A typical packet for player version 2.0 (beta): + * + * 0000:50 4E 41 00 06 00 02 00 00 00 01 00 02 1B C1 00 PNA...........Á. + * 0010:00 67 75 78 F5 63 00 0A 57 69 6E 32 2E 30 2E 30 .guxõc..Win2.0.0 + * 0020:2E 35 6C 00 00 52 00 1C 72 61 66 69 6C 65 73 2F .5l..R..rafiles/ + * 0030:77 65 62 73 69 74 65 2F 32 30 72 65 6C 65 61 73 website/20releas + * 0040:65 2E 72 61 79 53 00 00 06 36 42 e.rayS...6B + * + * Port number 0x1BC1 is found at offset 0x0d. + * + * This is just a horrible switch statement. Variable ra tells + * us where we're going. + */ + + bptr = m->m_data; + while (bptr < m->m_data + m->m_len) { + u_short p; + static int ra = 0; + char ra_tbl[4]; + + ra_tbl[0] = 0x50; + ra_tbl[1] = 0x4e; + ra_tbl[2] = 0x41; + ra_tbl[3] = 0; + + switch (ra) { + case 0: + case 2: + case 3: + if (*bptr++ != ra_tbl[ra]) { + ra = 0; + continue; + } + break; + + case 1: + /* + * We may get 0x50 several times, ignore them + */ + if (*bptr == 0x50) { + ra = 1; + bptr++; + continue; + } else if (*bptr++ != ra_tbl[ra]) { + ra = 0; + continue; + } + break; + + case 4: + /* + * skip version number + */ + bptr++; + break; + + case 5: + /* + * The difference between versions 1.0 and + * 2.0 is here. For future versions of + * the player this may need to be modified. + */ + if (*(bptr + 1) == 0x02) + bptr += 8; + else + bptr += 4; + break; + + case 6: + /* This is the field containing the port + * number that RA-player is listening to. + */ + lport = (((u_char*)bptr)[0] << 8) + + ((u_char *)bptr)[1]; + if (lport < 6970) + lport += 256; /* don't know why */ + if (lport < 6970 || lport > 7170) + return 1; /* failed */ + + /* try to get udp port between 6970 - 7170 */ + for (p = 6970; p < 7071; p++) { + if (udp_listen( htons(p), + so->so_laddr.s_addr, + htons(lport), + SS_FACCEPTONCE)) { + break; + } + } + if (p == 7071) + p = 0; + *(u_char *)bptr++ = (p >> 8) & 0xff; + *(u_char *)bptr++ = p & 0xff; + ra = 0; + return 1; /* port redirected, we're done */ + break; + + default: + ra = 0; + } + ra++; + } + return 1; + + default: + /* Ooops, not emulated, won't call tcp_emu again */ + so->so_emu = 0; + return 1; + } +} + +/* + * Do misc. config of SLiRP while its running. + * Return 0 if this connections is to be closed, 1 otherwise, + * return 2 if this is a command-line connection + */ +int +tcp_ctl(so) + struct SLIRPsocket *so; +{ + struct sbuf *sb = &so->so_snd; + int command; + struct ex_list *ex_ptr; + int do_pty; + // struct SLIRPsocket *tmpso; + + DEBUG_CALL("tcp_ctl"); + DEBUG_ARG("so = %lx", (long )so); + +#if 0 + /* + * Check if they're authorised + */ + if (ctl_addr.s_addr && (ctl_addr.s_addr == -1 || (so->so_laddr.s_addr != ctl_addr.s_addr))) { + sb->sb_cc = sprintf(sb->sb_wptr,"Error: Permission denied.\r\n"); + sb->sb_wptr += sb->sb_cc; + return 0; + } +#endif + command = (ntohl(so->so_faddr.s_addr) & 0xff); + + switch(command) { + default: /* Check for exec's */ + + /* + * Check if it's pty_exec + */ + for (ex_ptr = exec_list; ex_ptr; ex_ptr = ex_ptr->ex_next) { + if (ex_ptr->ex_fport == so->so_fport && + command == ex_ptr->ex_addr) { + do_pty = ex_ptr->ex_pty; + goto do_exec; + } + } + + /* + * Nothing bound.. + */ + /* tcp_fconnect(so); */ + + /* FALLTHROUGH */ + case CTL_ALIAS: + sb->sb_cc = sprintf(sb->sb_wptr, + "Error: No application configured.\r\n"); + sb->sb_wptr += sb->sb_cc; + return(0); + + do_exec: + DEBUG_MISC((dfd, " executing %s \n",ex_ptr->ex_exec)); + return(fork_exec(so, ex_ptr->ex_exec, do_pty)); + +#if 0 + case CTL_CMD: + for (tmpso = tcb.so_next; tmpso != &tcb; tmpso = tmpso->so_next) { + if (tmpso->so_emu == EMU_CTL && + !(tmpso->so_tcpcb? + (tmpso->so_tcpcb->t_state & (TCPS_TIME_WAIT|TCPS_LAST_ACK)) + :0)) { + /* Ooops, control connection already active */ + sb->sb_cc = sprintf(sb->sb_wptr,"Sorry, already connected.\r\n"); + sb->sb_wptr += sb->sb_cc; + return 0; + } + } + so->so_emu = EMU_CTL; + ctl_password_ok = 0; + sb->sb_cc = sprintf(sb->sb_wptr, "Slirp command-line ready (type \"help\" for help).\r\nSlirp> "); + sb->sb_wptr += sb->sb_cc; + do_echo=-1; + return(2); +#endif + } +} diff --git a/src/slirp/tcp_timer.c b/src/slirp/tcp_timer.c new file mode 100644 index 0000000..db0c0dd --- /dev/null +++ b/src/slirp/tcp_timer.c @@ -0,0 +1,322 @@ +/* + * Copyright (c) 1982, 1986, 1988, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)tcp_timer.c 8.1 (Berkeley) 6/10/93 + * tcp_timer.c,v 1.2 1994/08/02 07:49:10 davidg Exp + */ + +#include "slirp.h" + +int tcp_keepidle = TCPTV_KEEP_IDLE; +int tcp_keepintvl = TCPTV_KEEPINTVL; +int tcp_maxidle; +int so_options = DO_KEEPALIVE; + +struct tcpstat tcpstat; /* tcp statistics */ +u_int32_t tcp_now; /* for RFC 1323 timestamps */ + +/* + * Fast timeout routine for processing delayed acks + */ +void +tcp_fasttimo() +{ + register struct SLIRPsocket *so; + register struct tcpcb *tp; + + DEBUG_CALL("tcp_fasttimo"); + + so = tcb.so_next; + if (so) + for (; so != &tcb; so = so->so_next) + if ((tp = (struct tcpcb *)so->so_tcpcb) && + (tp->t_flags & TF_DELACK)) { + tp->t_flags &= ~TF_DELACK; + tp->t_flags |= TF_ACKNOW; + tcpstat.tcps_delack++; + (void) tcp_output(tp); + } +} + +/* + * Tcp protocol timeout routine called every 500 ms. + * Updates the timers in all active tcb's and + * causes finite state machine actions if timers expire. + */ +void +tcp_slowtimo() +{ + register struct SLIRPsocket *ip, *ipnxt; + register struct tcpcb *tp; + register int i; + + DEBUG_CALL("tcp_slowtimo"); + + tcp_maxidle = TCPTV_KEEPCNT * tcp_keepintvl; + /* + * Search through tcb's and update active timers. + */ + ip = tcb.so_next; + if (ip == 0) + return; + for (; ip != &tcb; ip = ipnxt) { + ipnxt = ip->so_next; + tp = sototcpcb(ip); + if (tp == 0) + continue; + for (i = 0; i < TCPT_NTIMERS; i++) { + if (tp->t_timer[i] && --tp->t_timer[i] == 0) { + tcp_timers(tp,i); + if (ipnxt->so_prev != ip) + goto tpgone; + } + } + tp->t_idle++; + if (tp->t_rtt) + tp->t_rtt++; +tpgone: + ; + } + tcp_iss += TCP_ISSINCR/PR_SLOWHZ; /* increment iss */ +#ifdef TCP_COMPAT_42 + if ((int)tcp_iss < 0) + tcp_iss = 0; /* XXX */ +#endif + tcp_now++; /* for timestamps */ +} + +/* + * Cancel all timers for TCP tp. + */ +void +tcp_canceltimers(tp) + struct tcpcb *tp; +{ + register int i; + + for (i = 0; i < TCPT_NTIMERS; i++) + tp->t_timer[i] = 0; +} + +int tcp_backoff[TCP_MAXRXTSHIFT + 1] = + { 1, 2, 4, 8, 16, 32, 64, 64, 64, 64, 64, 64, 64 }; + +/* + * TCP timer processing. + */ +struct tcpcb * +tcp_timers(tp, timer) + struct tcpcb *tp; + int timer; +{ + int rexmt; + + DEBUG_CALL("tcp_timers"); + + switch (timer) { + + /* + * 2 MSL timeout in shutdown went off. If we're closed but + * still waiting for peer to close and connection has been idle + * too long, or if 2MSL time is up from TIME_WAIT, delete connection + * control block. Otherwise, check again in a bit. + */ + case TCPT_2MSL: + if (tp->t_state != TCPS_TIME_WAIT && + tp->t_idle <= tcp_maxidle) + tp->t_timer[TCPT_2MSL] = tcp_keepintvl; + else + tp = tcp_close(tp); + break; + + /* + * Retransmission timer went off. Message has not + * been acked within retransmit interval. Back off + * to a longer retransmit interval and retransmit one segment. + */ + case TCPT_REXMT: + + /* + * XXXXX If a packet has timed out, then remove all the queued + * packets for that session. + */ + + if (++tp->t_rxtshift > TCP_MAXRXTSHIFT) { + /* + * This is a hack to suit our terminal server here at the uni of canberra + * since they have trouble with zeroes... It usually lets them through + * unharmed, but under some conditions, it'll eat the zeros. If we + * keep retransmitting it, it'll keep eating the zeroes, so we keep + * retransmitting, and eventually the connection dies... + * (this only happens on incoming data) + * + * So, if we were gonna drop the connection from too many retransmits, + * don't... instead halve the t_maxseg, which might break up the NULLs and + * let them through + * + * *sigh* + */ + + tp->t_maxseg >>= 1; + if (tp->t_maxseg < 32) { + /* + * We tried our best, now the connection must die! + */ + tp->t_rxtshift = TCP_MAXRXTSHIFT; + tcpstat.tcps_timeoutdrop++; + tp = tcp_drop(tp, tp->t_softerror); + /* tp->t_softerror : ETIMEDOUT); */ /* XXX */ + return (tp); /* XXX */ + } + + /* + * Set rxtshift to 6, which is still at the maximum + * backoff time + */ + tp->t_rxtshift = 6; + } + tcpstat.tcps_rexmttimeo++; + rexmt = TCP_REXMTVAL(tp) * tcp_backoff[tp->t_rxtshift]; + TCPT_RANGESET(tp->t_rxtcur, rexmt, + (short)tp->t_rttmin, TCPTV_REXMTMAX); /* XXX */ + tp->t_timer[TCPT_REXMT] = tp->t_rxtcur; + /* + * If losing, let the lower level know and try for + * a better route. Also, if we backed off this far, + * our srtt estimate is probably bogus. Clobber it + * so we'll take the next rtt measurement as our srtt; + * move the current srtt into rttvar to keep the current + * retransmit times until then. + */ + if (tp->t_rxtshift > TCP_MAXRXTSHIFT / 4) { +/* in_losing(tp->t_inpcb); */ + tp->t_rttvar += (tp->t_srtt >> TCP_RTT_SHIFT); + tp->t_srtt = 0; + } + tp->snd_nxt = tp->snd_una; + /* + * If timing a segment in this window, stop the timer. + */ + tp->t_rtt = 0; + /* + * Close the congestion window down to one segment + * (we'll open it by one segment for each ack we get). + * Since we probably have a window's worth of unacked + * data accumulated, this "slow start" keeps us from + * dumping all that data as back-to-back packets (which + * might overwhelm an intermediate gateway). + * + * There are two phases to the opening: Initially we + * open by one mss on each ack. This makes the window + * size increase exponentially with time. If the + * window is larger than the path can handle, this + * exponential growth results in dropped packet(s) + * almost immediately. To get more time between + * drops but still "push" the network to take advantage + * of improving conditions, we switch from exponential + * to linear window opening at some threshold size. + * For a threshold, we use half the current window + * size, truncated to a multiple of the mss. + * + * (the minimum cwnd that will give us exponential + * growth is 2 mss. We don't allow the threshold + * to go below this.) + */ + { + u_int win = min(tp->snd_wnd, tp->snd_cwnd) / 2 / tp->t_maxseg; + if (win < 2) + win = 2; + tp->snd_cwnd = tp->t_maxseg; + tp->snd_ssthresh = win * tp->t_maxseg; + tp->t_dupacks = 0; + } + (void) tcp_output(tp); + break; + + /* + * Persistence timer into zero window. + * Force a byte to be output, if possible. + */ + case TCPT_PERSIST: + tcpstat.tcps_persisttimeo++; + tcp_setpersist(tp); + tp->t_force = 1; + (void) tcp_output(tp); + tp->t_force = 0; + break; + + /* + * Keep-alive timer went off; send something + * or drop connection if idle for too long. + */ + case TCPT_KEEP: + tcpstat.tcps_keeptimeo++; + if (tp->t_state < TCPS_ESTABLISHED) + goto dropit; + +/* if (tp->t_socket->so_options & SO_KEEPALIVE && */ + if ((so_options) && tp->t_state <= TCPS_CLOSE_WAIT) { + if (tp->t_idle >= tcp_keepidle + tcp_maxidle) + goto dropit; + /* + * Send a packet designed to force a response + * if the peer is up and reachable: + * either an ACK if the connection is still alive, + * or an RST if the peer has closed the connection + * due to timeout or reboot. + * Using sequence number tp->snd_una-1 + * causes the transmitted zero-length segment + * to lie outside the receive window; + * by the protocol spec, this requires the + * correspondent TCP to respond. + */ + tcpstat.tcps_keepprobe++; +#ifdef TCP_COMPAT_42 + /* + * The keepalive packet must have nonzero length + * to get a 4.2 host to respond. + */ + tcp_respond(tp, &tp->t_template, (struct SLIRPmbuf *)NULL, + tp->rcv_nxt - 1, tp->snd_una - 1, 0); +#else + tcp_respond(tp, &tp->t_template, (struct SLIRPmbuf *)NULL, + tp->rcv_nxt, tp->snd_una - 1, 0); +#endif + tp->t_timer[TCPT_KEEP] = tcp_keepintvl; + } else + tp->t_timer[TCPT_KEEP] = tcp_keepidle; + break; + + dropit: + tcpstat.tcps_keepdrops++; + tp = tcp_drop(tp, 0); /* ETIMEDOUT); */ + break; + } + + return (tp); +} diff --git a/src/slirp/tcp_timer.h b/src/slirp/tcp_timer.h new file mode 100644 index 0000000..e860b42 --- /dev/null +++ b/src/slirp/tcp_timer.h @@ -0,0 +1,138 @@ +/* + * Copyright (c) 1982, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)tcp_timer.h 8.1 (Berkeley) 6/10/93 + * tcp_timer.h,v 1.4 1994/08/21 05:27:38 paul Exp + */ + +#ifndef _TCP_TIMER_H_ +#define _TCP_TIMER_H_ + +/* + * Definitions of the TCP timers. These timers are counted + * down PR_SLOWHZ times a second. + */ +#define TCPT_NTIMERS 4 + +#define TCPT_REXMT 0 /* retransmit */ +#define TCPT_PERSIST 1 /* retransmit persistence */ +#define TCPT_KEEP 2 /* keep alive */ +#define TCPT_2MSL 3 /* 2*msl quiet time timer */ + +/* + * The TCPT_REXMT timer is used to force retransmissions. + * The TCP has the TCPT_REXMT timer set whenever segments + * have been sent for which ACKs are expected but not yet + * received. If an ACK is received which advances tp->snd_una, + * then the retransmit timer is cleared (if there are no more + * outstanding segments) or reset to the base value (if there + * are more ACKs expected). Whenever the retransmit timer goes off, + * we retransmit one unacknowledged segment, and do a backoff + * on the retransmit timer. + * + * The TCPT_PERSIST timer is used to keep window size information + * flowing even if the window goes shut. If all previous transmissions + * have been acknowledged (so that there are no retransmissions in progress), + * and the window is too small to bother sending anything, then we start + * the TCPT_PERSIST timer. When it expires, if the window is nonzero, + * we go to transmit state. Otherwise, at intervals send a single byte + * into the peer's window to force him to update our window information. + * We do this at most as often as TCPT_PERSMIN time intervals, + * but no more frequently than the current estimate of round-trip + * packet time. The TCPT_PERSIST timer is cleared whenever we receive + * a window update from the peer. + * + * The TCPT_KEEP timer is used to keep connections alive. If an + * connection is idle (no segments received) for TCPTV_KEEP_INIT amount of time, + * but not yet established, then we drop the connection. Once the connection + * is established, if the connection is idle for TCPTV_KEEP_IDLE time + * (and keepalives have been enabled on the socket), we begin to probe + * the connection. We force the peer to send us a segment by sending: + * + * This segment is (deliberately) outside the window, and should elicit + * an ack segment in response from the peer. If, despite the TCPT_KEEP + * initiated segments we cannot elicit a response from a peer in TCPT_MAXIDLE + * amount of time probing, then we drop the connection. + */ + +/* + * Time constants. + */ +#define TCPTV_MSL ( 5*PR_SLOWHZ) /* max seg lifetime (hah!) */ + +#define TCPTV_SRTTBASE 0 /* base roundtrip time; + if 0, no idea yet */ +#define TCPTV_SRTTDFLT ( 3*PR_SLOWHZ) /* assumed RTT if no info */ + +#define TCPTV_PERSMIN ( 5*PR_SLOWHZ) /* retransmit persistence */ +#define TCPTV_PERSMAX ( 60*PR_SLOWHZ) /* maximum persist interval */ + +#define TCPTV_KEEP_INIT ( 75*PR_SLOWHZ) /* initial connect keep alive */ +#define TCPTV_KEEP_IDLE (120*60*PR_SLOWHZ) /* dflt time before probing */ +#define TCPTV_KEEPINTVL ( 75*PR_SLOWHZ) /* default probe interval */ +#define TCPTV_KEEPCNT 8 /* max probes before drop */ + +#define TCPTV_MIN ( 1*PR_SLOWHZ) /* minimum allowable value */ +/* #define TCPTV_REXMTMAX ( 64*PR_SLOWHZ) */ /* max allowable REXMT value */ +#define TCPTV_REXMTMAX ( 12*PR_SLOWHZ) /* max allowable REXMT value */ + +#define TCP_LINGERTIME 120 /* linger at most 2 minutes */ + +#define TCP_MAXRXTSHIFT 12 /* maximum retransmits */ + + +#ifdef TCPTIMERS +char *tcptimers[] = + { "REXMT", "PERSIST", "KEEP", "2MSL" }; +#endif + +/* + * Force a time value to be in a certain range. + */ +#define TCPT_RANGESET(tv, value, tvmin, tvmax) { \ + (tv) = (value); \ + if ((tv) < (tvmin)) \ + (tv) = (tvmin); \ + else if ((tv) > (tvmax)) \ + (tv) = (tvmax); \ +} + +extern int tcp_keepidle; /* time before keepalive probes begin */ +extern int tcp_keepintvl; /* time between keepalive probes */ +extern int tcp_maxidle; /* time to drop after starting probes */ +extern int tcp_ttl; /* time to live for TCP segs */ +extern int tcp_backoff[]; + +struct tcpcb; + +void tcp_fasttimo _P((void)); +void tcp_slowtimo _P((void)); +void tcp_canceltimers _P((struct tcpcb *)); +struct tcpcb * tcp_timers _P((register struct tcpcb *, int)); + +#endif diff --git a/src/slirp/tcp_var.h b/src/slirp/tcp_var.h new file mode 100644 index 0000000..422a57b --- /dev/null +++ b/src/slirp/tcp_var.h @@ -0,0 +1,248 @@ +/* + * Copyright (c) 1982, 1986, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)tcp_var.h 8.3 (Berkeley) 4/10/94 + * tcp_var.h,v 1.3 1994/08/21 05:27:39 paul Exp + */ + +#ifndef _TCP_VAR_H_ +#define _TCP_VAR_H_ + +#include "tcpip.h" +#include "tcp_timer.h" + +#if SIZEOF_CHAR_P == 4 + typedef struct tcpiphdr *tcpiphdrp_32; +#else + typedef u_int32_t tcpiphdrp_32; +#endif + +/* + * Tcp control block, one per tcp; fields: + */ +struct tcpcb { + tcpiphdrp_32 seg_next; /* sequencing queue */ + tcpiphdrp_32 seg_prev; + short t_state; /* state of this connection */ + short t_timer[TCPT_NTIMERS]; /* tcp timers */ + short t_rxtshift; /* log(2) of rexmt exp. backoff */ + short t_rxtcur; /* current retransmit value */ + short t_dupacks; /* consecutive dup acks recd */ + u_short t_maxseg; /* maximum segment size */ + char t_force; /* 1 if forcing out a byte */ + u_short t_flags; +#define TF_ACKNOW 0x0001 /* ack peer immediately */ +#define TF_DELACK 0x0002 /* ack, but try to delay it */ +#define TF_NODELAY 0x0004 /* don't delay packets to coalesce */ +#define TF_NOOPT 0x0008 /* don't use tcp options */ +#define TF_SENTFIN 0x0010 /* have sent FIN */ +#define TF_REQ_SCALE 0x0020 /* have/will request window scaling */ +#define TF_RCVD_SCALE 0x0040 /* other side has requested scaling */ +#define TF_REQ_TSTMP 0x0080 /* have/will request timestamps */ +#define TF_RCVD_TSTMP 0x0100 /* a timestamp was received in SYN */ +#define TF_SACK_PERMIT 0x0200 /* other side said I could SACK */ + + /* Make it static for now */ +/* struct tcpiphdr *t_template; / * skeletal packet for transmit */ + struct tcpiphdr t_template; + + struct SLIRPsocket *t_socket; /* back pointer to socket */ +/* + * The following fields are used as in the protocol specification. + * See RFC783, Dec. 1981, page 21. + */ +/* send sequence variables */ + tcp_seq snd_una; /* send unacknowledged */ + tcp_seq snd_nxt; /* send next */ + tcp_seq snd_up; /* send urgent pointer */ + tcp_seq snd_wl1; /* window update seg seq number */ + tcp_seq snd_wl2; /* window update seg ack number */ + tcp_seq iss; /* initial send sequence number */ + u_int32_t snd_wnd; /* send window */ +/* receive sequence variables */ + u_int32_t rcv_wnd; /* receive window */ + tcp_seq rcv_nxt; /* receive next */ + tcp_seq rcv_up; /* receive urgent pointer */ + tcp_seq irs; /* initial receive sequence number */ +/* + * Additional variables for this implementation. + */ +/* receive variables */ + tcp_seq rcv_adv; /* advertised window */ +/* retransmit variables */ + tcp_seq snd_max; /* highest sequence number sent; + * used to recognize retransmits + */ +/* congestion control (for slow start, source quench, retransmit after loss) */ + u_int32_t snd_cwnd; /* congestion-controlled window */ + u_int32_t snd_ssthresh; /* snd_cwnd size threshold for + * for slow start exponential to + * linear switch + */ +/* + * transmit timing stuff. See below for scale of srtt and rttvar. + * "Variance" is actually smoothed difference. + */ + short t_idle; /* inactivity time */ + short t_rtt; /* round trip time */ + tcp_seq t_rtseq; /* sequence number being timed */ + short t_srtt; /* smoothed round-trip time */ + short t_rttvar; /* variance in round-trip time */ + u_short t_rttmin; /* minimum rtt allowed */ + u_int32_t max_sndwnd; /* largest window peer has offered */ + +/* out-of-band data */ + char t_oobflags; /* have some */ + char t_iobc; /* input character */ +#define TCPOOB_HAVEDATA 0x01 +#define TCPOOB_HADDATA 0x02 + short t_softerror; /* possible error not yet reported */ + +/* RFC 1323 variables */ + u_char snd_scale; /* window scaling for send window */ + u_char rcv_scale; /* window scaling for recv window */ + u_char request_r_scale; /* pending window scaling */ + u_char requested_s_scale; + u_int32_t ts_recent; /* timestamp echo data */ + u_int32_t ts_recent_age; /* when last updated */ + tcp_seq last_ack_sent; + +}; + +#define sototcpcb(so) ((so)->so_tcpcb) + +/* + * The smoothed round-trip time and estimated variance + * are stored as fixed point numbers scaled by the values below. + * For convenience, these scales are also used in smoothing the average + * (smoothed = (1/scale)sample + ((scale-1)/scale)smoothed). + * With these scales, srtt has 3 bits to the right of the binary point, + * and thus an "ALPHA" of 0.875. rttvar has 2 bits to the right of the + * binary point, and is smoothed with an ALPHA of 0.75. + */ +#define TCP_RTT_SCALE 8 /* multiplier for srtt; 3 bits frac. */ +#define TCP_RTT_SHIFT 3 /* shift for srtt; 3 bits frac. */ +#define TCP_RTTVAR_SCALE 4 /* multiplier for rttvar; 2 bits */ +#define TCP_RTTVAR_SHIFT 2 /* multiplier for rttvar; 2 bits */ + +/* + * The initial retransmission should happen at rtt + 4 * rttvar. + * Because of the way we do the smoothing, srtt and rttvar + * will each average +1/2 tick of bias. When we compute + * the retransmit timer, we want 1/2 tick of rounding and + * 1 extra tick because of +-1/2 tick uncertainty in the + * firing of the timer. The bias will give us exactly the + * 1.5 tick we need. But, because the bias is + * statistical, we have to test that we don't drop below + * the minimum feasible timer (which is 2 ticks). + * This macro assumes that the value of TCP_RTTVAR_SCALE + * is the same as the multiplier for rttvar. + */ +#define TCP_REXMTVAL(tp) \ + (((tp)->t_srtt >> TCP_RTT_SHIFT) + (tp)->t_rttvar) + +/* XXX + * We want to avoid doing m_pullup on incoming packets but that + * means avoiding dtom on the tcp reassembly code. That in turn means + * keeping an mbuf pointer in the reassembly queue (since we might + * have a cluster). As a quick hack, the source & destination + * port numbers (which are no longer needed once we've located the + * tcpcb) are overlayed with an mbuf pointer. + */ +#if SIZEOF_CHAR_P == 4 +typedef struct SLIRPmbuf *mbufp_32; +#else +typedef u_int32_t mbufp_32; +#endif +#define REASS_MBUF(ti) (*(mbufp_32 *)&((ti)->ti_t)) + +/* + * TCP statistics. + * Many of these should be kept per connection, + * but that's inconvenient at the moment. + */ +struct tcpstat { + u_long tcps_connattempt; /* connections initiated */ + u_long tcps_accepts; /* connections accepted */ + u_long tcps_connects; /* connections established */ + u_long tcps_drops; /* connections dropped */ + u_long tcps_conndrops; /* embryonic connections dropped */ + u_long tcps_closed; /* conn. closed (includes drops) */ + u_long tcps_segstimed; /* segs where we tried to get rtt */ + u_long tcps_rttupdated; /* times we succeeded */ + u_long tcps_delack; /* delayed acks sent */ + u_long tcps_timeoutdrop; /* conn. dropped in rxmt timeout */ + u_long tcps_rexmttimeo; /* retransmit timeouts */ + u_long tcps_persisttimeo; /* persist timeouts */ + u_long tcps_keeptimeo; /* keepalive timeouts */ + u_long tcps_keepprobe; /* keepalive probes sent */ + u_long tcps_keepdrops; /* connections dropped in keepalive */ + + u_long tcps_sndtotal; /* total packets sent */ + u_long tcps_sndpack; /* data packets sent */ + u_long tcps_sndbyte; /* data bytes sent */ + u_long tcps_sndrexmitpack; /* data packets retransmitted */ + u_long tcps_sndrexmitbyte; /* data bytes retransmitted */ + u_long tcps_sndacks; /* ack-only packets sent */ + u_long tcps_sndprobe; /* window probes sent */ + u_long tcps_sndurg; /* packets sent with URG only */ + u_long tcps_sndwinup; /* window update-only packets sent */ + u_long tcps_sndctrl; /* control (SYN|FIN|RST) packets sent */ + + u_long tcps_rcvtotal; /* total packets received */ + u_long tcps_rcvpack; /* packets received in sequence */ + u_long tcps_rcvbyte; /* bytes received in sequence */ + u_long tcps_rcvbadsum; /* packets received with ccksum errs */ + u_long tcps_rcvbadoff; /* packets received with bad offset */ +/* u_long tcps_rcvshort; */ /* packets received too short */ + u_long tcps_rcvduppack; /* duplicate-only packets received */ + u_long tcps_rcvdupbyte; /* duplicate-only bytes received */ + u_long tcps_rcvpartduppack; /* packets with some duplicate data */ + u_long tcps_rcvpartdupbyte; /* dup. bytes in part-dup. packets */ + u_long tcps_rcvoopack; /* out-of-order packets received */ + u_long tcps_rcvoobyte; /* out-of-order bytes received */ + u_long tcps_rcvpackafterwin; /* packets with data after window */ + u_long tcps_rcvbyteafterwin; /* bytes rcvd after window */ + u_long tcps_rcvafterclose; /* packets rcvd after "close" */ + u_long tcps_rcvwinprobe; /* rcvd window probe packets */ + u_long tcps_rcvdupack; /* rcvd duplicate acks */ + u_long tcps_rcvacktoomuch; /* rcvd acks for unsent data */ + u_long tcps_rcvackpack; /* rcvd ack packets */ + u_long tcps_rcvackbyte; /* bytes acked by rcvd acks */ + u_long tcps_rcvwinupd; /* rcvd window update packets */ +/* u_long tcps_pawsdrop; */ /* segments dropped due to PAWS */ + u_long tcps_predack; /* times hdr predict ok for acks */ + u_long tcps_preddat; /* times hdr predict ok for data pkts */ + u_long tcps_socachemiss; /* tcp_last_so misses */ + u_long tcps_didnuttin; /* Times tcp_output didn't do anything XXX */ +}; + +extern struct tcpstat tcpstat; /* tcp statistics */ +extern u_int32_t tcp_now; /* for RFC 1323 timestamps */ + +#endif diff --git a/src/slirp/tcpip.h b/src/slirp/tcpip.h new file mode 100644 index 0000000..2edccb2 --- /dev/null +++ b/src/slirp/tcpip.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 1982, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)tcpip.h 8.1 (Berkeley) 6/10/93 + * tcpip.h,v 1.3 1994/08/21 05:27:40 paul Exp + */ + +#ifndef _TCPIP_H_ +#define _TCPIP_H_ + +/* + * Tcp+ip header, after ip options removed. + */ +struct tcpiphdr { + struct ipovly ti_i; /* overlaid ip structure */ + struct tcphdr ti_t; /* tcp header */ +}; +#define ti_next ti_i.ih_next +#define ti_prev ti_i.ih_prev +#define ti_x1 ti_i.ih_x1 +#define ti_pr ti_i.ih_pr +#define ti_len ti_i.ih_len +#define ti_src ti_i.ih_src +#define ti_dst ti_i.ih_dst +#define ti_sport ti_t.th_sport +#define ti_dport ti_t.th_dport +#define ti_seq ti_t.th_seq +#define ti_ack ti_t.th_ack +#define ti_x2 ti_t.th_x2 +#define ti_off ti_t.th_off +#define ti_flags ti_t.th_flags +#define ti_win ti_t.th_win +#define ti_sum ti_t.th_sum +#define ti_urp ti_t.th_urp + +/* + * Just a clean way to get to the first byte + * of the packet + */ +struct tcpiphdr_2 { + struct tcpiphdr dummy; + char first_char; +}; + +#endif diff --git a/src/slirp/tftp.c b/src/slirp/tftp.c new file mode 100644 index 0000000..3a851a6 --- /dev/null +++ b/src/slirp/tftp.c @@ -0,0 +1,331 @@ +/* + * tftp.c - a simple, read-only tftp server for qemu + * + * Copyright (c) 2004 Magnus Damm + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "slirp.h" + +struct tftp_session { + int in_use; + char filename[TFTP_FILENAME_MAX]; + + struct in_addr client_ip; + u_int16_t client_port; + + int timestamp; +}; + +struct tftp_session tftp_sessions[TFTP_SESSIONS_MAX]; + +const char *tftp_prefix; + +static void tftp_session_update(struct tftp_session *spt) +{ + spt->timestamp = curtime; + spt->in_use = 1; +} + +static void tftp_session_terminate(struct tftp_session *spt) +{ + spt->in_use = 0; +} + +static int tftp_session_allocate(struct tftp_t *tp) +{ + struct tftp_session *spt; + int k; + + for (k = 0; k < TFTP_SESSIONS_MAX; k++) { + spt = &tftp_sessions[k]; + + if (!spt->in_use) + goto found; + + /* sessions time out after 5 inactive seconds */ + if ((int)(curtime - spt->timestamp) > 5000) + goto found; + } + + return -1; + + found: + memset(spt, 0, sizeof(*spt)); + memcpy(&spt->client_ip, &tp->ip.ip_src, sizeof(spt->client_ip)); + spt->client_port = tp->udp.uh_sport; + + tftp_session_update(spt); + + return k; +} + +static int tftp_session_find(struct tftp_t *tp) +{ + struct tftp_session *spt; + int k; + + for (k = 0; k < TFTP_SESSIONS_MAX; k++) { + spt = &tftp_sessions[k]; + + if (spt->in_use) { + if (!memcmp(&spt->client_ip, &tp->ip.ip_src, sizeof(spt->client_ip))) { + if (spt->client_port == tp->udp.uh_sport) { + return k; + } + } + } + } + + return -1; +} + +static int tftp_read_data(struct tftp_session *spt, u_int16_t block_nr, + u_int8_t *buf, int len) +{ + int fd; + int bytes_read = 0; + + fd = open(spt->filename, O_RDONLY | O_BINARY); + + if (fd < 0) { + return -1; + } + + if (len) { + lseek(fd, block_nr * 512, SEEK_SET); + + bytes_read = read(fd, buf, len); + } + + close(fd); + + return bytes_read; +} + +static int tftp_send_error(struct tftp_session *spt, + u_int16_t errorcode, const char *msg, + struct tftp_t *recv_tp) +{ + struct sockaddr_in saddr, daddr; + struct SLIRPmbuf *m; + struct tftp_t *tp; + + m = m_get(); + + if (!m) { + return -1; + } + + memset(m->m_data, 0, m->m_size); + + m->m_data += if_maxlinkhdr; + tp = (void *)m->m_data; + m->m_data += sizeof(struct udpiphdr); + + tp->tp_op = htons(TFTP_ERROR); + tp->x.tp_error.tp_error_code = htons(errorcode); + strncpy((char *)tp->x.tp_error.tp_msg, msg, sizeof(tp->x.tp_error.tp_msg)); + tp->x.tp_error.tp_msg[sizeof(tp->x.tp_error.tp_msg)-1] = 0; + + saddr.sin_addr = recv_tp->ip.ip_dst; + saddr.sin_port = recv_tp->udp.uh_dport; + + daddr.sin_addr = spt->client_ip; + daddr.sin_port = spt->client_port; + + m->m_len = sizeof(struct tftp_t) - 514 + 3 + strlen(msg) - + sizeof(struct ip) - sizeof(struct udphdr); + + udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY); + + tftp_session_terminate(spt); + + return 0; +} + +static int tftp_send_data(struct tftp_session *spt, + u_int16_t block_nr, + struct tftp_t *recv_tp) +{ + struct sockaddr_in saddr, daddr; + struct SLIRPmbuf *m; + struct tftp_t *tp; + int nobytes; + + if (block_nr < 1) { + return -1; + } + + m = m_get(); + + if (!m) { + return -1; + } + + memset(m->m_data, 0, m->m_size); + + m->m_data += if_maxlinkhdr; + tp = (void *)m->m_data; + m->m_data += sizeof(struct udpiphdr); + + tp->tp_op = htons(TFTP_DATA); + tp->x.tp_data.tp_block_nr = htons(block_nr); + + saddr.sin_addr = recv_tp->ip.ip_dst; + saddr.sin_port = recv_tp->udp.uh_dport; + + daddr.sin_addr = spt->client_ip; + daddr.sin_port = spt->client_port; + + nobytes = tftp_read_data(spt, block_nr - 1, tp->x.tp_data.tp_buf, 512); + + if (nobytes < 0) { + m_free(m); + + /* send "file not found" error back */ + + tftp_send_error(spt, 1, "File not found", tp); + + return -1; + } + + m->m_len = sizeof(struct tftp_t) - (512 - nobytes) - + sizeof(struct ip) - sizeof(struct udphdr); + + udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY); + + if (nobytes == 512) { + tftp_session_update(spt); + } + else { + tftp_session_terminate(spt); + } + + return 0; +} + +static void tftp_handle_rrq(struct tftp_t *tp, int pktlen) +{ + struct tftp_session *spt; + int s, k, n; + u_int8_t *src, *dst; + + s = tftp_session_allocate(tp); + + if (s < 0) { + return; + } + + spt = &tftp_sessions[s]; + + src = tp->x.tp_buf; + dst = (u_int8_t *)spt->filename; + n = pktlen - ((uint8_t *)&tp->x.tp_buf[0] - (uint8_t *)tp); + + /* get name */ + + for (k = 0; k < n; k++) { + if (k < TFTP_FILENAME_MAX) { + dst[k] = src[k]; + } + else { + return; + } + + if (src[k] == '\0') { + break; + } + } + + if (k >= n) { + return; + } + + k++; + + /* check mode */ + if ((n - k) < 6) { + return; + } + + if (memcmp(&src[k], "octet\0", 6) != 0) { + tftp_send_error(spt, 4, "Unsupported transfer mode", tp); + return; + } + + /* do sanity checks on the filename */ + + if ((spt->filename[0] != '/') + || (spt->filename[strlen(spt->filename) - 1] == '/') + || strstr(spt->filename, "/../")) { + tftp_send_error(spt, 2, "Access violation", tp); + return; + } + + /* only allow exported prefixes */ + + if (!tftp_prefix + || (strncmp(spt->filename, tftp_prefix, strlen(tftp_prefix)) != 0)) { + tftp_send_error(spt, 2, "Access violation", tp); + return; + } + + /* check if the file exists */ + + if (tftp_read_data(spt, 0, (u_int8_t *)spt->filename, 0) < 0) { + tftp_send_error(spt, 1, "File not found", tp); + return; + } + + tftp_send_data(spt, 1, tp); +} + +static void tftp_handle_ack(struct tftp_t *tp, int pktlen) +{ + int s; + + s = tftp_session_find(tp); + + if (s < 0) { + return; + } + + if (tftp_send_data(&tftp_sessions[s], + ntohs(tp->x.tp_data.tp_block_nr) + 1, + tp) < 0) { + return; + } +} + +void tftp_input(struct SLIRPmbuf *m) +{ + struct tftp_t *tp = (struct tftp_t *)m->m_data; + + switch(ntohs(tp->tp_op)) { + case TFTP_RRQ: + tftp_handle_rrq(tp, m->m_len); + break; + + case TFTP_ACK: + tftp_handle_ack(tp, m->m_len); + break; + } +} diff --git a/src/slirp/tftp.h b/src/slirp/tftp.h new file mode 100644 index 0000000..266ce16 --- /dev/null +++ b/src/slirp/tftp.h @@ -0,0 +1,40 @@ +/* tftp defines */ + +#define TFTP_SESSIONS_MAX 3 + +#define TFTP_SERVER 69 + +#define TFTP_RRQ 1 +#define TFTP_WRQ 2 +#define TFTP_DATA 3 +#define TFTP_ACK 4 +#define TFTP_ERROR 5 + +#define TFTP_FILENAME_MAX 512 + +#ifdef PRAGMA_PACK_SUPPORTED +#pragma pack(1) +#endif + +struct tftp_t { + struct ip ip; + struct udphdr udp; + u_int16_t tp_op; + union { + struct { + u_int16_t tp_block_nr; + u_int8_t tp_buf[512]; + } tp_data; + struct { + u_int16_t tp_error_code; + u_int8_t tp_msg[512]; + } tp_error; + u_int8_t tp_buf[512 + 2]; + } x; +} PACKED__; + +#ifdef PRAGMA_PACK_SUPPORTED +#pragma pack(PACK_END) +#endif + +void tftp_input(struct SLIRPmbuf *m); diff --git a/src/slirp/udp.c b/src/slirp/udp.c new file mode 100644 index 0000000..928a64f --- /dev/null +++ b/src/slirp/udp.c @@ -0,0 +1,676 @@ +/* + * Copyright (c) 1982, 1986, 1988, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)udp_usrreq.c 8.4 (Berkeley) 1/21/94 + * udp_usrreq.c,v 1.4 1994/10/02 17:48:45 phk Exp + */ + +/* + * Changes and additions relating to SLiRP + * Copyright (c) 1995 Danny Gasparovski. + * + * Please read the file COPYRIGHT for the + * terms and conditions of the copyright. + */ + +#include +#include "slirp.h" +#include "ip_icmp.h" + +struct udpstat udpstat; + +struct SLIRPsocket udb; + +/* + * UDP protocol implementation. + * Per RFC 768, August, 1980. + */ +#ifndef COMPAT_42 +int udpcksum = 1; +#else +int udpcksum = 0; /* XXX */ +#endif + +struct SLIRPsocket *udp_last_so = &udb; + +void +udp_init() +{ + udb.so_next = udb.so_prev = &udb; +} +/* m->m_data points at ip packet header + * m->m_len length ip packet + * ip->ip_len length data (IPDU) + */ +void +udp_input(m, iphlen) + struct SLIRPmbuf *m; + int iphlen; +{ + struct ip *ip; + struct udphdr *uh; +/* struct SLIRPmbuf *opts = 0;*/ + int len; + struct ip save_ip; + struct SLIRPsocket *so; + + DEBUG_CALL("udp_input"); + DEBUG_ARG("m = %lx", (long)m); + DEBUG_ARG("iphlen = %d", iphlen); + + udpstat.udps_ipackets++; + + /* + * Strip IP options, if any; should skip this, + * make available to user, and use on returned packets, + * but we don't yet have a way to check the checksum + * with options still present. + */ + if(iphlen > sizeof(struct ip)) { + ip_stripoptions(m, (struct SLIRPmbuf *)0); + iphlen = sizeof(struct ip); + } + + /* + * Get IP and UDP header together in first SLIRPmbuf. + */ + ip = mtod(m, struct ip *); + uh = (struct udphdr *)((SLIRPcaddr_t)ip + iphlen); + + /* + * Make SLIRPmbuf data length reflect UDP length. + * If not enough data to reflect UDP length, drop. + */ + len = ntohs((u_int16_t)uh->uh_ulen); + + if (ip->ip_len != len) { + if (len > ip->ip_len) { + udpstat.udps_badlen++; + goto bad; + } + m_adj(m, len - ip->ip_len); + ip->ip_len = len; + } + + /* + * Save a copy of the IP header in case we want restore it + * for sending an ICMP error message in response. + */ + save_ip = *ip; + save_ip.ip_len+= iphlen; /* tcp_input subtracts this */ + + /* + * Checksum extended UDP header and data. + */ + if (udpcksum && uh->uh_sum) { + ((struct ipovly *)ip)->ih_next = 0; + ((struct ipovly *)ip)->ih_prev = 0; + ((struct ipovly *)ip)->ih_x1 = 0; + ((struct ipovly *)ip)->ih_len = uh->uh_ulen; + /* keep uh_sum for ICMP reply + * uh->uh_sum = cksum(m, len + sizeof (struct ip)); + * if (uh->uh_sum) { + */ + if(cksum(m, len + sizeof(struct ip))) { + udpstat.udps_badsum++; + goto bad; + } + } + + /* + * handle DHCP/BOOTP + */ + if (ntohs(uh->uh_dport) == BOOTP_SERVER) { + bootp_input(m); + goto bad; + } + + /* + * handle TFTP + */ + if (ntohs(uh->uh_dport) == TFTP_SERVER) { + tftp_input(m); + goto bad; + } + + /* + * Locate pcb for datagram. + */ + so = udp_last_so; + if (so->so_lport != uh->uh_sport || + so->so_laddr.s_addr != ip->ip_src.s_addr) { + struct SLIRPsocket *tmp; + + for (tmp = udb.so_next; tmp != &udb; tmp = tmp->so_next) { + if (tmp->so_lport == uh->uh_sport && + tmp->so_laddr.s_addr == ip->ip_src.s_addr) { + tmp->so_faddr.s_addr = ip->ip_dst.s_addr; + tmp->so_fport = uh->uh_dport; + so = tmp; + break; + } + } + if (tmp == &udb) { + so = NULL; + } else { + udpstat.udpps_pcbcachemiss++; + udp_last_so = so; + } + } + + if (so == NULL) { + /* + * If there's no socket for this packet, + * create one + */ + if ((so = socreate()) == NULL) goto bad; + if(udp_attach(so) == -1) { + DEBUG_MISC((dfd," udp_attach errno = %d-%s\n", + errno,strerror(errno))); + sofree(so); + goto bad; + } + + /* + * Setup fields + */ + /* udp_last_so = so; */ + so->so_laddr = ip->ip_src; + so->so_lport = uh->uh_sport; + + if ((so->so_iptos = udp_tos(so)) == 0) + so->so_iptos = ip->ip_tos; + + /* + * XXXXX Here, check if it's in udpexec_list, + * and if it is, do the fork_exec() etc. + */ + } + + so->so_faddr = ip->ip_dst; /* XXX */ + so->so_fport = uh->uh_dport; /* XXX */ + + iphlen += sizeof(struct udphdr); + m->m_len -= iphlen; + m->m_data += iphlen; + + /* + * Now we sendto() the packet. + */ + if (so->so_emu) + udp_emu(so, m); + + if(sosendto(so,m) == -1) { + m->m_len += iphlen; + m->m_data -= iphlen; + *ip=save_ip; + DEBUG_MISC((dfd,"udp tx errno = %d-%s\n",errno,strerror(errno))); + icmp_error(m, ICMP_UNREACH,ICMP_UNREACH_NET, 0,strerror(errno)); + } + + m_free(so->so_m); /* used for ICMP if error on sorecvfrom */ + + /* restore the orig SLIRPmbuf packet */ + m->m_len += iphlen; + m->m_data -= iphlen; + *ip=save_ip; + so->so_m=m; /* ICMP backup */ + + return; +bad: + m_freem(m); + /* if (opts) m_freem(opts); */ + return; +} + +int udp_output2(struct SLIRPsocket *so, struct SLIRPmbuf *m, + struct sockaddr_in *saddr, struct sockaddr_in *daddr, + int iptos) +{ + struct udpiphdr *ui; + int error = 0; + + DEBUG_CALL("udp_output"); + DEBUG_ARG("so = %lx", (long)so); + DEBUG_ARG("m = %lx", (long)m); + DEBUG_ARG("saddr = %lx", (long)saddr->sin_addr.s_addr); + DEBUG_ARG("daddr = %lx", (long)daddr->sin_addr.s_addr); + + /* + * Adjust for header + */ + m->m_data -= sizeof(struct udpiphdr); + m->m_len += sizeof(struct udpiphdr); + + /* + * Fill in SLIRPmbuf with extended UDP header + * and addresses and length put into network format. + */ + ui = mtod(m, struct udpiphdr *); + ui->ui_next = ui->ui_prev = 0; + ui->ui_x1 = 0; + ui->ui_pr = IPPROTO_UDP; + ui->ui_len = htons(m->m_len - sizeof(struct ip)); /* + sizeof (struct udphdr)); */ + /* XXXXX Check for from-one-location sockets, or from-any-location sockets */ + ui->ui_src = saddr->sin_addr; + ui->ui_dst = daddr->sin_addr; + ui->ui_sport = saddr->sin_port; + ui->ui_dport = daddr->sin_port; + ui->ui_ulen = ui->ui_len; + + /* + * Stuff checksum and output datagram. + */ + ui->ui_sum = 0; + if (udpcksum) { + if ((ui->ui_sum = cksum(m, /* sizeof (struct udpiphdr) + */ m->m_len)) == 0) + ui->ui_sum = 0xffff; + } + ((struct ip *)ui)->ip_len = m->m_len; + + ((struct ip *)ui)->ip_ttl = ip_defttl; + ((struct ip *)ui)->ip_tos = iptos; + + udpstat.udps_opackets++; + + error = ip_output(so, m); + + return (error); +} + +int udp_output(struct SLIRPsocket *so, struct SLIRPmbuf *m, + struct sockaddr_in *addr) + +{ + struct sockaddr_in saddr, daddr; + + saddr = *addr; + if ((so->so_faddr.s_addr & htonl(0xffffff00)) == special_addr.s_addr) { + saddr.sin_addr.s_addr = so->so_faddr.s_addr; + if ((so->so_faddr.s_addr & htonl(0x000000ff)) == htonl(0xff)) + saddr.sin_addr.s_addr = alias_addr.s_addr; + } + daddr.sin_addr = so->so_laddr; + daddr.sin_port = so->so_lport; + + return udp_output2(so, m, &saddr, &daddr, so->so_iptos); +} + +int +udp_attach(so) + struct SLIRPsocket *so; +{ + struct sockaddr_in addr; + + if((so->s = socket(AF_INET,SOCK_DGRAM,0)) != -1) { + /* + * Here, we bind() the socket. Although not really needed + * (sendto() on an unbound socket will bind it), it's done + * here so that emulation of ytalk etc. don't have to do it + */ + memset(&addr, 0, sizeof(struct sockaddr_in)); + addr.sin_family = AF_INET; + addr.sin_port = 0; + addr.sin_addr.s_addr = INADDR_ANY; + if(bind(so->s, (struct sockaddr *)&addr, sizeof(addr))<0) { + int lasterrno=errno; + closesocket(so->s); + so->s=-1; +#ifdef _WIN32 + WSASetLastError(lasterrno); +#else + errno=lasterrno; +#endif + } else { + /* success, insert in queue */ + so->so_expire = curtime + SO_EXPIRE; + insque(so,&udb); + } + } + return(so->s); +} + +void +udp_detach(so) + struct SLIRPsocket *so; +{ + closesocket(so->s); + /* if (so->so_m) m_free(so->so_m); done by sofree */ + + sofree(so); +} + +struct tos_t udptos[] = { + {0, 53, IPTOS_LOWDELAY, 0}, /* DNS */ + {517, 517, IPTOS_LOWDELAY, EMU_TALK}, /* talk */ + {518, 518, IPTOS_LOWDELAY, EMU_NTALK}, /* ntalk */ + {0, 7648, IPTOS_LOWDELAY, EMU_CUSEEME}, /* Cu-Seeme */ + {0, 0, 0, 0} +}; + +u_int8_t +udp_tos(so) + struct SLIRPsocket *so; +{ + int i = 0; + + while(udptos[i].tos) { + if ((udptos[i].fport && ntohs(so->so_fport) == udptos[i].fport) || + (udptos[i].lport && ntohs(so->so_lport) == udptos[i].lport)) { + so->so_emu = udptos[i].emu; + return udptos[i].tos; + } + i++; + } + + return 0; +} + +#ifdef EMULATE_TALK +#include "talkd.h" +#endif + +/* + * Here, talk/ytalk/ntalk requests must be emulated + */ +void +udp_emu(so, m) + struct SLIRPsocket *so; + struct SLIRPmbuf *m; +{ + struct sockaddr_in addr; + socklen_t addrlen = sizeof(addr); +#ifdef EMULATE_TALK + CTL_MSG_OLD *omsg; + CTL_MSG *nmsg; + char buff[sizeof(CTL_MSG)]; + u_char type; + +struct talk_request { + struct talk_request *next; + struct SLIRPsocket *udp_so; + struct SLIRPsocket *tcp_so; +} *req; + + static struct talk_request *req_tbl = 0; + +#endif + +struct cu_header { + uint16_t d_family; // destination family + uint16_t d_port; // destination port + uint32_t d_addr; // destination address + uint16_t s_family; // source family + uint16_t s_port; // source port + uint32_t so_addr; // source address + uint32_t seqn; // sequence number + uint16_t message; // message + uint16_t data_type; // data type + uint16_t pkt_len; // packet length +} *cu_head; + + switch(so->so_emu) { + +#ifdef EMULATE_TALK + case EMU_TALK: + case EMU_NTALK: + /* + * Talk emulation. We always change the ctl_addr to get + * some answers from the daemon. When an ANNOUNCE comes, + * we send LEAVE_INVITE to the local daemons. Also when a + * DELETE comes, we send copies to the local daemons. + */ + if (getsockname(so->s, (struct sockaddr *)&addr, &addrlen) < 0) + return; + +#define IS_OLD (so->so_emu == EMU_TALK) + +#define COPY_MSG(dest, src) { dest->type = src->type; \ + dest->id_num = src->id_num; \ + dest->pid = src->pid; \ + dest->addr = src->addr; \ + dest->ctl_addr = src->ctl_addr; \ + memcpy(&dest->l_name, &src->l_name, NAME_SIZE_OLD); \ + memcpy(&dest->r_name, &src->r_name, NAME_SIZE_OLD); \ + memcpy(&dest->r_tty, &src->r_tty, TTY_SIZE); } + +#define OTOSIN(ptr, field) ((struct sockaddr_in *)&ptr->field) +/* old_sockaddr to sockaddr_in */ + + + if (IS_OLD) { /* old talk */ + omsg = mtod(m, CTL_MSG_OLD*); + nmsg = (CTL_MSG *) buff; + type = omsg->type; + OTOSIN(omsg, ctl_addr)->sin_port = addr.sin_port; + OTOSIN(omsg, ctl_addr)->sin_addr = our_addr; + strncpy(omsg->l_name, getlogin(), NAME_SIZE_OLD); + } else { /* new talk */ + omsg = (CTL_MSG_OLD *) buff; + nmsg = mtod(m, CTL_MSG *); + type = nmsg->type; + OTOSIN(nmsg, ctl_addr)->sin_port = addr.sin_port; + OTOSIN(nmsg, ctl_addr)->sin_addr = our_addr; + strncpy(nmsg->l_name, getlogin(), NAME_SIZE_OLD); + } + + if (type == LOOK_UP) + return; /* for LOOK_UP this is enough */ + + if (IS_OLD) { /* make a copy of the message */ + COPY_MSG(nmsg, omsg); + nmsg->vers = 1; + nmsg->answer = 0; + } else + COPY_MSG(omsg, nmsg); + + /* + * If if is an ANNOUNCE message, we go through the + * request table to see if a tcp port has already + * been redirected for this socket. If not, we solisten() + * a new socket and add this entry to the table. + * The port number of the tcp socket and our IP + * are put to the addr field of the message structures. + * Then a LEAVE_INVITE is sent to both local daemon + * ports, 517 and 518. This is why we have two copies + * of the message, one in old talk and one in new talk + * format. + */ + + if (type == ANNOUNCE) { + int s; + u_short temp_port; + + for(req = req_tbl; req; req = req->next) + if (so == req->udp_so) + break; /* found it */ + + if (!req) { /* no entry for so, create new */ + req = (struct talk_request *) + malloc(sizeof(struct talk_request)); + req->udp_so = so; + req->tcp_so = solisten(0, + OTOSIN(omsg, addr)->sin_addr.s_addr, + OTOSIN(omsg, addr)->sin_port, + SS_FACCEPTONCE); + req->next = req_tbl; + req_tbl = req; + } + + /* replace port number in addr field */ + addrlen = sizeof(addr); + getsockname(req->tcp_so->s, + (struct sockaddr *) &addr, + &addrlen); + OTOSIN(omsg, addr)->sin_port = addr.sin_port; + OTOSIN(omsg, addr)->sin_addr = our_addr; + OTOSIN(nmsg, addr)->sin_port = addr.sin_port; + OTOSIN(nmsg, addr)->sin_addr = our_addr; + + /* send LEAVE_INVITEs */ + temp_port = OTOSIN(omsg, ctl_addr)->sin_port; + OTOSIN(omsg, ctl_addr)->sin_port = 0; + OTOSIN(nmsg, ctl_addr)->sin_port = 0; + omsg->type = nmsg->type = LEAVE_INVITE; + + s = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); + addr.sin_addr = our_addr; + addr.sin_family = AF_INET; + addr.sin_port = htons(517); + sendto(s, (char *)omsg, sizeof(*omsg), 0, + (struct sockaddr *)&addr, sizeof(addr)); + addr.sin_port = htons(518); + sendto(s, (char *)nmsg, sizeof(*nmsg), 0, + (struct sockaddr *) &addr, sizeof(addr)); + closesocket(s) ; + + omsg->type = nmsg->type = ANNOUNCE; + OTOSIN(omsg, ctl_addr)->sin_port = temp_port; + OTOSIN(nmsg, ctl_addr)->sin_port = temp_port; + } + + /* + * If it is a DELETE message, we send a copy to the + * local daemons. Then we delete the entry corresponding + * to our socket from the request table. + */ + + if (type == DELETE) { + struct talk_request *temp_req, *req_next; + int s; + u_short temp_port; + + temp_port = OTOSIN(omsg, ctl_addr)->sin_port; + OTOSIN(omsg, ctl_addr)->sin_port = 0; + OTOSIN(nmsg, ctl_addr)->sin_port = 0; + + s = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); + addr.sin_addr = our_addr; + addr.sin_family = AF_INET; + addr.sin_port = htons(517); + sendto(s, (char *)omsg, sizeof(*omsg), 0, + (struct sockaddr *)&addr, sizeof(addr)); + addr.sin_port = htons(518); + sendto(s, (char *)nmsg, sizeof(*nmsg), 0, + (struct sockaddr *)&addr, sizeof(addr)); + closesocket(s); + + OTOSIN(omsg, ctl_addr)->sin_port = temp_port; + OTOSIN(nmsg, ctl_addr)->sin_port = temp_port; + + /* delete table entry */ + if (so == req_tbl->udp_so) { + temp_req = req_tbl; + req_tbl = req_tbl->next; + free(temp_req); + } else { + temp_req = req_tbl; + for(req = req_tbl->next; req; req = req_next) { + req_next = req->next; + if (so == req->udp_so) { + temp_req->next = req_next; + free(req); + break; + } else { + temp_req = req; + } + } + } + } + + return; +#endif + + case EMU_CUSEEME: + + /* + * Cu-SeeMe emulation. + * Hopefully the packet is more that 16 bytes long. We don't + * do any other tests, just replace the address and port + * fields. + */ + if (m->m_len >= sizeof (*cu_head)) { + if (getsockname(so->s, (struct sockaddr *)&addr, &addrlen) < 0) + return; + cu_head = mtod(m, struct cu_header *); + cu_head->s_port = addr.sin_port; + cu_head->so_addr = our_addr.s_addr; + } + + return; + } +} + +struct SLIRPsocket * +udp_listen(port, laddr, lport, flags) + u_int port; + u_int32_t laddr; + u_int lport; + int flags; +{ + struct sockaddr_in addr; + struct SLIRPsocket *so; + socklen_t addrlen = sizeof(struct sockaddr_in); + int opt = 1; + + if ((so = socreate()) == NULL) { + free(so); + return NULL; + } + so->s = socket(AF_INET,SOCK_DGRAM,0); + so->so_expire = curtime + SO_EXPIRE; + insque(so,&udb); + + memset(&addr, 0, sizeof(struct sockaddr_in)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = INADDR_ANY; + addr.sin_port = port; + + if (bind(so->s,(struct sockaddr *)&addr, addrlen) < 0) { + udp_detach(so); + return NULL; + } + setsockopt(so->s,SOL_SOCKET,SO_REUSEADDR,(char *)&opt,sizeof(int)); +/* setsockopt(so->s,SOL_SOCKET,SO_OOBINLINE,(char *)&opt,sizeof(int)); */ + + getsockname(so->s,(struct sockaddr *)&addr,&addrlen); + so->so_fport = addr.sin_port; + if (addr.sin_addr.s_addr == 0 || addr.sin_addr.s_addr == loopback_addr.s_addr) + so->so_faddr = alias_addr; + else + so->so_faddr = addr.sin_addr; + + so->so_lport = lport; + so->so_laddr.s_addr = laddr; + if (flags != SS_FACCEPTONCE) + so->so_expire = 0; + + so->so_state = SS_ISFCONNECTED; + + return so; +} diff --git a/src/slirp/udp.h b/src/slirp/udp.h new file mode 100644 index 0000000..807e55b --- /dev/null +++ b/src/slirp/udp.h @@ -0,0 +1,114 @@ +/* + * Copyright (c) 1982, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)udp.h 8.1 (Berkeley) 6/10/93 + * udp.h,v 1.3 1994/08/21 05:27:41 paul Exp + */ + +#ifndef _UDP_H_ +#define _UDP_H_ + +#define UDP_TTL 0x60 +#define UDP_UDPDATALEN 16192 + +extern struct SLIRPsocket *udp_last_so; + +/* + * Udp protocol header. + * Per RFC 768, September, 1981. + */ +#ifdef PRAGMA_PACK_SUPPORTED +#pragma pack(1) +#endif + +struct udphdr { + u_int16_t uh_sport; /* source port */ + u_int16_t uh_dport; /* destination port */ + int16_t uh_ulen; /* udp length */ + u_int16_t uh_sum; /* udp checksum */ +} PACKED__; + +#ifdef PRAGMA_PACK_SUPPORTED +#pragma pack(PACK_END) +#endif + +/* + * UDP kernel structures and variables. + */ +struct udpiphdr { + struct ipovly ui_i; /* overlaid ip structure */ + struct udphdr ui_u; /* udp header */ +}; +#define ui_next ui_i.ih_next +#define ui_prev ui_i.ih_prev +#define ui_x1 ui_i.ih_x1 +#define ui_pr ui_i.ih_pr +#define ui_len ui_i.ih_len +#define ui_src ui_i.ih_src +#define ui_dst ui_i.ih_dst +#define ui_sport ui_u.uh_sport +#define ui_dport ui_u.uh_dport +#define ui_ulen ui_u.uh_ulen +#define ui_sum ui_u.uh_sum + +struct udpstat { + /* input statistics: */ + u_long udps_ipackets; /* total input packets */ + u_long udps_hdrops; /* packet shorter than header */ + u_long udps_badsum; /* checksum error */ + u_long udps_badlen; /* data length larger than packet */ + u_long udps_noport; /* no socket on port */ + u_long udps_noportbcast; /* of above, arrived as broadcast */ + u_long udps_fullsock; /* not delivered, input socket full */ + u_long udpps_pcbcachemiss; /* input packets missing pcb cache */ + /* output statistics: */ + u_long udps_opackets; /* total output packets */ +}; + +/* + * Names for UDP sysctl objects + */ +#define UDPCTL_CHECKSUM 1 /* checksum UDP packets */ +#define UDPCTL_MAXID 2 + +extern struct udpstat udpstat; +extern struct SLIRPsocket udb; +struct SLIRPmbuf; + +void udp_init _P((void)); +void udp_input _P((register struct SLIRPmbuf *, int)); +int udp_output _P((struct SLIRPsocket *, struct SLIRPmbuf *, struct sockaddr_in *)); +int udp_attach _P((struct SLIRPsocket *)); +void udp_detach _P((struct SLIRPsocket *)); +u_int8_t udp_tos _P((struct SLIRPsocket *)); +void udp_emu _P((struct SLIRPsocket *, struct SLIRPmbuf *)); +struct SLIRPsocket * udp_listen _P((u_int, u_int32_t, u_int, int)); +int udp_output2(struct SLIRPsocket *so, struct SLIRPmbuf *m, + struct sockaddr_in *saddr, struct sockaddr_in *daddr, + int iptos); +#endif diff --git a/src/win-config.c b/src/win-config.c index c22850e..d658f59 100644 --- a/src/win-config.c +++ b/src/win-config.c @@ -20,11 +20,17 @@ #include "video.h" #include "vid_voodoo.h" #include "win.h" +#ifdef USE_NETWORKING +#include "nethandler.h" +#endif extern int is486; static int romstolist[ROM_MAX], listtomodel[ROM_MAX], romstomodel[ROM_MAX], modeltolist[ROM_MAX]; static int settings_sound_to_list[20], settings_list_to_sound[20]; static int settings_mouse_to_list[20], settings_list_to_mouse[20]; +#ifdef USE_NETWORKING +static int settings_network_to_list[20], settings_list_to_network[20]; +#endif static char *hdd_names[16]; int has_been_inited = 0; @@ -114,6 +120,47 @@ static void recalc_snd_list(HWND hdlg, int model) SendMessage(h, CB_SETCURSEL, settings_sound_to_list[sound_card_current], 0); } +#ifdef USE_NETWORKING +static void recalc_net_list(HWND hdlg, int model) +{ + HWND h = GetDlgItem(hdlg, IDC_COMBONETCARD); + int c = 0, d = 0; + int set_zero = 0; + + SendMessage(h, CB_RESETCONTENT, 0, 0); + + while (1) + { + char *s = network_card_getname(c); + + if (!s[0]) + break; + + settings_network_to_list[c] = d; + + if (network_card_available(c)) + { + device_t *network_dev = network_card_getdevice(c); + + if (!network_dev || (network_dev->flags & DEVICE_MCA) == (models[model].flags & MODEL_MCA)) + { + SendMessage(h, CB_ADDSTRING, 0, (LPARAM)(LPCSTR)s); + settings_list_to_network[d] = c; + d++; + } + else if (c == network_card_current) + set_zero = 1; + } + + c++; + } + if (set_zero) + SendMessage(h, CB_SETCURSEL, 0, 0); + else + SendMessage(h, CB_SETCURSEL, settings_network_to_list[network_card_current], 0); +} +#endif + static void recalc_hdd_list(HWND hdlg, int model, int use_selected_hdd) { HWND h; @@ -197,7 +244,9 @@ static BOOL CALLBACK config_dlgproc(HWND hdlg, UINT message, WPARAM wParam, LPAR int cpu_type; int temp_mouse_type; int hdd_changed = 0; - +#ifdef USE_NETWORKING + int temp_network_card; +#endif UDACCEL accel; // pclog("Dialog msg %i %08X\n",message,message); switch (message) @@ -400,6 +449,15 @@ static BOOL CALLBACK config_dlgproc(HWND hdlg, UINT message, WPARAM wParam, LPAR recalc_hdd_list(hdlg, romstomodel[romset], 0); +#ifdef USE_NETWORKING + recalc_net_list(hdlg, romstomodel[romset]); + + h = GetDlgItem(hdlg, IDC_CONFIGURENETCARD); + if (network_card_has_config(network_card_current)) + EnableWindow(h, TRUE); + else + EnableWindow(h, FALSE); +#endif return TRUE; case WM_COMMAND: @@ -465,13 +523,20 @@ static BOOL CALLBACK config_dlgproc(HWND hdlg, UINT message, WPARAM wParam, LPAR c = SendMessage(h, CB_GETCURSEL, 0, 0); if (hdd_names[c]) hdd_changed = strncmp(hdd_names[c], hdd_controller_name, sizeof(hdd_controller_name)-1); - +#ifdef USE_NETWORKING + h = GetDlgItem(hdlg, IDC_COMBONETCARD); + temp_network_card = settings_list_to_network[SendMessage(h, CB_GETCURSEL, 0, 0)]; +#endif if (temp_model != model || gfx != gfxcard || mem != mem_size || fpu != hasfpu || temp_GAMEBLASTER != GAMEBLASTER || temp_GUS != GUS || temp_SSI2001 != SSI2001 || temp_sound_card_current != sound_card_current || temp_voodoo != voodoo_enabled || temp_dynarec != cpu_use_dynarec || temp_fda_type != fdd_get_type(0) || temp_fdb_type != fdd_get_type(1) || - temp_mouse_type != mouse_type || hdd_changed) + temp_mouse_type != mouse_type || hdd_changed +#ifdef USE_NETWORKING + || temp_network_card != network_card_current +#endif + ) { if (!has_been_inited || MessageBox(NULL,"This will reset PCem!\nOkay to continue?","PCem",MB_OKCANCEL)==IDOK) { @@ -489,6 +554,9 @@ static BOOL CALLBACK config_dlgproc(HWND hdlg, UINT message, WPARAM wParam, LPAR voodoo_enabled = temp_voodoo; cpu_use_dynarec = temp_dynarec; mouse_type = temp_mouse_type; +#ifdef USE_NETWORKING + network_card_current = temp_network_card; +#endif fdd_set_type(0, temp_fda_type); fdd_set_type(1, temp_fdb_type); @@ -651,6 +719,9 @@ static BOOL CALLBACK config_dlgproc(HWND hdlg, UINT message, WPARAM wParam, LPAR recalc_hdd_list(hdlg, temp_model, 1); recalc_snd_list(hdlg, temp_model); +#ifdef USE_NETWORKING + recalc_net_list(hdlg, temp_model); +#endif } break; case IDC_COMBOCPUM: @@ -809,6 +880,28 @@ static BOOL CALLBACK config_dlgproc(HWND hdlg, UINT message, WPARAM wParam, LPAR temp_joystick_type = SendMessage(h, CB_GETCURSEL, 0, 0); joystickconfig_open(hdlg, 3, temp_joystick_type); break; +#ifdef USE_NETWORKING + case IDC_COMBONETCARD: + if (HIWORD(wParam) == CBN_SELCHANGE) + { + h = GetDlgItem(hdlg, IDC_COMBONETCARD); + temp_network_card = settings_list_to_network[SendMessage(h, CB_GETCURSEL, 0, 0)]; + + h = GetDlgItem(hdlg, IDC_CONFIGURENETCARD); + if (network_card_has_config(temp_network_card)) + EnableWindow(h, TRUE); + else + EnableWindow(h, FALSE); + } + break; + + case IDC_CONFIGURENETCARD: + h = GetDlgItem(hdlg, IDC_COMBONETCARD); + temp_network_card = settings_list_to_network[SendMessage(h, CB_GETCURSEL, 0, 0)]; + + deviceconfig_open(hdlg, (void *)network_card_getdevice(temp_network_card)); + break; +#endif } break; } diff --git a/src/win.c b/src/win.c index 7ba7943..22079bc 100644 --- a/src/win.c +++ b/src/win.c @@ -44,6 +44,10 @@ #include "paths.h" //#include "win-opengl.h" +#ifdef DRMINGW +#include +#endif + #ifndef MAPVK_VK_TO_VSC #define MAPVK_VK_TO_VSC 0 #endif @@ -514,6 +518,10 @@ int WINAPI WinMain (HINSTANCE hThisInstance, int c, d; LARGE_INTEGER qpc_freq; +#ifdef DRMINGW + ExcHndlInit(); +#endif + process_command_line(); hinstance=hThisInstance;